--- title: "Project Euler #5: Smallest Multiple" author: nbloomf date: 2017-03-10 tags: project-euler, literate-haskell --- First some boilerplate. > module ProjectEuler005 where [Problem 5](https://projecteuler.net/problem=5) from Project Euler:
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?
The smallest natural number $k$ that is divisible by natural numbers $a$ and $b$ is called their *least common multiple*; we'll denote this by $a \wedge b$. The inverse notion, the *largest* natural number that *divides* both $a$ and $b$, is called their *greatest common divisor*; we'll denote this by $a \vee b$. As operations, the LCM and GCD have lots of nice properties. But two are important for us. For all natural numbers $a$, $b$, and $c$, we have the following. 1. $a \wedge b = \frac{ab}{a \vee b}$ 2. $a \wedge (b \wedge c) = (a \wedge b) \wedge c$ The first property says that the LCM can be computed easily if we know the GCD. This is useful because there is a nice algorithm, called the Euclidean Algorithm, for computing GCDs.
Let $a$ and $b$ be natural numbers, and let $q$ and $r$ be natural numbers such that $a = qb+r$ and $0 \leq r < b$. Then $a \vee b = b \vee r$.
What makes the Euclidean Algorithm so nice is that, combined with the Well-Ordering Property of the natural numbers, it gives us a recursive algorithm for computing GCDs. > (\/), (/\) :: Integer -> Integer -> Integer > > a \/ 0 = a > a \/ b = b \/ (a`rem`b) > > a /\ b = (a*b)`div`(a \/ b) The second property above says that if we want to find the LCM of a bunch of integers, we can do so pairwise, and it doesn't matter what order we do this in. For instance, using ``foldr1``: > lcms :: [Integer] -> Integer > lcms = foldr1 (/\) Now ``lcms`` will take a nonempty list of positive integers and compute their least common multiple. ```haskell $> lcms [1..10] 2520 ``` The Euclidean Algorithm is pretty snappy. Just for fun, here are some timings. ------------------------------- ``k`` ``lcms [1..(k*10^4)]`` ------ ----------------------- 1 0.26 s 2 0.74 s 3 1.76 s 4 2.67 s 5 4.26 s 6 5.63 s 7 7.81 s 8 9.51 s 9 11.24 s ------------------------------- Anyway, the final answer is: > pe5 :: Integer > pe5 = lcms [1..20] > > main :: IO () > main = putStrLn $ show pe5