Haskell素数测试混淆

Haskell素数测试混淆,haskell,primality-test,Haskell,Primality Test,所以我对Haskell完全陌生,希望它不会表现得太多。无论如何,我试图创建一个函数来确定一个数字是否为素数。基本思想是:给定一个数字,看看它是否可以被任何小于它的数整除。如果是,则返回false。如果不是,则为prime,返回true。到目前为止(已知正在运行的)代码如下: 产生: ghci> isPrime 9 False ghci> isPrime 13 True 我想做的是对此进行一点优化,因为我只需要检查小于或等于sqrt(x)的值。问题是,当我尝试实现这一点时,事情变得疯

所以我对Haskell完全陌生,希望它不会表现得太多。无论如何,我试图创建一个函数来确定一个数字是否为素数。基本思想是:给定一个数字,看看它是否可以被任何小于它的数整除。如果是,则返回false。如果不是,则为prime,返回true。到目前为止(已知正在运行的)代码如下:

产生:

ghci> isPrime 9
False
ghci> isPrime 13
True
我想做的是对此进行一点优化,因为我只需要检查小于或等于sqrt(x)的值。问题是,当我尝试实现这一点时,事情变得疯狂:

isPrime x = not (even x) && not (divisible x (ceiling(sqrt(fromIntegral(x-1)))))
除了它看起来很糟糕(我说我是新来的)之外,它没有给出正确的结果:

ghci> isPrime 9
False
ghci> isPrime 13
False
我想弄清楚是什么改变了,因为:

ghci> ceiling(sqrt(13))
4

似乎给了我正确的号码。我知道这是个小问题,但我很困惑…

你把你的情况搞混了:

divisible x y = ((x `mod` y) /= 0) && not (divisible x (y-1))
应该是

divisible x y = (x `mod` y) == 0 || divisible x (y-1)
为测试工作

按原样,您的
可除
函数将展开,例如

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
你的代码使用平方根

如果没有平方根,它恰好适用于奇数,因为奇数复合数与其任何除数之间的差总是偶数。展开可整除的
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
是偶数,因此
(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
是奇数素数,则展开达到
可除p(p-1)=not^(p-3)(可除p(p-(p-2))
p-3
是偶数,
可除p2
偶数p
,即
False

一般来说,对于奇数<代码> n>代码>,请考虑<代码>可分割的NS/代码>,并使<<代码> d>代码>是<代码> n>代码>的最大除数,不超过<代码> s>代码>,如果<代码> n>代码>是复合的,或<代码> d=2 < /代码>如果<代码> n>代码>为素数。
可整除的ns
的展开仍然以同样的方式进行

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
因此
可除数ns
测量
s
n
的下一个较小除数或到2的距离的奇偶性,以较大者为准。当
s
n-1
时,起点始终为偶数,因此计算结果正确,但
上限(sqrt(from integral(n-1))
可能为奇数,在这种情况下,结果被翻转,组合被声明为素数,反之亦然

如果您确保第一个调用获得偶数第二个参数(因此,如果
上限(sqrt(fromIntegral(n-1)))
为奇数,则从
上限(sqrt(fromIntegral(n-1))+1开始,您可以使
可除的
函数用于奇数的平方根界素性测试,但该函数的逻辑令人困惑,其名称不能正确描述其结果

更惯用的写作方式是

isPrime n = and [n `mod` k /= 0 | k <- [2 .. 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)))
isPrime n=all((/=0)。(n`mod`)(takeWhile(以下是我的做法:


divisibleBy
是一个简单的可整除性测试。
isPrime
对1到x之间的所有整数执行此测试,如果
x
可被这些整数中的任何一个整除,则返回true。您可以将上界更改为root
x
,就像您在代码中所做的那样,但在其他情况下此操作有效。

Protip:而不是测试对于
sqrt(d)非常感谢,我很欣赏这个深入的解决方案。编辑:这么说,有人知道为什么sqrt不能正常工作吗?以什么方式不能工作?是
而不是
导致了它,在最初的版本中,它只对奇数有效,因为在最大除数中总是有奇数步数。我将把它添加到我的回答更详细。顺便说一句,你的
isPrime
说2不是质数。哈哈,呜呜。谢谢你的深入解释,我想我自己不会明白的。我找到了你首先提出的解决方案来消除2,我进一步优化了,在“全部…”部分前面添加了一个“奇数n&”部分。它更比后面的解决方案简单,并且提前消除了一半的非素数。或者
isPrime x=any(可除x)[2..(x-1)]
isPrime 2 = True
isPrime n = all ((/= 0) . (n `mod`)) (2 : [3, 5 .. ceiling (sqrt (fromIntegral n))])
isPrime n = all ((/= 0) . (n `mod`)) (takeWhile (<= bound) (2:3:scanl (+) 5 (cycle [2,4])))
  where
    bound = ceiling (sqrt (fromIntegral (n-1)))
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)))
divisibleBy :: (Integral a) => a -> a -> Bool
divisibleBy x y = mod x y == 0

isPrime :: (Integral a) => a -> Bool
isPrime x = or $ map (divisibleBy x) [2..(x-1)]