It turns out that although I’d already solved project euler problem 14, for some reason or another, I didn’t have the code stored in my git repository along with all the rest of them.
So I sat down to write some code:
collatz 1 = [1]
collatz n = n : collatz next
where
next | even n = n `div` 2
| odd n = 3 * n + 1
That was my first definition of the collatz sequence. I rewrote it using unfoldr:
collatz = unfoldr next
where
next 0 = Nothing
next 1 = Just (1, 0)
next n | even n = Just (n, n `div` 2)
| odd n = Just (n, 3 * n + 1)
… which I liked a little bit better.
Then I went on to actually solving the problem: looking for the integer with the longest collatz sequence (in the range 1..1000000). Here’s a straightforward approach:
maximumBy (comparing $ length.collatz) [1..1000000]
It almost reads like English. But of course, it doesn’t actually Just Work. We get a stack overflow, since maximumBy uses a lazy foldl:
maximumBy :: (a -> a -> Ordering) -> [a] -> a maximumBy _ [] = error "List.maximumBy: empty list" maximumBy cmp xs = foldl1 max xs where max x y = case cmp x y of GT -> x _ -> y
So I wrote this using the strict foldl1':
main = print $ foldl1' max $ map (length.collatz) [1..1000000]
… which runs without overflowing, but of course, it doesn’t actually return the number I’m looking for (it prints out the length of the longest sequence, not the integer that generated that sequence). But anyway, it shows the proof of concept: strict evaluation of the fold avoids the stack overflow. So I just need a strict maximumBy', which is just the same as maximumBy but uses foldl1' in place of foldl1.
So now I have a program that runs and produces the correct answer, but it takes about 90-100 seconds.
It turns out my original definiton of collatz (not using unfoldr) brings this down to about 70 seconds. So okay, I use that instead.
Then just for kicks, I wrote this:
main = print . snd . maximum $ map ((length &&& head) . collatz) [1..1000000]
… and to my great surprise, it ran in 8 seconds! There’s clearly something going on here that I don’t understand yet, and it amounts to a 10x speed difference.