检查整数(或整数列表的所有元素)是否为素数

时间:2014-10-12 10:41:03

标签: haskell recursion primes

我正在使用递归来做一个简单的Haskell函数。目前,这似乎有效,但是,如果我输入2,它实际上是假的,这很令人生气。我认为代码不尽如人意,所以,如果你有任何建议,那也很酷!

我对这门语言很陌生!

编辑:好的,所以我理解素数是什么。

例如,我希望能够检查2,3,5,7等,并isPrime返回true。当然,如果我使用1,4,6,8等运行该函数,那么它将返回false

所以,我的想法是在伪代码中我需要做如下:

num = 2 -> return true
num > 2 && num = even -> return false

在那之后,我很难在任何正常工作的代码中写下来,所以下面的代码是我正在进行的工作,但我真的很厌烦Haskell所以我现在无处可去。

module Recursion where

isPrime :: Int -> Bool
isPrime x = if x > 2 then ((x `mod` (x-1)) /= 0) && not (isPrime (x-1)) else False

3 个答案:

答案 0 :(得分:7)

确定,

让我们一步一步地做到这一点:

在数学中,(自然)数n是正数,如果它有2个除数:1和它本身(思想1不是素数)。

所以让我们先得到一个数字的所有除数:

divisors :: Integer -> [Integer]
divisors n = [ d | d <- [1..n], n `mod` d == 0 ]
然后得到他们的计数:

divisorCount :: Integer -> Int
divisorCount = length . divisors

并且我们只使用定义来实现最天真的实现:

isPrime :: Integer -> Bool
isPrime n = divisorCount n == 2

现在当然可能会有一些不足之处:

  • 而是检查没有除数> 1< n
  • 你不必检查所有除数n-1,这足以检查n的平方根
  • ...

好的只是为了提供更高性能的版本并让@Jubobs满意;)这里有另一种选择:

isPrime :: Integer -> Bool
isPrime n
  | n <= 1 = False
  | otherwise = not . any divides $ [2..sqrtN]
  where divides d = n `mod` d == 0
        sqrtN = floor . sqrt $ fromIntegral n

这个将检查2与数字的平方根之间是否存在除数

完整代码:

使用quickcheck确保两个定义正常:

module Prime where

import Test.QuickCheck

divisors :: Integer -> [Integer]
divisors n = [ d | d <- [1..n], n `mod` d == 0 ]

divisorCount :: Integer -> Int
divisorCount = length . divisors

isPrime :: Integer -> Bool
isPrime n
  | n <= 1 = False
  | otherwise = not . any divides $ [2..sqrtN]
  where divides d = n `mod` d == 0
        sqrtN = floor . sqrt $ fromIntegral n

isPrime' :: Integer -> Bool
isPrime' n = divisorCount n == 2

main :: IO()
main = quickCheck (\n -> isPrime' n == isPrime n)

!!警告!!

我刚刚看到(在我的脑海里有一些东西),我的方式sqrtN is not the best way to do it - 对不起。我认为这里的小数字的例子没有问题,但也许你真的想要使用这样的东西(直接来自链接):

(^!) :: Num a => a -> Int -> a
(^!) x n = x^n

squareRoot :: Integer -> Integer
squareRoot 0 = 0
squareRoot 1 = 1
squareRoot n =
   let twopows = iterate (^!2) 2
       (lowerRoot, lowerN) =
          last $ takeWhile ((n>=) . snd) $ zip (1:twopows) twopows
       newtonStep x = div (x + div n x) 2
       iters = iterate newtonStep (squareRoot (div n lowerN) * lowerRoot)
       isRoot r  =  r^!2 <= n && n < (r+1)^!2
   in  head $ dropWhile (not . isRoot) iters

但这对于手头的问题似乎有点沉重,所以我只是在这里说一遍。

答案 1 :(得分:3)

以下是关于素数的两个事实。

  1. 第一个素数是2。
  2. 大于2的整数是素数,如果它不能被任何素数除以其平方根。
  3. 这些知识自然会引导您采用以下方法:

    -- primes : the infinite list of prime numbers
    primes :: [Integer]
    primes = 2 : filter isPrime [3,5..]
    
    -- isPrime n : is positive integer 'n' a prime number?
    isPrime :: Integer -> Bool
    isPrime n
        | n < 2     = False
        | otherwise = all (\p -> n `mod` p /= 0) (primesPrefix n)
        where primesPrefix n = takeWhile (\p -> p * p <= n) primes
    

    作为奖励,这里有一个函数来测试整数列表中的所有项是否为素数。

    -- arePrimes ns : are all integers in list 'ns' prime numbers?
    arePrimes :: [Integer] -> Bool
    arePrimes = all isPrime
    

    ghci中的一些示例:

    ghci> isPrime 3
    True
    ghci> isPrime 99
    False
    ghci> arePrimes [2,3,7]
    True
    ghci> arePrimes [2,3,4,7]
    False
    

答案 2 :(得分:0)

你可以从&#34; 2除数&#34;得到一个递归的表达式。逐步变体refinement

isPrime n 
    = 2 == length  [ d | d <- [1..n], rem n d == 0 ]
    = n > 1 && null [ d | d <- [2..n-1], rem n d == 0 ]
    = n > 1 && and [ rem n d > 0 | d <- takeWhile ((<= n).(^2)) [2..] ]
    = n > 1 && g 2
       where
         g d = d^2 > n || (rem n d > 0 && g (d+1))
    = n == 2 || (n > 2 && rem n 2 > 0 && g 3)
       where
         g d = d^2 > n || (rem n d > 0 && g (d+2))

这就是你的递归功能。说服自己每个步骤的有效性。

当然,在我们用2来检查除法之后,没有必要尝试除以4,6,8等;这是最后一次转型的原因,只能通过赔率进行检查。但实际上我们只需要通过 primes 检查可分性。