Fast Prime Number Generation in Clojure

后端 未结 16 1836
萌比男神i
萌比男神i 2020-12-02 12:30

I\'ve been working on solving Project Euler problems in Clojure to get better, and I\'ve already run into prime number generation a couple of times. My problem is that it is

16条回答
  •  無奈伤痛
    2020-12-02 12:52

    Based on Will's comment, here is my take on postponed-primes:

    (defn postponed-primes-recursive
      ([]
         (concat (list 2 3 5 7)
                 (lazy-seq (postponed-primes-recursive
                            {}
                            3
                            9
                            (rest (rest (postponed-primes-recursive)))
                            9))))
      ([D p q ps c]
         (letfn [(add-composites
                   [D x s]
                   (loop [a x]
                     (if (contains? D a)
                       (recur (+ a s))
                       (persistent! (assoc! (transient D) a s)))))]
           (loop [D D
                  p p
                  q q
                  ps ps
                  c c]
             (if (not (contains? D c))
               (if (< c q)
                 (cons c (lazy-seq (postponed-primes-recursive D p q ps (+ 2 c))))
                 (recur (add-composites D
                                        (+ c (* 2 p))
                                        (* 2 p))
                        (first ps)
                        (* (first ps) (first ps))
                        (rest ps)
                        (+ c 2)))
               (let [s (get D c)]
                 (recur (add-composites
                         (persistent! (dissoc! (transient D) c))
                         (+ c s)
                         s)
                        p
                        q
                        ps
                        (+ c 2))))))))
    

    Initial submission for comparison:

    Here is my attempt to port this prime number generator from Python to Clojure. The below returns an infinite lazy sequence.

    (defn primes
      []
      (letfn [(prime-help
                [foo bar]
                (loop [D foo
                       q bar]
                  (if (nil? (get D q))
                    (cons q (lazy-seq
                             (prime-help
                              (persistent! (assoc! (transient D) (* q q) (list q)))
                              (inc q))))
                    (let [factors-of-q (get D q)
                          key-val (interleave
                                   (map #(+ % q) factors-of-q)
                                   (map #(cons % (get D (+ % q) (list)))
                                        factors-of-q))]
                      (recur (persistent!
                              (dissoc!
                               (apply assoc! (transient D) key-val)
                               q))
                             (inc q))))))]
        (prime-help {} 2)))
    

    Usage:

    user=> (first (primes))
    2
    user=> (second (primes))
    3
    user=> (nth (primes) 100)
    547
    user=> (take 5 (primes))
    (2 3 5 7 11)
    user=> (time (nth (primes) 10000))
    "Elapsed time: 409.052221 msecs"
    104743
    

    edit:

    Performance comparison, where postponed-primes uses a queue of primes seen so far rather than a recursive call to postponed-primes:

    user=> (def counts (list 200000 400000 600000 800000))
    #'user/counts
    user=> (map #(time (nth (postponed-primes) %)) counts)
    ("Elapsed time: 1822.882 msecs"
     "Elapsed time: 3985.299 msecs"
     "Elapsed time: 6916.98 msecs"
     "Elapsed time: 8710.791 msecs"
    2750161 5800139 8960467 12195263)
    user=> (map #(time (nth (postponed-primes-recursive) %)) counts)
    ("Elapsed time: 1776.843 msecs"
     "Elapsed time: 3874.125 msecs"
     "Elapsed time: 6092.79 msecs"
     "Elapsed time: 8453.017 msecs"
    2750161 5800139 8960467 12195263)
    

提交回复
热议问题