【问题标题】:"Stack overflow (deep)" error in LispLisp 中的“堆栈溢出(深)”错误
【发布时间】:2018-08-27 04:07:19
【问题描述】:

我正在尝试创建一个函数prime-factors,它返回一个数字的质因数。为此,我创建了 is-prime 函数和 prime-factors-helper,它将对主要因素进行递归检查。

(defun is-prime (n &optional (d (- n 1))) 
  (if (/= n 1) (or (= d 1)
          (and (/= (rem n d) 0)
               (is-prime  n (- d 1)))) ()))

(defun prime-factors-helper (x n)
   (if (is-prime x) (list x) 
        (if (is-prime n) 
            (if (AND (= (mod x n) 0) (< n (/ x 2)))
                (append (list n) (prime-factors-helper (/ x n) (+ 1 n)))
                (prime-factors-helper x (+ 1 n)))       
            (prime-factors-helper x (+ 1 n)))))

(defun prime-factors (x)
    (prime-factors-helper x 2)) 

prime-factors 的主要功能似乎适用于某些数字。但是对于大数字,它会返回 "Stack overflow (deep)"

CL-USER 44 : 5 > (prime-factors 66)
(2 3 11)

CL-USER 49 : 5 > (prime-factors 512)
"Stack overflow (deep)" 

你能告诉我为什么它会返回这个错误吗?递归调用有问题吗?

[更新]

我重新定义了is-prime 函数,但显然这不是问题。

(defun is-prime (x &optional (i 2))
    (if (= x 1) nil
        (if (or (= x 2) (= x 3)) t
            (if (<= i (sqrt x))
                (if (= (mod x i ) 0) nil
                    (is-prime x (+ i 1)))
                t))))



(defun prime-factors-helper (x n)
   (if (is-prime x) 
       (list x) 
       (if (is-prime n) 
            (if (AND (= (mod x n) 0) (<= n (/ x 2)))
                (cons n (prime-factors-helper (/ x n) n))
                (prime-factors-helper x (+ 1 n)))       
            (prime-factors-helper x (+ 1 n)))))

优化问题

我还有另一个优化问题。当我有一个大数字(例如123456789)时,我会收到此错误消息Stack overflow (stack size 261120)。我相信因为正确答案是(3 3 3607 3803),所以我的程序一旦用前两个元素(3 3) 构造列表,就需要很长时间才能找到下一个主要因素。如何优化我的代码?

【问题讨论】:

  • 这意味着你的递归没有停止。您似乎停止递归的唯一一次是当 x 是素数时。如果这永远不会发生,那么递归将永远持续下去。您是否测试过is-prime 以确保它是正确的?
  • @Carcigenicate 是的,我测试过。没错
  • @Carcigenicate 它不是is-prime 函数。我对它的定义不同,但我仍然有完全相同的问题。请检查新的更新,我将is-prime 函数的新定义放在哪里
  • 那么你需要找出递归没有停止的原因。 (prime-factors 512) 也可能只是一个“太大”的问题,无法使用未优化的递归来解决。
  • @Carcigenicate 我解决了。实际上,不仅对于 512。它发生在所有 2 的倍数上。在条件(if (AND (= (mod x n) 0) (&lt; n (/ x 2))) 中,如果数字是 2 的倍数,即使它必须进入,它也永远不会进入这个条件。原因是我放了&lt;。应该是&lt;=。所以(if (AND (= (mod x n) 0) (&lt;= n (/ x 2)))。感谢您的帮助

标签: lisp common-lisp


【解决方案1】:

关于您的代码的一些一般性说明:

(defun is-prime (x &optional (i 2))
    (if (= x 1) nil
        (if (or (= x 2) (= x 3)) t
            (if (<= i (sqrt x))
                (if (= (mod x i ) 0) nil
                    (is-prime x (+ i 1)))
                t))))

(defun prime-factors-helper (x n)
   (if (is-prime x) (list x) 
        (if (is-prime n) 
            (if (and (= (mod x n) 0) (<= n (/ x 2)))
                (append (list n) (prime-factors-helper (/ x n) (+ 1 n)))
                (prime-factors-helper x (+ 1 n)))       
            (prime-factors-helper x (+ 1 n)))))


(defun prime-factors (x)
    (prime-factors-helper x 2)) 

CL-USER 44 : 5 > (prime-factors 66)
(2 3 11)

您应该改进的几件事:缩进、代码格式、正确使用 REPL 和选择 Lisp 函数:

代码缩进和格式化

让我们从缩进和格式开始。通常的缩进是:

(defun is-prime (x &optional (i 2))
  (if (= x 1)
      nil
    (if (or (= x 2) (= x 3))
        t
      (if (<= i (sqrt x))
          (if (= (mod x i ) 0)
              nil
            (is-prime x (+ i 1)))
        t))))

(defun prime-factors-helper (x n)
  (if (is-prime x)
      (list x) 
    (if (is-prime n) 
        (if (and (= (mod x n) 0) (<= n (/ x 2)))
            (append (list n) (prime-factors-helper (/ x n) (+ 1 n)))
          (prime-factors-helper x (+ 1 n)))
      (prime-factors-helper x (+ 1 n)))))


(defun prime-factors (x)
  (prime-factors-helper x 2)) 

改进代码

现在我们可以重写前两个函数:

使用case 比较数字,然后您可以使用orandnotif 表达式重写为逻辑表达式。

(defun is-prime (x &optional (i 2))
  (case x
    (1     nil)
    ((2 3) t)
    (t     (or (not (<= i (sqrt x)))
               (and (/= (mod x i) 0)
                    (is-prime x (+ i 1)))))))

接下来我们使用cond 降低缩进级别。

(append (list foo) bar) 只是(cons foo bar)

我们也可以去掉一个if

(defun prime-factors-helper (x n)
  (cond ((is-prime x)
         (list x))
        ((and (is-prime n)
              (= (mod x n) 0)
              (<= n (/ x 2)))
         (cons n (prime-factors-helper (/ x n) (+ 1 n))))
        (t (prime-factors-helper x (+ 1 n)))))

测试功能

现在我们需要对其进行测试:

(defun test (&optional (n 20))
  (loop for i from 2 below n
        for factors = (prime-factors i)
        do (print (list i
                        (= (reduce #'* factors) i)
                        (cons '* factors)))))

有问题:修复它!

如您所见,还有一个问题......当我们计算 8 的因数时,我不得不将代码从无限循环中中断。

CL-USER 51 > (test)

(2 T (* 2)) 
(3 T (* 3)) 
(4 T (* 2 2)) 
(5 T (* 5)) 
(6 T (* 2 3)) 
(7 T (* 7)) 
Break.
  1 (continue) Return from break.
  2 (abort) Return to top loop level 0.

但是这个很容易修复,并留作练习。

REPL。

当你在 LispWorks 中有这样的提示时

CL-USER 44 : 5 > 

这意味着你有五个 (!) 级别的休息时间。

是时候通过输入:top 命令进入顶级repl:

CL-USER 44 : 5 > :top

CL-USER 45 >

【讨论】:

  • 感谢您的发言。 2的倍数问题我已经解决了,只是忘记贴了。但是,我发现了另一个优化问题。你能在我的帖子中检查我的新问题吗?
  • @Benz:提出一个新问题,不要添加到现有问题中
  • 我刚做了。你能看看吗。 stackoverflow.com/questions/49352407/…
猜你喜欢
  • 1970-01-01
  • 2021-06-11
  • 2012-08-20
  • 1970-01-01
  • 2014-01-26
  • 2019-02-16
  • 2011-09-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多