Clojure Euler: Problem 012

What is the value of the first triangle number to have over five hundred divisors?


The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28.

The first ten terms would be:

1, 3, 6, 10, 15, 21, 28, 36, 45, 55, …

Let us list the factors of the first seven triangle numbers:

 1: 1
 3: 1,3
 6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28

We can see that 28 is the first triangle number to have over five divisors.

What is the value of the first triangle number to have over five hundred divisors?

When we see infinite sequence of some numbers, first thing that we do is implement it with using lazy sequences. Simple explanation and example for Fibonacci numbers can be found in Clojure Euler: Problem 002.

We have an easy case, so we implement triangle number as sum of all integers below:

(defn triangle-number [n]
  (reduce + (range 1 (inc n))))

Don’t miss the possibility reduce complexity from O(n) to O(1). Just apply arithmetic progression formula.

(defn triangle-number [n]
  (* n (/ (+ n 1) 2)))

Okey, we did small optimization (not so small) here and we have a function that calculates triangle number for n. Let’s find all of them!

(def triangles (map triangle-number (iterate inc 1)))

triangles refers to lazy sequence of triangle numbers.

Do not evaluate lazy seqs!

Use limit functions as take, take-while, drop, drop-while to test the sequence values, or build other lazy seqs with filter, map, etc.

Now, we need a function to calculate number of divisors.

(defn num-of-divisors [n]
  (* 2 (count (filter #(= (mod n %) 0) (range 2 (inc (int (sqrt n))))))))

If we remember Clojure Euler: Problem 003 then we know that using sqrt(n) instead of n as upper bound for divisors saves much time. Don’t forget mutiply that value by two, as you skip number after sqrt(n).

Last step: to calculate number over 500 divisors

(first (drop-while #(< (num-of-divisors %) 500) triangles))

We got the result, but spent ~11 secs. Too much.

How can we improve our solution?

There is theorem about Prime Factorization states:

Every positive integer has a unique prime factorization

For example: 15 = 3^1 * 5^1, 18 = 2^1 * 3^2 and so on.

Not hard to see that number of divisors from such factorization can be obtained by multiplying all prime powers incremented by one.

For example number 18 have 6 divisors (1, 2, 3, 6, 9, 18). If we take factorization 18 = 3^1 * 5^1, then number of divisors is equal to (1 + 1) * (2 + 1) = 6. You see, the same. Not hard to prove this theorem or read the proof.

Stop math, we need to code, so let’s code that.

First of all, we need factorization method:

(defn factorize [n]
  (loop [x n [p & ps] primes factors []]
    (cond (= 1 x) factors
          (zero? (mod x p)) (recur (/ x p) primes (conj factors p))
          :else (recur x ps factors))))

It uses primes from clojure.contrib.lazy-seqs. This method prints all factors (including duplicates) for n.

For example:

(factorize 18) => [2 3 3]

But instead of actual values of divisors, we just need their count.

(defn factorize-count [n]
  (reduce * (map (comp inc count) (vals (group-by identity (factorize n))))))

Calculate the result again:

(first (drop-while #(< (factorize-count %) 500) triangles))

Bingo! It gives the correct result in less than 3 seconds. Not bad as improvement.

P.S. If you test-addicted person, you, probably, point out that function num-of-divisors yields incorrect result for input 1. We can live with that, because our needed number is much greater than 1.

mishadoff 22 January 2013
blog comments powered by Disqus