Skip to content

Instantly share code, notes, and snippets.

@WillNess
Last active April 18, 2020 16:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WillNess/4747441 to your computer and use it in GitHub Desktop.
Save WillNess/4747441 to your computer and use it in GitHub Desktop.
So you've got the two bugs: your
> isprimerec _ 1 = False
> isprimerec n t = if (n `rem` t) == 0 then False else isprimerec n (n-1)
should have been
> isprimerec _ 1 = True
> isprimerec n t = if (n `rem` t) == 0 then False else isprimerec n (t-1)
or, with list comprehension,
> isprime n = n>1 && and [ rem n t /= 0 | t <- [n-1,n-2..2] ]
*Internalize* that extra parameter, it was a technicality anyway!
( Ahha, but what's that `and`, you ask? It's just like this recursive function, `every`:
> every (x:xs) = x && every xs -- 'x' is a Boolean, 'xs' is a list
> every [] = True -- of Booleans
... ok? ) But now a major *algorithmic* drawback becomes apparent: we test in the *wrong* order.
It will be much faster to test in ascending order:
> isprime n = n>1 && and [ rem n t /= 0 | t <- [2..n-1] ]
or even faster to stop at the `sqrt`,
> isprime n = n>1 && and [ rem n t /= 0 | t <- [2..q] ]
> where q = floor (sqrt (fromIntegral n))
or to test only by odds, and 2 (why test by 6, if we tested by 2 already? etc.):
> isprime n = n>1 && and [ rem n t /= 0 | t <- 2:[3,5..q] ]
> where q = floor (sqrt (fromIntegral n))
or just by *primes* (why test by 6, if we tested by 3 already? etc.):
> isprime n = n>1 && and [ rem n t /= 0 | t <- takeWhile ((<= n).(^2)) primes ]
> primes = 2 : filter isprime [3..]
and why test the *evens* when filtering the primes through - isn't it better
to not generate them in the first place?
> primes = 2 : filter isprime [3,5..]
but `isprime` always tests by 2 - yet we feed it only the odd numbers:
> primes = 2 : 3 : filter (noDivs $ drop 1 primes) [5,7..]
> noDivs factors n = and [ rem n t /= 0 | t <- takeWhile ((<= n).(^2)) factors ]
and why generate the multiples of 3 (`[9,15 ..]`) only to test and remove them later?
> -- Prelude> take 20 [5,7..]
> -- Prelude> take 20 [j+i | i<-[0,2..], j<-[5]]
> -- Prelude> take 20 [j+i | i<-[0,6..], j<-[5,7,9]]
> -- [5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43]
> primes = 2:3:5: filter (noDivs $ drop 2 primes) [j+i | i<-[0,6..], j<-[7,11]]
or without the multiples of 5,
> -- Prelude> take 20 [j+i | i<-[0,6..], j<-[7,11]]
> -- Prelude> take 20 [j+i | i<-[0,30..], j<-[7,11,13,17,19,23,25,29,31,35]]
> -- [7,11,13,17,19,23,25,29,31,35,37,41,43,47,49,53,55,59,61,65]
> primes = 2:3:5:7: filter (noDivs $ drop 3 primes)
[j+i | i<-[0,30..], j<-[11,13,17,19,23,29,31,37]]
... but that's already going too far.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment