为什么这会破坏Lispworks中的堆?

时间:2015-10-08 15:44:53

标签: lisp common-lisp collatz lispworks

我试图解决Problem 14 in Project Euler(找到1到1000000之间最长的Collat​​z序列)。

我的代码由一个递归的,memoized函数组成,用于计算Collat​​z序列的长度,然后循环找到最大值。请参阅以下代码:

(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更好,但是我无法用哈希表替换向量,因为collat​​z序列在大约50%的时间里上升。运行代码后,哈希表有大约250万个条目。

最后,奇怪的是,我设法重现了这个错误,同时测试了一个长循环(一百万次迭代)的语法,它只是处理了一些变量并且根本没有收集任何东西。遗憾的是,我失去了代码--LispWorks只是蠢蠢欲动,唉。我最好的猜测是LispWorks中存在一些泄漏或其他内存管理故障。内脏。

5 个答案:

答案 0 :(得分:3)

我发现这里存在两个效率低下的问题:

  1. 您正在使用由连续整数序列索引的哈希表。你可能应该使用(可扩展的)向量。

  2. 你的递归不是尾递归;您可能更愿意优化它。

  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 collat​​z ))

的数字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

编译后的代码可以正常工作,而无需增加堆栈。