60 Days of Euler in F# - Problem 44

The Problem

Pentagonal numbers are generated by the formula, Pn=n(3n−1)/2. The first ten pentagonal numbers are:

1, 5, 12, 22, 35, 51, 70, 92, 117, 145, ...

It can be seen that P4 + P7 = 22 + 70 = 92 = P8. However, their difference, 70 − 22 = 48, is not pentagonal.

Find the pair of pentagonal numbers, Pj and Pk, for which their sum and difference are pentagonal and D = |Pk − Pj| is minimised; what is the value of D?

The Solution

I like it when the problem spells out the main function we're going to need to find the solution. So let's start with that:

let p n = n * (3 * n - 1) / 2

I lack the math skills to choose a good upper bound for this one, so I'll arbitrarily choose 10000. If we find we don't get the right answer, we can adjust it later.

[<Literal>]
let maxN = 10000

We we really need to get going is an array of pentagonal numbers that we can pair up. So let's take the first 10000 pentagonal numbers and stuff them in an array.

let pentagonalNumbers = 
    Seq.unfold (fun state -> Some(p state, state + 1)) 1
    |> Seq.take maxN
    |> Array.ofSeq

And since we need to test our results to see if they are pentagonal, we can use that same array to build a map that we can use to test our results for pentagonality. (Is that a word? If not, it should be.)

let pentagonalMap = 
    pentagonalNumbers 
    |> Seq.map (fun x-> (x,x)) 
    |> Map.ofSeq

let isPentagonal n = 
    Map.containsKey n pentagonalMap

To solve the problem, we'll start by pairing up our pentagonal numbers with other pentagonal numbers.

let combos = [
    for j in 0..(maxN-2) do
        for k in (j+1)..(maxN-1) -> 
            (pentagonalNumbers.[j],pentagonalNumbers.[k])
]

combos pairs up j values (all pentagonal numbers except the last one) with k values (all pentagonal numbers greater than the j value). This ensures that Pk - Pj is always positive and that j and k are never equal.

Now we can solve the problem:

combos
|> List.filter (fun (j,k) -> isPentagonal (j + k) && isPentagonal (k - j))
|> List.map (fun (j,k) -> k - j)
|> List.min

We filter out combos for which j+k is not pentagonal or k-j is not pentagonal. Then we map the filters pairs to k-j. The minimum value of k-j is the answer.

Other Posts in This Series