【问题标题】:Prime sieve results in (me going to) stack overflowPrime sieve 导致(我要去)堆栈溢出
【发布时间】:2015-02-02 01:32:15
【问题描述】:

对于 euler 项目,我正在寻找一种方法来实现 Eratosthenes 的筛子,因为我希望它比我最初的实现更快(并且需要这样)。我想出了这个功能:

sieve :: Int -> [Int] -> [Int]
sieve p (x:xs) | rem x p == 0 = sieve p xs
               | otherwise = x : (sieve x $ sieve p xs)
sieve _ [] = []

这很有效,但是非常快地击中风扇,导致堆栈溢出。我前往这里寻求一些建议,并立即点击了spoiler,在我看来,它看起来完全一样,但性能上的差异却很奇怪。 我仍然想继续使用我自己的实现,并想知道我的函数是否可以轻松更改以使用更少的内存。

【问题讨论】:

标签: performance haskell time-complexity primes sieve


【解决方案1】:

你的代码里面有一个指数级的爆炸:

sieve p (x:xs) | rem x p == 0 = sieve p xs
               | otherwise = x : (sieve x $ sieve p xs)
--                                          ^^^^^^^       here!
--                                ^^^^^^^                 and here!

您打算让内部 sieve 调用继续通过 p 进行过滤,但由于您使用相同的 sieve 函数,它也会在遇到新质数时启动新过滤器 - 但这完全是多余的,因为“上层”调用也会为相同的素数启动新的过滤器!

sieve 2 [3..]
 = 3 : sieve 3 (sieve 2 [4..])
 = 3 : sieve 3 (5 : sieve 5 (sieve 2 [6..]))
 = 3 : 5 : sieve 5 (sieve 3 (sieve 5 (sieve 2 [6..])))
....         -- ^^^               ^^^                      -- !!!

7 在这里通过四个sieves 到达顶部后,每个将分成两个创建四个新的sieve 7s,所以会有八个 sieves 在链中!!更糟糕的是,当 9 - 复合! - 过筛,将275分开,只会被拒绝3。所以它实际上更糟糕比素数的指数n,并且接近于最后一个素数的指数(即~ n log(n) )。

改成

sieve p (x:xs) | rem x p == 0 = sieve p xs
               | otherwise = x : (sieve x $ filter ((>0).(`rem` p)) xs)

你得到的代码与你引用的代码相同。

如果您更喜欢手动编写所有代码,您可以引入一个新参数,一个控制是否启动新过滤器的布尔标志:

primes = 2 : sieve True 2 [3..]
sieve b p (x:xs) | rem x p == 0 = sieve b p xs
                 | b = x : (sieve b x $ sieve False p xs)
                 | otherwise = x : sieve b p xs

【讨论】:

    猜你喜欢
    • 2015-05-21
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 2015-12-21
    • 2018-11-27
    相关资源
    最近更新 更多