gpt4 book ai didi

optimization - 如何优化我的递归 Lisp 函数

转载 作者:太空宇宙 更新时间:2023-11-03 19:03:54 24 4
gpt4 key购买 nike

我正在尝试创建一个函数 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)))
(cons n (prime-factors-helper (/ x n) n))
(prime-factors-helper x (+ 1 n)))
(prime-factors-helper x (+ 1 n)))))

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

问题

我有一个优化问题。当我有一个大数字时,例如 123456789,我收到此错误消息 Stack overflow (stack size 261120)。我相信因为正确的答案是 (3 3 3607 3803),我的程序一旦用前两个元素 (3 3) 构造列表,它就会这样长期寻找下一个质因数。如何优化我的代码?

 CL-USER 53 > (prime-factors 512)
(2 2 2 2 2 2 2 2 2)

CL-USER 54 > (prime-factors 123456789)

Stack overflow (stack size 261120).
1 (abort) Return to level 0.
2 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

最佳答案

复制自https://codereview.stackexchange.com/a/189932/20936 :

您的代码有几个问题。

风格

  1. is-prime 是 C/Java 风格。 Lispers 使用 primepprime-number-p
  2. zerop(= 0 ...) 更清晰。
  3. Lispers 使用缩进,而不是计算括号来阅读代码。你的因此代码几乎不可读。如果你是,请使用 Emacs不确定如何正确格式化 lisp。

堆栈溢出

is-prime 是尾递归的,所以如果你编译它,它应该变成简单的循环,应该没有堆栈问题。

但是,先不要着急。

算法

迭代次数

让我们使用trace看到问题:

> (prime-factors 17)
1. Trace: (IS-PRIME '17)
2. Trace: (IS-PRIME '17 '15)
3. Trace: (IS-PRIME '17 '14)
4. Trace: (IS-PRIME '17 '13)
5. Trace: (IS-PRIME '17 '12)
6. Trace: (IS-PRIME '17 '11)
7. Trace: (IS-PRIME '17 '10)
8. Trace: (IS-PRIME '17 '9)
9. Trace: (IS-PRIME '17 '8)
10. Trace: (IS-PRIME '17 '7)
11. Trace: (IS-PRIME '17 '6)
12. Trace: (IS-PRIME '17 '5)
13. Trace: (IS-PRIME '17 '4)
14. Trace: (IS-PRIME '17 '3)
15. Trace: (IS-PRIME '17 '2)
16. Trace: (IS-PRIME '17 '1)
16. Trace: IS-PRIME ==> T
15. Trace: IS-PRIME ==> T
14. Trace: IS-PRIME ==> T
13. Trace: IS-PRIME ==> T
12. Trace: IS-PRIME ==> T
11. Trace: IS-PRIME ==> T
10. Trace: IS-PRIME ==> T
9. Trace: IS-PRIME ==> T
8. Trace: IS-PRIME ==> T
7. Trace: IS-PRIME ==> T
6. Trace: IS-PRIME ==> T
5. Trace: IS-PRIME ==> T
4. Trace: IS-PRIME ==> T
3. Trace: IS-PRIME ==> T
2. Trace: IS-PRIME ==> T
1. Trace: IS-PRIME ==> T
(17)

当只需要 (isqrt 17) = 4 迭代时,您进行了 17 次迭代。

重新计算

现在编译is-prime把递归变成一个循环看看:

> (prime-factors 12345)
1. Trace: (IS-PRIME '12345)
1. Trace: IS-PRIME ==> NIL
1. Trace: (IS-PRIME '2)
1. Trace: IS-PRIME ==> T
1. Trace: (IS-PRIME '12345)
1. Trace: IS-PRIME ==> NIL
1. Trace: (IS-PRIME '3)
1. Trace: IS-PRIME ==> T
1. Trace: (IS-PRIME '4115)
1. Trace: IS-PRIME ==> NIL
1. Trace: (IS-PRIME '3)
1. Trace: IS-PRIME ==> T
1. Trace: (IS-PRIME '4115)
1. Trace: IS-PRIME ==> NIL
1. Trace: (IS-PRIME '4)
1. Trace: IS-PRIME ==> NIL
1. Trace: (IS-PRIME '4115)
1. Trace: IS-PRIME ==> NIL
1. Trace: (IS-PRIME '5)
1. Trace: IS-PRIME ==> T
1. Trace: (IS-PRIME '823)
1. Trace: IS-PRIME ==> T
(3 5 823)

您正在多次检查相同数字的素数!

额外优化

primep 可以找到除数,而不仅仅是检查素数。

优化算法

(defun compositep (n &optional (d (isqrt n)))
"If n is composite, return a divisor.
Assumes n is not divisible by anything over d."
(and (> n 1)
(> d 1)
(if (zerop (rem n d))
d
(compositep n (- d 1)))))

(defun prime-decomposition (n)
"Return the prime decomposition of n."
(let ((f (compositep n)))
(if f
(nconc (prime-decomposition (/ n f))
(prime-decomposition f))
(list n))))

请注意,最后一种优化是可能的 - memoizationcompositep:

(let ((known-composites (make-hash-table)))
(defun compositep (n &optional (d (isqrt n)))
"If n is composite, return a divisor.
Assumes n is not divisible by anything over d."
(multiple-value-bind (value found-p) (gethash n known-composites)
(if found-p
value
(setf (gethash n known-composites)
(and (> n 1)
(> d 1)
(if (zerop (rem n d))
d
(compositep n (- d 1)))))))))

或者,更好的是,质数分解:

(let ((known-decompositions (make-hash-table)))
(defun prime-decomposition (n)
"Return the prime decomposition of n."
(or (gethash n known-decompositions)
(setf (gethash n known-decompositions)
(let ((f (compositep n)))
(if f
(append (prime-decomposition (/ n f))
(prime-decomposition f))
(list n)))))))

注意使用或append instead ofnconc .

另一个有趣的优化是改变迭代compositep 从降序到升序。这应该大大加快它的速度,因为它会更早终止经常:

(let ((known-composites (make-hash-table)))
(defun compositep (n)
"If n is composite, return a divisor.
Assumes n is not divisible by anything over d."
(multiple-value-bind (value found-p) (gethash n known-composites)
(if found-p
value
(setf (gethash n known-composites)
(loop for d from 2 to (isqrt n)
when (zerop (rem n d))
return d))))))

关于optimization - 如何优化我的递归 Lisp 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58275411/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com