Project Euler – Problem 5 Solution

Yan Cui

I help clients go faster for less using serverless technologies.

This article is brought to you by

The real-time data platform that empowers developers to build innovative products faster and more reliably than ever before.

Learn more

Problem

2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.

What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?

Solution

let isEvenlyDivided(n, m) = n % m = 0
let isEvenlyDividedByAll(n, numbers) = numbers |> Seq.forall (fun x -> isEvenlyDivided(n, x))

let findSmallestCommonMultiple(numbers) =
    let max = Array.max(numbers)
    Seq.unfold (fun x -> Some(x, x + 1)) max
    |> Seq.filter (fun x -> isEvenlyDividedByAll(x, numbers))
    |> Seq.head

let commonMultiplier = findSmallestCommonMultiple [|1..20|]

Again, I build two functions to handle the logic of checking whether a number can be evenly divided by all the numbers in a supplied list. I have used another new function Seq.forall (I can’t find the MSDN doc for this function, but see List.forall instead) which tests each element in the list with the supplied predicate.

In order to find the smallest common multiple for all the numbers from 1 to 20, I first generated a sequence of all the natural numbers equal or greater than 20:

Seq.unfold (fun x –> Some(x, x + 1)) max // max is resolved to 20 by Array.max(numbers)

Then for each number I applied the predicate isEvenlyDividedByAll to see if it can be evenly divided by each of the numbers from 1 to 20, and finally, Seq.head returns the first element that matches the predicate and that’s our answer!

One last thing though, when you run this code you’ll see that it takes a rather long time to return. So to improve on the performance of this logic, you can add a small and yet effective step to only test numbers which are multiples of the largest number (20 in our case) in the list:

let findSmallestCommonMultiple(numbers) =
    let max = Array.max(numbers)
    Seq.unfold (fun x -> Some(x, x + 1)) 1
    |> Seq.map (fun x -> x * max)
    |> Seq.filter (fun x -> isEvenlyDividedByAll(x, numbers))
    |> Seq.head

Run the new findSmallestCommonMultiple function again and it now returns much much quicker :-)

Whenever you’re ready, here are 4 ways I can help you:

  1. Production-Ready Serverless: Join 20+ AWS Heroes & Community Builders and 1000+ other students in levelling up your serverless game. This is your one-stop shop for quickly levelling up your serverless skills.
  2. Do you want to know how to test serverless architectures with a fast dev & test loop? Check out my latest course, Testing Serverless Architectures and learn the smart way to test serverless.
  3. I help clients launch product ideas, improve their development processes and upskill their teams. If you’d like to work together, then let’s get in touch.
  4. Join my community on Discord, ask questions, and join the discussion on all things AWS and Serverless.

4 thoughts on “Project Euler – Problem 5 Solution”

  1. hello, how long did the first solution take? I am still waiting for the output :). The second solution worked pretty fast.

  2. actually you don’t have to check for all numbers, if a number can be divided to 20 => it can be div to 2,5 and 10 as well
    here’s my solution:

    let p5 =
    let isDivisor n =
    let rec loop = function
    | 10 -> true
    | x when n % x = 0 -> loop (x-1)
    | _ -> false
    loop 20
    let rec search = function
    | x when isDivisor x -> x
    | x -> search (x+1)
    search 2520

  3. Excellent series of blog posts!

    I was able to get down to 8 seconds by generating just multiples of 20:

    let isDivisibleBy seq n =
    seq
    |> Seq.forall (fun x -> n % x = 0)

    Seq.unfold (fun x -> Some(x, x + 20)) 20
    |> Seq.find (isDivisibleBy [|1 .. 20|])

Leave a Comment

Your email address will not be published. Required fields are marked *