So I’m totally new to Haskell, hope it doesn’t show too much. By any means, I was trying to create a function to determine if a number is prime. The basic idea is this: Given a number, see if it is divisible by any other number less than it. If so, return false. If not, it’s prime, return true. The code so far (that is known to be working) is this:
divisible :: Integer -> Integer -> Bool
divisible x 2 = even x
divisible x y = ((x `mod` y) /= 0) && not (divisible x (y-1))
isPrime :: Integer -> Bool
isPrime x = not (even x) && not (divisible x (x-1))
Produces:
ghci> isPrime 9
False
ghci> isPrime 13
True
What I’d like to do is optimize this a bit, since I only need to check values less than or equal to sqrt(x). The problem is, when I try and implement this, stuff gets crazy:
isPrime x = not (even x) && not (divisible x (ceiling(sqrt(fromIntegral(x-1)))))
Besides the fact that it looks terrible (I said I was new), it doesn’t give the correct result:
ghci> isPrime 9
False
ghci> isPrime 13
False
I’m trying to figure out what changed, because:
ghci> ceiling(sqrt(13))
4
Seems to be giving me the correct number. I know this is a small problem, but I’m seriously confused…
You got your conditions mixed up:
should be
for the test to work.
As is, your
divisiblefunction expands e.g.since
21 `mod` 3 == 0, andisPrime 21evaluates toTruewith the square root bound.However, I get
with your code using the square root.
Without the square root, it happened to work for odd numbers, because the difference between an odd composite and any of its divisors is always even. Unfolding
divisiblea few steps forn = p*m, wherepis the smallest prime factor of the odd compositen, we seeand inductively
if there are no divisors of
nlarger thann-k. Now, in the above situation, the largest divisor ofnism = n - (p-1)*m, so we obtainBut
n `mod` m == 0, so we haveSince
pis odd,p-1is even, and hence so is(p-1)*m, so altogether we have an odd number ofnots, which is the same as onenot, givingIf
pis an odd prime, the unfolding reachesdivisible p (p-1) = not^(p-3) (divisible p (p - (p-2))).p-3is even,divisible p 2iseven p, which isFalse.Generally, consider
divisible n sfor an oddn, and letdbe the largest divisor ofnnot exceedings, ifnis composite, ord = 2ifnis prime. The unfolding ofdivisible n sstill proceeds the same waywhile no divisor has been found and
s-k > 2. So in the case of a compositen, we findand in the case of an odd prime
n,So
divisible n smeasures the parity of the distance ofsto the next smaller divisor ofnor to 2, whichever is larger. Whenswasn-1, the starting point was always even, so it worked out correctly, butceiling (sqrt (fromIntegral (n-1)))can be odd, in which case the results are flipped and composites are declared prime and vice versa.You can make your
divisiblefunction work for the primality test of odd numbers with a square root bound if you make sure that the first call gets an even second argument (so ifceiling (sqrt (fromIntegral (n-1)))is odd, start atceiling (sqrt (fromIntegral (n-1))) + 1), but the logic of that function is confusing, and its name doesn’t correctly describe its results.A more idiomatic way to write it would be
The test becomes more efficient when one skips candidate divisors that are already known to be nondivisors from prior tests, easy is skipping all even numbers except 2,
slightly more involved, but still more efficient is also skipping multiples of 3
In the same vein one can eliminate the multiples of more small primes from the trial divisors, each gaining a bit of efficiency, but at the cost of a more complicated wheel, e.g. also eliminating multiples of 5 leads to