1

I'm trying to solve problem #26 from Project Euler in Haskell, but I have encountered some problems.

I've managed to figure out that the recurring cycle of the reciprocal is only related to its prime divisors, so I think I just need to find out a reciprocal of a prime number with the longest recurring cycle. So I wrote a algorithm in Haskell:

isPrime :: Int -> Bool
isPrime k
    | k <= 1    = error "Seriously?"
    | otherwise = null [ x | x <- [2..floor(sqrt(fromIntegral k))], k `mod` x == 0]

lp = [x | x <- [7..1000], isPrime x]

s = map (\n -> head [x | x <- [ceiling(logBase 10 (fromIntegral n))..], 10^x `mod` n == 1]) lp

main::IO()
main = print $ maximum s

However, it cannot produce an answer. I have tried using the lamda, which could produce the digits of recurring cycle, with a few prime numbers and I manage to get the right count of digits (I hope there would be no problem with the algorithm). I have also checked the output of the list s, which produced [6,2,6,16,18,45,23,15,3,5,63, without an end. I have no idea why it is the case, since if I manually apply the function to each prime number I can get the right output.

Can anybody tell me what is wrong with my code, or is my way of solving it turns out to be wrong? Thanks.

Meowcolm Law
  • 392
  • 4
  • 13
  • The algorithm seems ok to me - not particularly efficient, so it may well run for ages, but the values you've printed out seem to be correct (at least those I've checked), and there's no way `s` should be an infinite lift. I'll try testing it myself in ghci if no-one has come up with an explanation by then, but it'll have to wait till I'm at home tonight. [PS for the sake of speed you might just want to find a list of all primes less than 1000 and hard-code that as `lp`.] – Robin Zigmond Feb 05 '19 at 09:26
  • @RobinZigmond thanks, I find that the function, if I set the func ```f n = head [x | x <- [ceiling(logBase 10 (fromIntegral n))..], 10^x `mod` n == 1]``` gives an output if I directly input a prime number like ```f 89```, but it cannot work if replace it with ```f (lp !! 20)``` – Meowcolm Law Feb 05 '19 at 09:34
  • @RobinZigmond check out my edit to the answer; not all results were correct, this is how I realized there was some wrap-around arithmetic going on. it wasn't extremely slow, took a few seconds to produce the full list up to 1000. – Will Ness Feb 06 '19 at 17:20
  • @MeowcolmLaw it worked with `89` because it interpeted `89` as a [polymorphic literal](https://stackoverflow.com/a/54491443/849891) of type `Num a => a`, and defaulted to `Integer`. – Will Ness Feb 06 '19 at 17:22
  • I get frustrated with Haskell typing sometimes. Usually the fix is quick with `fromIntegral` but not here. I got this to work by eliminating the use of `n` with `[ (n,x) | i <- [n], x <- [(ceiling.logBase 10 $ fromIntegral i)..], mod (10^x) i == 1 ]` Use the `i` in place of `n`. You could use `let` but it would be less compact. – fp_mora Feb 25 '19 at 17:26

1 Answers1

4

Int is not a good choice here, since you operate pretty big numbers, with the 10^x. Int is Bounded, so wraps around past its upper bound:

> maxBound :: Int
9223372036854775807

> (maxBound :: Int) + 1
-9223372036854775808

Omitting the signature for isPrime altogether, we get

> :t lp
lp :: Integral b => [b]

Trying

> map (\n -> (n, head [x | x <- [ceiling(logBase 10 (fromIntegral n))..],
                           10^x `mod` n == 1])) 
      (lp :: [Int])
[(7,6),(11,2),(13,6),(17,16),(19,18),(23,45),(29,23),(31,15),(37,3),(41,5),(43,63),
 (47,Interrupted.

we see that your calculation gets stuck on 47. But using [Integer] (or nothing at all, so it defaults to Integer by itself), we successfully get the full result. You just misinterpret it. Re-read the problem statement, and you'll see.

(also, the answer for 43 in the above snippet is incorrect, while those for 7, 11, 13 are correct. Getting wrong results for bigger numbers is a strong signal that we have some integer wrap-around arithmetic errors going on; and that's how I found it out).

Will Ness
  • 70,110
  • 9
  • 98
  • 181