1

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?

HexedAgain
  • 1,011
  • 10
  • 21
  • Hey, marking this as duplicate because this is a very common gotcha and I think it'd be good to boost the visibility of some of the "canonical answers" already present on SO. Another really great one is here – [Why inserting 1000 000 values in a transient map in Clojure yields a map with 8 items in it?](http://stackoverflow.com/questions/29684803/why-inserting-1000-000-values-in-a-transient-map-in-clojure-yields-a-map-with-8/29684903#29684903). If it doesn't solve the problem completely, please let me know in a comment and I'll reopen (and do my best to address any gaps). – Michał Marczyk Oct 30 '15 at 14:00
  • 1
    @MichałMarczyk Aha ... thanks for that, it would appear (given only 8 "updates" in my debug output that that post explains my problem) - I'll keep working on this then – HexedAgain Oct 30 '15 at 14:06
  • BTW, this is a good use case for `memoize`. Look it up. – Diego Basch Oct 30 '15 at 14:39
  • 1
    @DiegoBasch cheers, i've had a play and it looks like what I want, albeit I now overflow somewhere (massive recurring java stack trace) when I map my new (memoized recursive) function over a range exceeding 35655 - perhaps implementation maintains some sort of stack and I'm blowing it?? – HexedAgain Oct 30 '15 at 16:17

0 Answers0