I’m beginning to learn F# and the best way for me to go about that is to start applying it. There are some interesting computational problems over at Project Euler . This post is the continuation of a series of posts each involving a separate Project Euler problem. This post involves solutions to Project Euler problem #14.

The following iterative sequence is defined for the set of positive integers:

n → n/2 (n is even) n → 3n + 1 (n is odd)

Using the rule above and starting with 13, we generate the following sequence: 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1

It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.

Which starting number, under one million, produces the longest chain?

NOTE: Once the chain starts the terms are allowed to go above one million.

Here’s my first stab at a solution:

open Microsoft.FSharp.Math let isEven i = i % 2I = 0I let rec sequence (n:bigint) = let next n = if isEven n then n/2I else 3I*n+1I seq { if n <> 1I then yield! sequence (next n) } let answer1:Lazy<bigint> = lazy (seq { 1I..999999I } |> Seq.map sequence |> Seq.maxBy Seq.length |> Seq.hd) |

This works, but it’s really slow trying to compute all of those sequences. Let’s try to memoize this function:

open System.Collections.Generic let memoize (func:'a->'b) = let cache = Dictionary<_, _>() fun n -> let (found, result) = cache.TryGetValue(n) if found then result else let result = func n cache.[n] <- result result let rec length:bigint->int = let f n = let next n = if isEven n then n/2I else 3I*n+1I if n = 1I then 1 else 1 + length (next n) f |> memoize let answer2 = lazy(seq { 1I..999999I } |> Seq.maxBy length) |

Instead of generating the sequence I simply compute the length of it and save the result – the time to generate the answer dropped from over 3 minutes to under 10 seconds on my machine.