【问题标题】:How do I define the sieve function for prime computation using higher–order functions?如何使用高阶函数为素数计算定义筛函数?
【发布时间】:2013-12-22 00:31:33
【问题描述】:

我在 Haskell 中有一个 sieve 的递归定义,用于计算素数。但我不知道如何使用 mapfilter 等高阶函数编写相同的函数。谁能给我看看?

sieve [] = []
sieve (x:xs) = check (x:xs)

check [] = []
check (x:xs)
        |x/=2 && x/=3 && x/=5 && x/=7 = comp (x:xs)
        |otherwise    = x : sieve xs

comp [] = []
comp (x:xs)
        |x `mod` 2 == 0 = sieve xs
        |x `mod` 3 == 0 = sieve xs
        |x `mod` 5 == 0 = sieve xs
        |x `mod` 7 == 0 = sieve xs
        |otherwise      = x : sieve xs

【问题讨论】:

标签: haskell primes higher-order-functions map-function sieve


【解决方案1】:

使用mapfilteriterate;很慢:

primes = map head $ iterate (\(x:xs) -> filter ((> 0).(`rem`x)) xs) [2..]

加上concat速度快得多,复杂性也大大提高:

primes = concat . map fst $ 
   iterate (\(_, (p:ps, xs)) -> case span (< p*p) xs of
                   { (h,t) -> (h, (ps, filter ((> 0).(`rem`p)) t)) }) 
           ([2], (primes, [3..]))

更多信息请访问Haskell wiki


如果您愿意,可以通过map 表达iterate

iterate f x = let { r = x : map f r } in r

还有filter

filter f xs = concat $ map (\x -> [x | f x]) xs

但是对于 Eratosthenes 的 true 筛子,它不通过可分性测试检测复合物,而是直接从它找到的素数生成它们,并在由此生成的复合物之间的间隙中找到素数,-我们需要更多辅助功能,如minus and uniontreelike-folding foldifoldr 可以用来代替foldi,但速度会降低,复杂性会变差):

primes = 2 : _Y ((3:) . minus [5,7..]     
                         . foldi (\(x:xs) ys -> x : union xs ys) [] 
                            . map (\p -> [p*p, p*p+2*p..]))
_Y g = g (_Y g) 

这运行得更快,接近使用 Haskell 的不可变代码可实现的最佳 empirical orders of growth。不可变数组可以更快,但这里将它们排除在外,因为 a。这不是问题,b。它们的性能取决于给定的 Haskell 实现,而不是用户代码。可变数组当然是最快的,但代码更复杂。

【讨论】:

  • 它仍然比可变版本慢得多。
  • @haskelline 是哪一个? ——你比较过empirical orders of growth吗? - 无论如何,问题是关于如何用高阶函数编写它,而不是关于如何编写最快的素数代码。我们为此提供了 arithmoi 包,它不适合 SO 答案。
  • 在我的机器上,简单的 C 版本获得最大素数的速度要快 4.5 倍 &lt; 10000000
  • @haskelline 太棒了! :) -- 等等,比什么还快? ——再一次,这是一个关于比较 C 和 Haskell 的问题吗? :) 你测量过你的 C 程序 BTW 的经验增长顺序吗?
  • 经验增长顺序在这里完全无关紧要。我说的是你答案中的最后一个版本。你是那个说这是 true 筛子的埃拉托色尼算法的人。我的论点是,不,这与简单的埃拉托色尼筛之间存在巨大的常数因素。
【解决方案2】:

我很快把这个拼凑起来,速度不是很好,但是很容易实现。

primes'::[Int]->[Int]
primes' [] = []
primes' (x:xs) = x:primes (filter ((/= 0) . (`mod` x)) xs)

main = print $ primes [2..20] -- always input a contiguous list from 2 to N.

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-28
  • 1970-01-01
  • 1970-01-01
  • 2014-02-17
  • 1970-01-01
相关资源
最近更新 更多