Claudio Cherubino's blog Life of a Googler

17Apr/083

Project Euler in F# – Problem 45

A long time has passed since I last posted the solution of one of Project Euler's problems.

Problem 45 is related to figurate numbers, i.e. numbers that can be represented as geometrical patterns.

In particular, we are going to find a number that is triangular, pentagonal and hexagonal at the same time:

Triangle, pentagonal, and hexagonal numbers are generated by the following formulae:
Triangle: Tn=n(n+1)/2
Pentagonal: Pn=n(3n?1)/2
Hexagonal: Hn=n(2n?1)

It can be verified that T285 = P165 = H143 = 40755.

Find the next triangle number that is also pentagonal and hexagonal.

A trivial solution will be computing the lists of triangular, pentagonal and hexagonal numbers and then find their intersection. However, we don't know how big the searched number can be, so we are not able to determine when to stop the generation of each list.

Hence we need a smarter solution, based on the mathematical properties of these numbers.

First of all, every hexagonal number is also a triangular number, so our problem is reduced to finding the smallest hexagonal pentagonal number greater than 40755.

Then we can generate the hexagonal numbers and check whether they are also pentagonal, stopping at the first one found.

According to the definition, we can test if a positive integer x is a pentagonal number by computing:

Test for pentagonal numbers

If n is an integer, then x is the nth pentagonal number. If n is not an integer, then x is not pentagonal.

Let's look at the F# code:

#light
open System
open Microsoft.FSharp.Math.BigInt

let hexagonal n = n * (2I * n - 1I)
let isPentagonal x = ((sqrt(to_float (24I * x + 1I)) + 1.0) % 6.0) = 0.0

let nums = 144I |> Seq.unfold (fun i -> Some (i, i + 1I))
let answer = nums |> Seq.map hexagonal |> Seq.find isPentagonal

At lines 5 and 6 we define the functions to compute the nth hexagonal number and to check if an integer x is pentagonal.

Then we start generating the hexagonal numbers to be tested, starting from the 144th, since the solution must be greater than 40755, that is the 143th hexagonal number.

In the last line we just return the first number for which isPentagonal is true, without having to know when to stop thanks to lazy evaluation, which allows us to continue generating numbers only when actually needed.

The solution to the problem is greater than I could imagine, that's why we need to use BigInt instead of normal integers.

What do you think about the approach I followed to solve this problem?

Comments (3) Trackbacks (0)
  1. I think this approach is great! I’ve been working on Problem 45 myself using the ‘generate three infinite sequences, and increment until they are all equal’ approach. However, as you discovered, the search space is a tad large…

  2. Good thingking, thanks for the hints. I’m not like the idea that generate three large sequences. Your idea is much better. I’m not use bignums, the solution is an integer of 32 bits. My C++ code:

    #define EPS 1e-10
    #define MAX 100000

    int hex(int n){ return (int) n*(2*n-1);}
    bool is_pentagonal(int x){
    double _n;
    int n;
    _n = (sqrt(24.0*x + 1.0) + 1.0)/6.0;
    n = (int) _n;
    return (fabs(_n-(double)n)<EPS);
    }
    main(){
    int j, hexagonal;
    for(j=144;j%d\n”,hexagonal);
    break;
    }
    }

    }
    [/c++]

  3. Thanks, the “The solution to the problem is greater than I could imagine” was they key i needed to find my error


Leave a comment

(required)

No trackbacks yet.