确定具有右折叠的素数

时间:2015-08-04 16:07:37

标签: haskell primes lazy-evaluation fold primality-test

我在互联网上找到了这个解决方案,我需要一些帮助来理解它:

isPrime' :: Integer -> Bool
isPrime' n = foldr (\x acc -> (n `rem` x) /= 0 && acc) True primes
    where primes = 2 : filter isPrime' [3,5..]

有几件事:

我的理解是,如果fold函数的累加器是Boolean,则必须在lambda函数本身中设置它。类似的东西:

(\x acc -> if (n `rem` x /= 0) then False else acc) True primes

但事实并非如此。

此外,primes使用的范围没有终止号码。我知道这个工作的原因是因为Haskell的懒惰评估,但这究竟是如何在这里工作的?

最后,这个函数似乎不会返回正确的布尔值。素数是一个没有其他除数但本身和1的素数。所以lambda不应该读:

(\x acc -> if (n `rem` x) == 0 then False else acc) True primes

我很困惑。请帮帮我。

3 个答案:

答案 0 :(得分:1)

至少对我来说,你的代码还没有完成。它缺少停止条件,可以在wiki

的类似解决方案中看到
isPrime n = n > 1 &&
              foldr (\p r -> p*p > n || ((n `rem` p) /= 0 && r)) True primes

primes = 2 : filter isPrime [3,5..]

在这里你可以看到,当没有可能的素因子时,条件p*p > n会产生True。由于执行延迟,||的正确部分未被评估,foldr停止。

答案 1 :(得分:1)

具有

primes = [p1, p2, p3, ..., pn, ...]

计算

isPrime' n = foldr (\x acc -> (n `rem` x) /= 0 && acc) True primes

好像在计算

isPrime' n = rem n p1 /= 0 && (
             rem n p2 /= 0 && (
             rem n p3 /= 0 && (
             ..........
             rem n pk /= 0 && (
             ..........          ..... ))))

对于复合n这个有效 - rem个表达式中的一个将为0,不等式为false,整个表达式也为假,因为&&是短路的,懒惰的第二个论点。

对于素数n,这将导致一个问题:根据素数的定义,rem个表达式中没有一个是0,直到我们在素数中达到素数p_i == n 。但它还没有,因为我们还没有发现它是最优质的 - 我们现在正在做它。 为了测试它是否是素数,我们必须知道它是素数 - 显然是一个糟糕的情况。

primes还需要一个尚未存在的值。这被称为“黑洞”。它会导致错误,计算中止(或卡住)。

换句话说,就像定义

一样
primes = 2 : filter isPrime' [3,5..]

被定义为

primes = 2 : [ n | n <- [3,5..]
                 , and [rem n p /= 0 | p <- takeWhile (< n) primes]]

问题是要在n为素数时停止,takeWhile (< n)必须在p中达到高于或等于n的{​​{1}},但它是还没有。 “黑洞”。

着名的“筛选”代码通过“转置”工作流来解决这个问题,

primes

从而使其工作,同时保持其计算效率低下(代码的较小问题),不必要地用太多素数测试其候选者(其中每个素数由其所有前面的素数测试而​​不是仅仅那些不高于其平方根的那些,使它不必要地慢。

通过使用

对测试进行适当的早期停止来减轻这种情况
primes = map head . iterate (\(x:xs)-> filter ((/=0).(`rem`x)) xs) $ [2..]

,根据您的定义,相当于

primes = 2 : [ n | n <- [3,5..]
                 , and [rem n p /= 0 | p <- takeWhile ((<= n).(^2)) primes]]

您现在可以将其写为基于isPrime' n = p1*p1 > n || rem n p1 /= 0 && ( p2*p2 > n || rem n p2 /= 0 && ( p3*p3 > n || rem n p3 /= 0 && ( .......... pk*pk > n || rem n pk /= 0 && ( .......... ..... )))) 的定义(可在此处的另一个答案中看到)。

答案 2 :(得分:0)

表达式

(n `rem` x) /= 0

显然产生了Bool结果。参数acc已经是Bool值。所以

(n `rem` x) /= 0 && acc

是两个Bool值的逻辑AND,显然产生Bool结果。困惑在哪里?

输入列表是无限的,仅由奇数组成。只要你在检查了有限数量的值之后就能产生结果,懒惰就会使一切变好。