EDIT It would appear I've run afoul of a common transient gotcha as noticed by Michal
I'm quite new to clojure, and am trying to solve project euler: 14 in a functional style without fully calculating each of 999999 collatz sequences. I.e. the collatz chain starting from 10 goes:
10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
As such length of collatz(10) = 7
If I then looked at the collatz chain for 9 I should proceed as follows:
9 -> 28 -> 14 -> 7 -> 22 -> 11 -> 34 -> 17 -> 52 -> 26 -> 13 -> 40 -> 20 -> 10 ***stop since I should have stored length(collatz(10)) somewhere***
from which I would get that length of collatz(9) = 13 + 7
To this end I want for every node in a collatz chain, to store its associated collatz length, and break once I see I'm treading the same ground. I expect this should just be linear if I get it right (and I only need to look within range [bound, bound/2])
My "first" attempt (ignoring those which are hopeless (like building in reverse)) was:
(defn _collatz [n dict]
(cond (contains? dict n) (list n (dict n))
(> n 1) (let [temp (_collatz (if (zero? (mod n 2)) (/ n 2) (+ (* 3 n) 1)) dict)
ret (cons n (cons (inc (second temp)) temp))] ret)
true '(1 1)))
(defn collatz [n & {:keys [dict] :or {dict {}}}]
(conj (apply hash-map (_collatz n dict)) dict))
(defn largest-collatz [bound]
(let [all-chain-lengths ((apply comp (map #(partial collatz % :dict) (range bound (+ (/ bound 2) 2) -1))) {})]
... gave up implementing this bit ...
all-chain-lengths))
and this is quadratic on bound (which I believe is due to walking down some list every pass, forming a dict out of it and then conjoining it to the dict I'd just passed in - doing this O(n) operation O(n) times. I have given up fiddling with it (for example, instead returning dicts which will be conj'd with other dicts and so on ...) because I think returning and joining any collection is going to be n^2 - I need create one dictionary and update it in place)
My second attempt is:
(defn _collatz2 [n dict]
(cond (not (nil? (get dict n))) (get dict n)
(> n 1) (let [len (inc (_collatz2 (if (zero? (mod n 2)) (/ n 2) (+ (* 3 n) 1))
dict))]
(assoc! dict n len)
(println (str "with n equal to " n ", returning: " len " - n is in dict?? (get dict " n "): " (get dict n)))
len)
true 1))
which if called at the repl as per:
(def a (transient {}))
(_collatz2 99 a)
(persistent! a)
gets me:
{2 2, 4 3, 8 4, 16 5, 5 6, 10 7, 20 8, 40 9}
and I am stumped as to what is happening here. the debug output is:
with n equal to 2, returning: 2 - n is in dict?? (get dict 2): 2
with n equal to 4, returning: 3 - n is in dict?? (get dict 4): 3
with n equal to 8, returning: 4 - n is in dict?? (get dict 8): 4
with n equal to 16, returning: 5 - n is in dict?? (get dict 16): 5
with n equal to 5, returning: 6 - n is in dict?? (get dict 5): 6
with n equal to 10, returning: 7 - n is in dict?? (get dict 10): 7
with n equal to 20, returning: 8 - n is in dict?? (get dict 20): 8
with n equal to 40, returning: 9 - n is in dict?? (get dict 40): 9
with n equal to 13, returning: 10 - n is in dict?? (get dict 13):
with n equal to 26, returning: 11 - n is in dict?? (get dict 26):
with n equal to 52, returning: 12 - n is in dict?? (get dict 52):
with n equal to 17, returning: 13 - n is in dict?? (get dict 17):
with n equal to 34, returning: 14 - n is in dict?? (get dict 34):
with n equal to 11, returning: 15 - n is in dict?? (get dict 11):
with n equal to 22, returning: 16 - n is in dict?? (get dict 22):
with n equal to 7, returning: 17 - n is in dict?? (get dict 7):
with n equal to 14, returning: 18 - n is in dict?? (get dict 14):
with n equal to 28, returning: 19 - n is in dict?? (get dict 28):
with n equal to 56, returning: 20 - n is in dict?? (get dict 56):
with n equal to 112, returning: 21 - n is in dict?? (get dict 112):
with n equal to 224, returning: 22 - n is in dict?? (get dict 224):
with n equal to 448, returning: 23 - n is in dict?? (get dict 448):
with n equal to 149, returning: 24 - n is in dict?? (get dict 149):
with n equal to 298, returning: 25 - n is in dict?? (get dict 298):
with n equal to 99, returning: 26 - n is in dict?? (get dict 99):
anyone have any ideas?