【发布时间】:2011-06-03 08:20:13
【问题描述】:
我只是在尝试(对我而言)一种新的编程语言:clojure。我写了一个非常幼稚的“筛子”实现,然后我尝试对其进行优化。
奇怪的是(至少对我而言),新的实现并没有更快,而是很多慢...
任何人都可以提供一些关于为什么这么慢的见解吗?
我也对如何改进此算法的其他技巧感兴趣...
最好的问候,
阿诺·古德
; naive sieve.
(defn sieve
([max] (sieve max (range 2 max) 2))
([max candidates n]
(if (> (* n n) max)
candidates
(recur max (filter #(or (= % n) (not (= (mod % n) 0))) candidates) (inc n)))))
; Instead of just passing the 'candidates' list, from which I sieve-out the non-primes,
; I also pass a 'primes' list, with the already found primes
; I hoped that this would increase the speed, because:
; - Instead of sieving-out multiples of 'all' numbers, I now only sieve-out the multiples of primes.
; - The filter predicate now becomes simpler.
; However, this code seems to be approx 20x as slow.
; Note: the primes in 'primes' end up reversed, but I don't care (much). Adding a 'reverse' call makes it even slower :-(
(defn sieve2
([max] (sieve2 max () (range 2 max)))
([max primes candidates]
(let [n (first candidates)]
(if (> (* n n) max)
(concat primes candidates)
(recur max (conj primes n) (filter #(not (= (mod % n) 0)) (rest candidates)))))))
; Another attempt to speed things up. Instead of sieving-out multiples of all numbers in the range,
; I want to sieve-out only multiples of primes.. I don't like the '(first (filter ' construct very much...
; It doesn't seem to be faster than 'sieve'.
(defn sieve3
([max] (sieve max (range 2 max) 2))
([max candidates n]
(if (> (* n n) max)
candidates
(let [new_candidates (filter #(or (= % n) (not (= (mod % n) 0))) candidates)]
(recur max new_candidates (first (filter #(> % n) new_candidates)))))))
(time (sieve 10000000))
(time (sieve 10000000))
(time (sieve2 10000000))
(time (sieve2 10000000))
(time (sieve2 10000000))
(time (sieve 10000000)) ; Strange, speeds are very different now... Must be some memory allocation thing caused by running sieve2
(time (sieve 10000000))
(time (sieve3 10000000))
(time (sieve3 10000000))
(time (sieve 10000000))
【问题讨论】:
-
我无法复制源代码,它在行尾被截断。
-
对不起...现在代码应该已经完成了。
-
速度的显着提高可能是由于 JIT 编译器在多次运行后启动.....您需要确保在实际运行之前运行您计划进行基准测试的任何代码几次避免这种影响的时机
-
@Arnaud:
sieve3在原始代码中调用sieve,还是打错字? -
@Arnaud:在我之前的评论中检查链接到(带有锚文本"profiled")的答案中的 cmets。
标签: clojure primes sieve-of-eratosthenes