你把你的条件搞混了:
divisible x y = ((x `mod` y) /= 0) && not (divisible x (y-1))
应该
divisible x y = (x `mod` y) == 0 || divisible x (y-1)
为测试工作。
照原样,您的divisible
功能会扩展,例如
divisible 21 5 = (21 `mod` 5 /= 0) && not (divisible 21 4)
= (21 `mod` 5 /= 0) && not ((21 `mod` 4 /= 0) && not (divisible 21 3))
= not ((21 `mod` 4 /= 0) && not ((21 `mod` 3 /= 0) && not (divisible 21 2)))
= not (True && not (False && not (divisible 21 3)))
= not (not False)
= False
因为21 `mod` 3 == 0
, 并以平方根为界isPrime 21
求值。True
但是,我得到
*StrangePrime> isPrime 9
True
*StrangePrime> isPrime 13
True
使用平方根的代码。
没有平方根,它碰巧适用于奇数,因为奇数复合和它的任何除数之间的差总是偶数。展开divisible
几步,我们看到奇数复合的最小素因子在n = p*m
哪里p
n
divisible n (n-1) = n `mod` (n-1) /= 0 && not (divisible n (n-2))
= not (divisible n (n-2))
= not (n `mod` (n-2) /= 0 && not (divisible n (n-3)))
= not (not (divisible n (n-3)))
= not^2 (divisible n (n-3))
并归纳地
divisible n (n-1) = not^(k-1) (divisible n (n-k))
如果没有n
大于的除数n-k
。现在,在上述情况下,最大的除数n
是m = n - (p-1)*m
,所以我们得到
divisible n (n-1) = not^((p-1)*m-1) (divisible n m)
= not^((p-1)*m-1) (n `mod` m /= 0 && not (...))
但是n `mod` m == 0
,所以我们有
divisible n (n-1) = not^((p-1)*m-1) False
因为p
是奇数,p-1
所以是偶数,所以 s 也是(p-1)*m
,所以我们总共有奇数个not
s,与一个 相同not
,给出
divisible n (n-1) = True
isPrime n = not (even n) && not (divisible n (n-1)) = True && not True = False
如果p
是奇素数,展开达到divisible p (p-1) = not^(p-3) (divisible p (p - (p-2)))
。p-3
是偶数,divisible p 2
是even p
,是False
。
一般来说,考虑divisible n s
一个奇数n
,并让d
是n
不超过的最大除数s
,如果n
是合数,或者d = 2
如果n
是素数。divisible n s
仍然以同样的方式展开
divisible n s = not^k (divisible n (s-k))
虽然没有找到除数并且s-k > 2
. 所以在复合的情况下n
,我们发现
divisible n s = not^(s-d) (divisible n d)
= not^(s-d) (n `mod` d /= 0 && not (...))
= not^(s-d) False
= odd (s-d)
= even s -- since d is odd, as a divisor of an odd number
在奇素数的情况下n
,
divisible n s = not^(s-2) (divisible n 2)
= not^(s-2) (even n)
= not^(s-2) False
= odd s
因此测量到下一个较小除数或到 2divisible n s
的距离的奇偶性,以较大者为准。什么时候,起点总是偶数,所以它计算正确,但可能是奇数,在这种情况下,结果被翻转并且复合被声明为素数,反之亦然。s
n
s
n-1
ceiling (sqrt (fromIntegral (n-1)))
divisible
如果您确保第一个调用获得偶数第二个参数(因此如果ceiling (sqrt (fromIntegral (n-1)))
是奇数,则从 开始),您可以使您的函数用于具有平方根界限的奇数的素数测试ceiling (sqrt (fromIntegral (n-1))) + 1
,但该函数的逻辑令人困惑,它的名字并不能正确地描述它的结果。
一种更惯用的写法是
isPrime n = and [n `mod` k /= 0 | k <- [2 .. ceiling (sqrt $ fromIntegral n)]]
当跳过先前测试中已知为非除数的候选除数时,测试变得更加有效,容易跳过除 2 之外的所有偶数,
isPrime 2 = True
isPrime n = all ((/= 0) . (n `mod`)) (2 : [3, 5 .. ceiling (sqrt (fromIntegral n))])
稍微多一些,但更有效的是跳过 3 的倍数
isPrime n = all ((/= 0) . (n `mod`)) (takeWhile (<= bound) (2:3:scanl (+) 5 (cycle [2,4])))
where
bound = ceiling (sqrt (fromIntegral (n-1)))
同样,可以从试验除数中消除更多小素数的倍数,每个都获得一点效率,但以更复杂的轮子为代价,例如也消除 5 的倍数导致
isPrime n = all ((/= 0) . (n `mod`)) (takeWhile (<= bound) (2:3:5: scanl (+) 7 (cycle [4,2,4,2,4,6,2,6])))
where
bound = ceiling (sqrt (fromIntegral (n-1)))