I recently began learning Clojure. In order to get better acquainted with it, I've been tackling Project Euler challenges.
Problem 14 is not really a difficult one. It asks for the number that results in the longest Collatz sequence.
The following iterative sequence is defined for the set of positive integers:
n → n/2 (n is even) n → 3n + 1 (n is odd)
Using the rule above and starting with 13, we generate the following sequence: 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.
Which starting number, under one million, produces the longest chain?
NOTE: Once the chain starts the terms are allowed to go above one million.
Although my solution does solve the problem, I am not sure if it conforms to the Clojure way of doing things, since it seems to be verbose.
- Does this conform to functional practices?
- Are there any mistakes which result in code being longer than needed?
- Is there a way of omitting the loop/recur and doing the same with list functions such as
map
andapply
- Any other suggestions in general?
(ns problem14)
(use '[clojure.test :only (is)])
(defn count-collatz
"Returns a vector [a b] where b is the
number that initiated the sequence, and
a is the number of steps taken to reach 1."
[input-num]
(loop [num input-num count 1]
(if (= num 1)
(vector count input-num)
(do (if (= (mod num 2) 0)
(recur (/ num 2) (inc count))
(recur (+ (* num 3) 1) (inc count)))))))
;; Test case from the project description.
(is (= (count-collatz 13) [10 13]))
(loop [number 1 peak [0 0]]
(if (>= number 1e6)
(str "Longest chain is " (last peak))
(do (let [result (count-collatz number)]
(do (if (> (first result) (first peak))
(recur (inc number) result)
(recur (inc number) peak)))))))