哈斯克尔的埃拉托斯特尼筛法
我正在解决 Haskell 中的一些经典问题来开发我的函数 技能,并且我在实施此 "Programming Praxis" 中建议的优化时遇到问题 site:
这个问题我有三种解决方案,第三种太慢了 与第二种解决方案相比。有人可以建议一些改进吗 我的代码?
我的实现是:
-- primeira implementação
primes n
| n < 2 = []
| n == 2 = [2]
| n `mod` 2 == 0 = primes'
| otherwise = if (find (\x -> n `mod` x == 0) primes') == Nothing then
n:primes'
else
primes'
where primes' = primes (n - 1)
-- segunda implementação
primes' :: Integer -> [Integer]
primes' n = sieve $ 2 : [3,5..n]
where sieve :: [Integer] -> [Integer]
sieve [] = []
sieve l@(x:xs)
| x*x >= n = l
| otherwise = x : sieve list'
where list' = filter (\y -> y `mod` x /= 0) xs
-- terceira implementação
primes'' :: Integer -> [Integer]
primes'' n = 2 : sieve 3 [3,5..n]
where sieve :: Integer -> [Integer] -> [Integer]
sieve _ [] = []
sieve m l@(x:xs)
| m*m >= n = l
| x < m*m = x : sieve m xs
| otherwise = sieve (m + 2) list'
where list'= filter (\y -> y `mod` m /= 0) l
I'm solving some classic problems in Haskell to develop my functional
skills and I have a problem to implement an optimization suggested at this "Programming Praxis" site:
I have three solutions to this problem and the third one is too slow
compared to the second solution. Can someone suggest some improvements to
my code?
My implementations are:
-- primeira implementação
primes n
| n < 2 = []
| n == 2 = [2]
| n `mod` 2 == 0 = primes'
| otherwise = if (find (\x -> n `mod` x == 0) primes') == Nothing then
n:primes'
else
primes'
where primes' = primes (n - 1)
-- segunda implementação
primes' :: Integer -> [Integer]
primes' n = sieve $ 2 : [3,5..n]
where sieve :: [Integer] -> [Integer]
sieve [] = []
sieve l@(x:xs)
| x*x >= n = l
| otherwise = x : sieve list'
where list' = filter (\y -> y `mod` x /= 0) xs
-- terceira implementação
primes'' :: Integer -> [Integer]
primes'' n = 2 : sieve 3 [3,5..n]
where sieve :: Integer -> [Integer] -> [Integer]
sieve _ [] = []
sieve m l@(x:xs)
| m*m >= n = l
| x < m*m = x : sieve m xs
| otherwise = sieve (m + 2) list'
where list'= filter (\y -> y `mod` m /= 0) l
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在我看来,第三次修订的问题在于如何选择下一个要筛选的元素。
你不加区别地增加 2。问题是你随后筛选了不必要的数字。
例如,在这个版本中,您最终将把 9 作为 m 传递,并且您将执行额外的递归来过滤 9,即使它甚至不在列表中,因此您不应该在第一个位置(因为它会在 3 上的第一个过滤器中被删除)
即使第二个版本不会开始过滤超过它筛选的数字的平方,但它永远不会选择不必要的筛选值。
换句话说,我认为你最终会筛选 3 到 n 之间的每个奇数。相反,您应该筛选上一轮尚未删除的每个奇数。
我认为要正确实现在当前 sift 值的平方处启动筛选的优化,您必须保留列表的前面,同时在后面进行筛选,其中后面包含元素 >= sift 值的平方。我认为这会迫使您使用串联,并且我不太确定优化是否足以抵消使用 ++ 引起的开销。
Looks to me like the problem with your third revision is how you choose the next element to sift on.
You indiscriminately increment by 2. The problem is that you then sift on unnecessary numbers.
for example, in this version your eventually going to pass 9 as m, and you're going to do an extra recursion to filter on 9, even though it isn't even in the list, and thus you should have never picked it in the first place (since it would have been removed in the very first filter on 3)
Even though the second version doesn't start the filtering past the square of the number it sifts on, it never chooses an unnecessary sifting value.
In other words, I think you end up sifting on every odd number between 3 and n. Instead you should be sifting on every odd number that hasn't already been removed by a previous pass.
I think to correctly implement the optimization of starting the sieve at the square of the current sift value, you have to retain the front of the list while sifting on the back where back contains the elements >= the square of the sift value. I think this would force you to use concatenations, and I'm not so sure that the optimization is good enough to cancel out the overhead induced by using ++.
首先,
mod
很慢,因此在无关紧要的情况下使用rem
(基本上,当您不处理负数时)。其次,使用 Criterion 来(向自己)展示什么更快以及哪些更改实际上是优化。我知道我没有对您的问题给出完整的答案,但它是您(和其他潜在回答者)开始的好地方,所以这里有一些代码:注意使用
rem< 的变体的改进运行时间/code>:
虽然我看到您这样做是为了自己的教育,但值得注意的是 Primes 的相关链接Haskell.org 和 hackage 上的快速 Primes 包。
First of all,
mod
is slow so userem
in situations where it doesn't matter (when you aren't dealing with negatives, basically). Secondly, use Criterion to show (to yourself) what is faster and what changes are actually optimizations. I know I'm not giving a full answer to you question with this, but its a good place for you (and other potential answerers) to start, so here's some code:Notice the improved runtime of the variants using
rem
:While I see you are doing this for your own education, its worth noting the related links of Primes on Haskell.org and the fast Primes package on hackage.
这不是优化的,而是富有表现力的实现:查看视频 Sieve of Eratosthenes in haskell< /a>
This is not optimized but expressive implementation: check video Sieve of Eratosthenes in haskell