我一直在努力解决Clojure中的问题,以便变得更好,而且我已经遇到了几次素数 . 我的问题是它只是花了太长时间 . 我希望有人可以帮我找到一种以Clojure-y方式做到这一点的有效方法 .
当我拳头做到这一点时,我粗暴地强迫它 . 这很容易做到 . 但是计算10001个素数在Xeon 2.33GHz上用了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中筛选出Eratosthenes . 我不认为我得到了所有的错误,但我放弃了,因为它太慢了(我认为甚至比上面更糟糕) .
(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较小,它还会导致堆栈溢出 . 尽管事实上我正在使用复发 . 那可能是我的错 .
接下来,我尝试使用Java ArrayList上的Java方法筛选 . 这需要相当多的时间和记忆 .
我最近的尝试是使用Clojure哈希映射的筛子,在筛子中插入所有数字然后分解不是素数的数字 . 最后,它采用密钥列表,它是它找到的素数 . 找到10000个素数需要大约10-12秒 . 我不确定它是否已经完全调试过了 . 它也是递归的(使用recur和loop),因为我试图成为Lispy .
因此,对于这些问题,问题10(总计2000000以下的所有素数)正在扼杀我 . 我最快的代码提出了正确的答案,但它需要105秒才能完成,并需要相当多的内存(我给它512 MB只是所以我不必大惊小怪) . 我的其他算法花了这么长时间我总是先停止它们 .
我可以使用筛子来快速计算Java或C中的许多质数,而不需要使用太多的内存 . 我知道我必须在我的Clojure / Lisp风格中遗漏导致问题的东西 .
有什么我做错了吗? Clojure对大序列有点慢吗?阅读一些项目的欧拉讨论,人们已经在不到100毫秒的时间内计算了其他Lisps中的前10000个素数 . 我意识到JVM可能会减慢速度并且Clojure相对年轻,但我不希望它有100倍的差异 .
有人可以通过快速的方式启发我来计算Clojure中的素数吗?
14 回答
这是另一种庆祝
Clojure's Java interop
的方法 . 这需要在2.4 Ghz Core 2 Duo上运行374ms(运行单线程) . 我让Java的BigInteger#isProbablePrime
中的高效Miller-Rabin
实现处理primality检查 .对于比这大得多的数字,5的确定性可能不是很好 . 这个确定性等于
96.875%
确定它是黄金时期(1 - .5^certainty
)我意识到这是一个非常古老的问题,但我最近最终寻找相同的东西,这里的链接不是我正在寻找的东西(尽可能限制功能类型,懒洋洋地生成〜每个我想要的素数) .
我偶然发现了一个很好的F# implementation,所以所有的积分都是他的 . 我只把它移植到Clojure:
用法很简单
聚会很晚,但我会举一个例子,使用Java BitSet:
在2014 Macbook Pro(2.3GHz Core i7)上运行,我得到:
请参见此处的最后一个示例:http://clojuredocs.org/clojure_core/clojure.core/lazy-seq
这是一个很好而简单的实现:
http://clj-me.blogspot.com/2008/06/primes.html
...但它是为一些1.0之前版本的Clojure编写的 . 请参阅Clojure Contrib中的lazy_seqs,了解与当前语言版本一起使用的版本 .
在10G堆栈大小的情况下,我在2.1Gz的macbook上在~33秒内获得第1001个素数 .
所以我刚刚开始使用Clojure,是的,这在项目Euler上出现了很多不是吗?我写了一个非常快速的试验分数素数算法,但它并没有真正扩展到每一次分裂变得非常缓慢之前 .
所以我再次开始,这次使用筛选方法:
用法和速度:
我对速度非常满意:它已经耗尽了2009 MBP上的REPL . 它主要是快速的,因为我完全避开惯用的Clojure,而是像猴子一样环绕 . 它也快了4倍,因为我使用瞬态矢量在筛子上工作而不是完全停留不可改变的 .
Edit: 经过Will Ness的一些建议/错误修复后,它现在运行得更快 .
这是Scheme中的简单筛子:
http://telegraphics.com.au/svn/puzzles/trunk/programming-in-scheme/primes-up-to.scm
这是一个高达10,000的素数运行:
根据Will的评论,这是我对postponed-primes的看法:
初次提交比较:
这是我尝试将this prime number generator从Python移植到Clojure . 以下返回无限延迟序列 .
用法:
编辑:
性能比较,其中
postponed-primes
使用到目前为止看到的素数队列而不是递归调用postponed-primes
:来自:http://steloflute.tistory.com/entry/Clojure-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%B5%9C%EC%A0%81%ED%99%94
使用Java数组
用法和速度:
我刚开始使用Clojure,所以我不知道它是否好,但这是我的解决方案:
来到这个线程并寻找更快的替代已经在这里的那些,我很惊讶没有人链接到以下article by Christophe Grand:
除了懒惰版本:
惯用语,而不是太糟糕
已经有很多答案,但我有一个替代解决方案,可以生成无限的素数序列 . 我也对一些解决方案的标记感兴趣 .
首先是一些Java互操作 . 以供参考:
本杰明@ https://stackoverflow.com/a/7625207/3731823是
primes-fn-2
nha @ https://stackoverflow.com/a/36432061/3731823是
primes-fn-3
我的实现是
primes-fn-4
:报告的数字(计算前N个素数的时间以毫秒计)是5次运行中最快的,实验之间没有JVM重启,因此您的里程可能会有所不同: