我正试图在Clojure中实施Eratosthenes的筛子。我想测试的一种方法是:
i
< = N.
filter
,以移除i
i+1
次迭代,请使用先前过滤的结果我知道我可以使用loop/recur
执行此操作,但这会导致堆栈溢出错误(由于某些原因,未应用尾调用优化)。
我如何迭代地完成它?我的意思是调用N次调用相同的例程,将i
次迭代的结果传递给i+1
。
答案 0 :(得分:5)
(defn sieve [beg end]
(letfn [(siever [to-sieve sieved]
(if (empty? to-sieve) sieved
(let [[f & r] to-sieve]
(if (> f (Math/sqrt end)) (into sieved to-sieve)
(recur (remove #(zero? (mod % f)) r)
(conj sieved f))))))]
(siever (range beg (inc end)) [])))
这个似乎工作得很好。用(筛2 1000000)测试它并在几秒钟内返回而不吹。
答案 1 :(得分:2)
哦,在看到这个问题之前我已经发表了对你的另一个问题的评论......无论如何,Eratosthenes的Sieve不执行余数/模数计算,只进行加法(以{的增量移动一个列表{1}},其中p
是到目前为止发现的最后一个素数)和“划掉”。当然,“余量+过滤”筛子产生的结果与SoE产生的结果相同,但两种筛分方案的算法复杂度完全不同。
现在,一个严格忠实的SoE需要是“有限的”,因为它只能在固定长度的数组上运行;但是,可以修改基本算法以支持“增量”筛选 - 延迟生成任意数量的素数 - 如果保留额外的数据结构以记录哪些数字到达“交叉”通过的数据到目前为止发现的素数。这种数据结构的完美选择将是优先级队列,但是地图也非常有用。
有关基于这一思想的函数式语言增量筛选的扩展讨论 - 包括仔细分析所涉及的所有算法的复杂性 - 请参阅Melissa E. O'Neill的文章The Genuine Sieve of Eratosthenes。它使用Haskell,但这不应该是一个问题(没有使用深奥的功能,Haskell代码特别清楚,所以它读起来就像常规的数学符号)。
例如在Clojure中的实现,请参阅Christophe Grand的Everybody loves the Sieve of Eratosthenes博客文章 - 强烈推荐,最终版本绝对美观且速度非常快 - 如果我可以自由使用,可能还有几个我的要点将它们插入此处:the first one,the second one。 (请注意,它们根本不是很漂亮,但它们对我来说很有用......第二个使用优先级队列而不是其他的,这是基于地图的,所以希望它有用现在作为一个粗略的例子。)
我想我没有回答你的主要Clojure实施问题,但我认为Michiel的答案和你的另一个问题的主要答案之间已经解决了。