Project Euler in F#: Problem 2

Problem 2

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …
Find the sum of all the even-valued terms in the sequence which do not exceed four million.

The Thinking

The problem is similar to Problem 1, but this time instead of a natural number sequence, we’re to use a fibbonacci sequence, evens only, less than 4 million. Then, we’ll need to sum them up; that we did in Problem 1 with Seq.sum. How can we generate the sequence? I’ll be taking a simple two-step approach. First, figure out a function to generate a fibbonacci sequence (up through 4,000,000), then take all the evens. We can Seq.sum that resulting sequence to find our answer.

Generating a Fibonacci Sequence

Generating a fibbonacci sequence in F# is a textboook case, and so much so that it’s actually an example snippet for the built-in F# function we’re going to use to generate it: Seq.unfold. Seq.unfold is a  function that returns a sequence, based on a function we provide. It takes two parameters: a sequence element generator function, and the inital value to start with.
[sourcecode]Seq.unfold generator state[/sourcecode]
The generator function must be defined with a single input parameter (the “state value”), and returning an “option tuple of the next element in the sequence and the next state value” (from documentation).

A fibbonacci algorithm requires two inputs (the previous two digits), but we can only pass one parameter. Luckily in F# we have Tuples. A tuple lets us package up several values into a single group, and is written with as a comma separated list inside parenthesis:
[sourcecode]let sometuple = (“this”, “is”, “a”, “single”, “tuple”)[/sourcecode]
The return value is also something new, using the built-in Option module. We’ll be returning an Option, either Some or None. None is a signal to unfold that this is the end of sequence, and for all the rest of the return results, we need to return Some-thing (the next element of the sequence as well as the next state value in tuple form) as we learned above, from the documentation for unfold.

This might seem like a lot to process all at once, but it ends up looking pretty simple when it’s all put together. The generator function for fibbonacci numbers less than 4m looks like this:
[sourcecode]let fibgen (x,y) = //define a function ‘fibgen’ and pass in a single parameter, a tuple that represents the most recent two digits of the fibbonacci sequence so far
if(x < 4000000) then // define a cut-off threshold to keep the sequence from going on forever Some(x+y, (y, x+y)) // return an Option tuple; the next elemnet of the sequence: x+y (the two most recent elements added together), and the next state value- a single tuple that will be used next time the funciton is run else None // we're up to 4m, so tell unfold we're done with the sequence [/sourcecode] And now, we can plug that into Seq.unfold: [sourcecode]let fibseq = Seq.unfold fibgen (1,1) // (1,1) is a single tuple parameter with the initial values for the fibgen function[/sourcecode] If we run this in the Interactive F# window in Visual Studio, we can confirm this produces the full fibbonacci sequence: [sourcecode]val it : seq = seq [2; 3; 5; 8; ...][/sourcecode]

Getting Just The Evens

If you recall our solution to Problem 1, it should be easy to figure out how to make a new sequence with only the even values by using seq, modulo, and yield that we’ve already learned.
[sourcecode]let fibevens = seq{for i in fibseq do if i % 2 = 0 then yield i}[/sourcecode]

My Solution

Putting it all together, with Seq.sum to add up the sequence:
[sourcecode]let fibgen (x,y) = if(x < 4000000) then Some(x+y, (y, x+y)) else None let fibseq = Seq.unfold fibgen (1,1) let fibevens = seq{for i in fibseq do if i % 2 = 0 then yield i} let result = Seq.sum fibevens printfn "%A" result [/sourcecode] Project Euler Problem 2: Answered

Further Reading

A Post Script

Each part of the above solution is named for clarity. We could easily compose these functions for a more compact solution:
[sourcecode]printfn “%A” (Seq.sum(seq{for i in ((1,1) |> Seq.unfold(fun (x,y) -> if(x < 4000000) then Some(x+y, (y, x+y)) else None)) do if i % 2 = 0 then yield i}))[/sourcecode] The only thing that gets weird in this compact version is the anonymous replacement for fibgen, which uses lambda syntax fun & ->, and the pipeline operator, |> to pass in the intial state. There are some goofy rules for when you can and cannot use piplineing; check out the Pipeline section of Chapter 8 of The F# Survival Guide for a good primer.

2 thoughts on “Project Euler in F#: Problem 2

Leave a Reply

Your email address will not be published.