Reputation: 3331
I'm doing a simple Haskell function using recursion. At the moment, this seems to work but, if I enter 2
, it actually comes up as false, which is irritating. I don't think the code is as good as it could be, so, if you have any advice there, that'd be cool too!
I'm pretty new to this language!
EDIT: Ok, so I understand what a prime number is.
For example, I want to be able to check 2, 3, 5, 7, etc and have isPrime
return true
. And of course if I run the function using 1, 4, 6, 8 etc then it will return false
.
So, my thinking is that in pseudo code I would need to do as follows:
num = 2 -> return true
num > 2 && num = even -> return false
After that, I'm struggling to write it down in any working code so the code below is my work in process, but I really suck with Haskell so I'm going nowhere at the minute.
module Recursion where
isPrime :: Int -> Bool
isPrime x = if x > 2 then ((x `mod` (x-1)) /= 0) && not (isPrime (x-1)) else False
Upvotes: 2
Views: 2259
Reputation: 66214
Here are two facts about prime numbers.
This knowledge should naturally lead you to something like the following approach:
-- 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
As a bonus, here is a function to test whether all items of a list of integers be prime numbers.
-- arePrimes ns : are all integers in list 'ns' prime numbers?
arePrimes :: [Integer] -> Bool
arePrimes = all isPrime
Some examples in ghci
:
ghci> isPrime 3
True
ghci> isPrime 99
False
ghci> arePrimes [2,3,7]
True
ghci> arePrimes [2,3,4,7]
False
Upvotes: 3
Reputation: 71070
You can get a recursive formulation from the "2 divisors" variant by step-wise 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))
And that's your recursive function. Convince yourself of each step's validity.
Of course after we've checked the division by 2, there's no need to try dividing by 4,6,8, etc.; that's the reason for the last transformation, to check by odds only. But really we need to check the divisibility by primes only.
Upvotes: 0
Reputation: 52270
Ok,
let's do this step by step:
In math a (natural) number n
is prime if it has exactly 2 divisors: 1 and itself (mind 1 is not a prime).
So let's first get all of the divisors of a number:
divisors :: Integer -> [Integer]
divisors n = [ d | d <- [1..n], n `mod` d == 0 ]
then get the count of them:
divisorCount :: Integer -> Int
divisorCount = length . divisors
and voila we have the most naive implementation using just the definition:
isPrime :: Integer -> Bool
isPrime n = divisorCount n == 2
now of course there can be quite some impprovements:
> 1
and < n
n-1
, it's enough to check to the squareroot of nOk just to give a bit more performant version and make @Jubobs happy ;) here is an alternative:
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
This one will check that there is no divisor between 2
and the squareroot of the number
using quickcheck to make sure the two definitions are ok:
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)
I just saw (had something in the back of my mind), that the way I did sqrtN
is not the best way to do it - sorry for that. I think for the examples with small numbers here it will be no problem, but maybe you really want to use something like this (right from the link):
(^!) :: 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
but this seems a bit heavy for the question on hand so I just remark it here.
Upvotes: 7