clojure - Clojure 中的快速素数生成

标签 clojure lisp primes

我一直在努力解决 Project Euler Clojure 中的问题变得更好,而且我已经遇到过几次素数生成。我的问题是它花费的时间太长了。我希望有人能帮助我找到一种有效的方法,以 Clojure 风格的方式来完成这项工作。

当我用拳头做这件事时,我是用暴力破解的。这很容易做到。但是在 Xeon 2.33GHz 上以这种方式计算 10001 个质数需要 2 分钟,对于规则来说太长了,而且通常来说太长了。这是算法:

(defn next-prime-slow
    "Find the next prime number, checking against our already existing list"
    ([sofar guess]
        (if (not-any? #(zero? (mod guess %)) sofar)
            guess                         ; Then we have a prime
            (recur sofar (+ guess 2)))))  ; Try again                               

(defn find-primes-slow
    "Finds prime numbers, slowly"
    ([]
        (find-primes-slow 10001 [2 3]))   ; How many we need, initial prime seeds
    ([needed sofar]
        (if (<= needed (count sofar))
            sofar                         ; Found enough, we're done
            (recur needed (concat sofar [(next-prime-slow sofar (last sofar))])))))

通过将 next-prime-slow 替换为考虑了一些额外规则(例如 6n +/- 1 属性)的较新例程,我能够将速度提高到大约 70 秒。

接下来,我尝试在纯 Clojure 中制作埃拉托色尼筛。我认为我没有解决所有错误,但我放弃了,因为它太慢了(我认为甚至比上面的更糟糕)。

(defn clean-sieve
    "Clean the sieve of what we know isn't prime based"
    [seeds-left sieve]
    (if (zero? (count seeds-left))
        sieve              ; Nothing left to filter the list against
        (recur
            (rest seeds-left)    ; The numbers we haven't checked against
            (filter #(> (mod % (first seeds-left)) 0) sieve)))) ; Filter out multiples

(defn self-clean-sieve  ; This seems to be REALLY slow
    "Remove the stuff in the sieve that isn't prime based on it's self"
    ([sieve]
        (self-clean-sieve (rest sieve) (take 1 sieve)))
    ([sieve clean]
        (if (zero? (count sieve))
            clean
            (let [cleaned (filter #(> (mod % (last clean)) 0) sieve)]
                (recur (rest cleaned) (into clean [(first cleaned)]))))))

(defn find-primes
    "Finds prime numbers, hopefully faster"
    ([]
        (find-primes 10001 [2]))
    ([needed seeds]
        (if (>= (count seeds) needed)
            seeds        ; We have enough
            (recur       ; Recalculate
                needed
                (into
                    seeds    ; Stuff we've already found
                    (let [start (last seeds)
                            end-range (+ start 150000)]   ; NOTE HERE
                        (reverse                                                
                            (self-clean-sieve
                            (clean-sieve seeds (range (inc start) end-range))))))))))

这很糟糕。如果数字 150000 较小,也会导致堆栈溢出。尽管事实上我正在使用 recur。那可能是我的错。

接下来我尝试了一个筛子,在 Java ArrayList 上使用 Java 方法。这需要相当多的时间和内存。

我最近的尝试是使用 Clojure 散列图进行筛选,将所有数字插入筛选中,然后分离非素数。最后,它获取 key 列表,即它找到的素数。找到 10000 个质数大约需要 10-12 秒。我不确定它是否已完全调试。它也是递归的(使用递归和循环),因为我正在尝试成为 Lispy。

对于这类问题,问题 10(求和 2000000 以下的所有素数)让我很难受。我最快的代码给出了正确的答案,但它花了 105 秒才完成,并且需要相当多的内存(我给它 512 MB 只是为了让我不必大惊小怪)。我的其他算法需要很长时间,我总是先停止它们。

我可以使用筛子在 Java 或 C 中非常快速地计算出那么多素数,而无需使用太多内存。我知道我一定是在我的 Clojure/Lisp 风格中遗漏了导致问题的某些东西。

我真的做错了什么吗? Clojure 处理大序列是否有点慢?阅读 Euler 项目的一些讨论,人们已经在 100 毫秒内计算出其他 Lisp 中的前 10000 个素数。我意识到 JVM 可能会减慢速度,而 Clojure 相对较新,但我预计不会有 100 倍的差异。

谁能告诉我在 Clojure 中快速计算素数的方法?

最佳答案

这是庆祝 Clojure 的 Java 互操作 的另一种方法。这在 2.4 Ghz Core 2 Duo(运行单线程)上需要 374 毫秒。我让 Java 的 BigInteger#isProbablePrime 中高效的 Miller-Rabin 实现处理素数检查。

(def certainty 5)

(defn prime? [n]
      (.isProbablePrime (BigInteger/valueOf n) certainty))

(concat [2] (take 10001 
   (filter prime? 
      (take-nth 2 
         (range 1 Integer/MAX_VALUE)))))

Miller-Rabin 5 的确定性对于比这大得多的数字可能不是很好。该确定性等于 96.875% 确定它是素数 (1 - .5^certainty)

关于clojure - Clojure 中的快速素数生成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/960980/

相关文章:

lisp - 从列表 [Common Lisp] 返回唯一项的递归函数

python - Python中的简单素数生成器

clojure - io!的实际用途是什么! Clojure 中的 block ?

function - 如何在 Racket 中将 `and` 作为函数传递?

clojure - 在运行时设置 Clojure "constants"

string - 将 lisp 字符串转换为流

c# - 使用 BigInteger 的阿特金筛法的质数

algorithm - 埃拉托色尼筛 : speeding up the "cross off multiples" step

clojure - 内存不足错误: GC overhead limit exceeded Criterium

java - 如何在 Eclipse 中运行 Clojure 测试?