我正在尝试创建一个函数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)"
你能告诉我为什么它会返回这个错误吗?递归调用有什么问题吗?
[UPDATE]
我重新定义了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)
构造列表,找到下一个素数因子需要很长时间。如何优化我的代码?
答案 0 :(得分:5)
关于您的代码的一些一般性评论:
(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
比较数字,然后您可以使用if
,or
和and
将not
表达式重写为逻辑表达式。
(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
命令到达顶级代表的时间:
CL-USER 44 : 5 > :top
CL-USER 45 >