【问题标题】:Check whether an integer (or all elements of a list of integers) be prime检查整数(或整数列表的所有元素)是否为素数
【发布时间】:2014-12-07 02:10:10
【问题描述】:

我正在使用递归做一个简单的 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

【问题讨论】:

  • 问题不在于您的代码或您对 Haskell 的理解 - 问题在于您的 prime 数字的定义 - 您在这里说的是:如果一个数字大于 2,则它是素数,不可整除由它的前身而它的前身不是主要的 - 所有这些都是错误的
  • 也许你可以添加你试图说的 - 什么时候应该是素数?
  • 我明白什么是质数,我只是在努力把它写成代码!
  • 那么请将您想要的定义添加到您的问题中,我们可以在代码中回答
  • 您还没有说(您认为)质数是什么,只是您知道它们是什么。请写下(用数学和文字,而不是 Haskell)你对素数的定义。 然后想一想你将如何检查一个数字是否是素数(用笔和纸)。只有这样你才应该担心将其翻译成 Haskell。

标签: haskell recursion primes


【解决方案1】:

好的,

让我们一步一步来:

在数学中,一个(自然)数 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

现在当然可以有相当多的改进:

  • 而是检查没有除数&gt; 1&lt; 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和数字的平方根之间没有除数

完整代码:

使用快速检查确保两个定义都正常:

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

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

【讨论】:

  • 谢谢,我会试一试,如果我遇到任何问题,请告诉您
  • @Jubobs 是的,它不是,它并没有声称就像最后的评论会告诉你的那样 - 但恕我直言,它最接近你在素数上找到的东西 (en.wikipedia.org/wiki/Prime_number)并且很容易实现 - 我认为 OP 在这里不需要效率 - 他需要理解
  • @CarstenKönig 是的,但是恕我直言,最好将 OP 引导到使用惰性求值的解决方案(更具体地说,这里是布尔值的短路求值)。
  • 我确实不需要效率,我只是想掌握 Haskell 中的递归。
  • @CarstenKönig 会作为独立代码运行吗?刚刚测试了前一个,我注意到你所说的缺陷,所以我会看看这个。顺便谢谢!
【解决方案2】:

这里有两个关于素数的事实。

  1. 第一个质数是 2。
  2. 大于 2 的整数是素数,当且仅当它不能被直到其平方根的任何素数整除。

这些知识自然会引导您采用以下方法:

-- 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

【讨论】:

  • 您愿意评论一些代码吗?从他的回答中,我大约 90% 理解了上面的代码!道歉!
  • 好的,所以首先我们检查是否 n d 变成n-1?
  • @germainelol 查看我的编辑。如果您需要进一步说明,请告诉我。
  • 在您上次编辑之前,primes 的前缀也保留在内存中。您只是在检查是否相等(takewhile 只是作为搜索限制),现在您添加了 mod 计算,尽管它们是短路的。如果你想加快搜索成员的速度,你可以用列表换一棵树,以获得对数优势(在树上搜索是 O(log n))。尽管将素数树保留在内存中的实用性值得怀疑。
  • @WillNess 对不起。由于某种原因,我错过了你的评论。您能否详细说明如何将素数存储在树中?
【解决方案3】:

您可以通过逐步refinement 从“2 除数”变体中获得递归公式:

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 的整除性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    • 2012-12-07
    • 2017-09-05
    • 2014-05-13
    • 2012-05-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多