我试图解决Problem 14 in Project Euler(找到1到1000000之间最长的Collatz序列)。
我的代码由一个递归的,memoized函数组成,用于计算Collatz序列的长度,然后循环找到最大值。请参阅以下代码:
(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))
这在Clozure中运行良好,但在Lispworks中导致堆溢出。由于它不合时宜地退出,我无法找出发生了什么。实际上,我不明白为什么这会消耗如此多的堆空间 - 最大的递归序列是300多次调用。我是否错过了代码中的低效率?
任何输入都表示赞赏。关于代码的进一步评论也很受欢迎。
PS:我使用的是Lispworks Personal Edition,它对堆大小施加了限制。
更新 我确实按照Rainer Joswig的建议进行了编译,但它并没有帮助。
关于coredump和sds的评论,or
在这种情况下确实比if
更好,但是我无法用哈希表替换向量,因为collatz序列在大约50%的时间里上升。运行代码后,哈希表有大约250万个条目。
最后,奇怪的是,我设法重现了这个错误,同时测试了一个长循环(一百万次迭代)的语法,它只是处理了一些变量并且根本没有收集任何东西。遗憾的是,我失去了代码--LispWorks只是蠢蠢欲动,唉。我最好的猜测是LispWorks中存在一些泄漏或其他内存管理故障。内脏。
答案 0 :(得分:3)
我发现这里存在两个效率低下的问题:
您正在使用由连续整数序列索引的哈希表。你可能应该使用(可扩展的)向量。
你的递归不是尾递归;您可能更愿意优化它。
不可否认,这些都不能解释堆耗尽。
答案 1 :(得分:3)
有一件事是确保编译n-collatz
:
(compile 'n-collatz)
或者通过常用的IDE命令使用编译器。
输入LispWorks Listener的代码另有解释:
CL-USER 140 > (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))))))))
N-COLLATZ
CL-USER 141 > #'n-collatz
#<interpreted function N-COLLATZ 40600020CC>
CL-USER 142 > (compile 'n-collatz)
N-COLLATZ
NIL
NIL
CL-USER 143 > #'n-collatz
#<Function N-COLLATZ 4060007054>
答案 2 :(得分:3)
LispWorks只是蠢蠢欲动,唉。我最好的猜测是有一些 泄漏或其他内存管理故障在LispWorks&#39;内脏。
你正在使用LW的个人版,它有一个内存限制,这个东西达到了这个限制。它引发了一个对话框,然后退出。
LW的商业版本没有任何问题。
答案 3 :(得分:1)
我认为每次调用
时都必须调整哈希表的大小(setf(gethash n collatz ))
的数字n高于当前表格大小。当您在没有size参数的情况下调用make-hash-table时,系统会选择依赖于实现的值。每次超过此值时,必须在执行期间调整表的大小,这会消耗大量资源并可能导致您提到的崩溃。 要解决此问题,您可以使用您不知道的值来创建表:
(defparameter *collatz* (make-hash-table :size 1000000)).
答案 4 :(得分:0)
我正在使用解释程序在Mac上运行LispWorks 7.1.1 64bit
CL-USER 1 > (defparameter *collatz* (make-hash-table))
*COLLATZ*
CL-USER 2 > (setf (gethash 1 *collatz*) 0)
0
CL-USER 3 > (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))))))))
N-COLLATZ
CL-USER 4 > (loop for i from 1 to 1000000 maximize (n-collatz i))
Stack overflow (stack size 15998).
1 (continue) Extend stack by 50%.
2 (abort) Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
因此,上面显示的是“堆栈溢出”,而不是“堆溢出”。请注意,可以调整堆栈的大小并继续。
现在,我在全新的LispWorks中再次尝试了该方法,但是编译了该函数:
CL-USER 1 > (defparameter *collatz* (make-hash-table))
*COLLATZ*
CL-USER 2 > (setf (gethash 1 *collatz*) 0)
0
CL-USER 3 > (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))))))))
N-COLLATZ
CL-USER 4 > (compile 'n-collatz)
N-COLLATZ
NIL
NIL
CL-USER 5 > (loop for i from 1 to 1000000 maximize (n-collatz i))
524
编译后的代码可以正常工作,而无需增加堆栈。