I'm trying to solve Problem 14 in Project Euler (find the longest Collatz sequence between 1 and 1000000).
My code consists of a recursive, memoized function to calculate the length of Collatz sequences followed by a loop to find the maximum. Please see the code below:
(defparameter *collatz* (make-hash-table))
(setf (gethash 1 *collatz*) 0)
(defun n-collatz (n)
(if (gethash n *collatz*)
(gethash n *collatz*)
(setf (gethash n *collatz*)
(if (evenp n)
(1+ (n-collatz (/ n 2)))
(1+ (n-collatz (1+ (* n 3))))))))
(loop for i from 1 to 1000000 maximize (n-collatz i))
This worked fine in Clozure, but caused a heap overflow in Lispworks. As it exited ungracefully, I couldn't find out what happened. Actually, I don't understand why this consumes so much heap space—the biggest recursion sequence is 300-something calls. Did I miss some inefficiency in the code?
Any input is appreciated. Further comments on the code are appreciated as well.
PS: I'm using Lispworks Personal Edition, which imposes a limit on heap size.
UPDATE I did try compiling as Rainer Joswig suggested, but it didn't help.
Regarding coredump's and sds's comments, or
is indeed better than if
in this case, but I can't substitute the hash table for a vector because the collatz sequence goes up about 50% of the time. After running the code, the hash table has some 2.5 million entries.
Finally, and strangely, I managed to reproduce the bug while testing the syntax of a longish loop (a million iterations) that just juggled some variables and didn't collect anything at all. I lost the code unfortunately—LispWorks just went poof, alas. My best guess is that there's some leak or other memory management glitch in LispWorks' entrails.
I see two inefficiencies here:
You are using a hash table indexed by a contiguous integer sequence. You should probably be using an (extensible) vector instead.
Your recursion is not tail-recursion; you might prefer to optimize that.
Admittedly, none of these could explain a heap exhaustion.