关于euler 4项目lisp程序的反馈
我刚开始学习CommonLisp,所以我一直在研究ProjectEuler问题。这是我的解决方案(有来自的一些帮助)。你们对风格的改变有什么建议吗?以及怎样使它变得更加口齿不清关于euler 4项目lisp程序的反馈,lisp,common-lisp,Lisp,Common Lisp,我刚开始学习CommonLisp,所以我一直在研究ProjectEuler问题。这是我的解决方案(有来自的一些帮助)。你们对风格的改变有什么建议吗?以及怎样使它变得更加口齿不清 ; A palindromic number reads the same both ways. The largest palindrome made ; from the product of two 2-digit numbers is 9009 = 91 99. ; Find the largest palin
; A palindromic number reads the same both ways. The largest palindrome made
; from the product of two 2-digit numbers is 9009 = 91 99.
; Find the largest palindrome made from the product of two 3-digit numbers.
(defun num-to-list (num)
(let ((result nil))
(do ((x num (truncate x 10)))
((= x 0 ) result)
(setq result (cons (mod x 10) result)))))
(defun palindrome? (num)
(let ((x (num-to-list num)))
(equal x (reverse x))))
(defun all-n-digit-nums (n)
(loop for i from (expt 10 (1- n)) to (1- (expt 10 n)) collect i))
(defun all-products-of-n-digit-nums (n)
(let ((nums (all-n-digit-nums n)))
(loop for x in nums
appending (loop for y in nums collecting (* x y)))))
(defun all-palindromes (n)
(let ((nums (all-products-of-n-digit-nums n)))
(loop for x in nums
when (palindrome? x) collecting x)))
(defun largest-palindrome (n)
(apply 'max (all-palindromes 3)))
(print (largest-palindrome 3))
可简化为:
(push thing list)
我对代码的其他评论与其说是关于Lisp风格,不如说是关于算法。创建所有这些中间的数字列表似乎是一个糟糕的方法,只需编写嵌套循环来计算和测试数字
(defun all-palindromes (n)
(loop for i from (expt 10 (1- n)) to (1- (expt 10 n))
do (loop for j from (expt 10 (1- n)) to (1- (expt 10 n))
for num = (* i j)
when (palindrome? num)
collect num)))
但是循环
有一个可以使用的功能:最大化
。因此,您可以:
(defun largest-palindrome (n)
(loop with start = (expt 10 (1- n))
and end = (1- (expt 10 n))
for i from start to end
do (loop for j from start to end
for num = (* i j)
when (palindrome? num)
maximize num)))
下面是另一个优化:
(defun largest-palindrome (n)
(loop with start = (expt 10 (1- n))
and end = (1- (expt 10 n))
for i from start to end
do (loop for j from i to end
for num = (* i j)
when (palindrome? num)
maximize num)))
使内部循环从
i
开始,而不是从start
开始,可以避免检查M*N
和N*M
的冗余。下面的示例有点做作,但它发现回文的迭代次数比原来的方法少得多:
(defun number-to-list (n)
(loop with i = n
with result = nil
while (> i 0) do
(multiple-value-bind (a b)
(floor i 10)
(setf i a result (cons b result)))
finally (return result)))
(defun palindrome-p (n)
(loop with source = (coerce n 'vector)
for i from 0 below (floor (length source) 2) do
(when (/= (aref source i) (aref source (- (length source) i 1)))
(return))
finally (return t)))
(defun suficiently-large-palindrome-of-3 ()
;; This is a fast way to find some sufficiently large palindrome
;; that fits our requirement, but may not be the largest
(loop with left = 999
with right = 999
for maybe-palindrome = (number-to-list (* left right)) do
(cond
((palindrome-p maybe-palindrome)
(return (values left right)))
((> left 99)
(decf left))
((> right 99)
(setf left 999 right (1- right)))
(t ; unrealistic situation
; we didn't find any palindromes
; which are multiples of two 3-digit
; numbers
(return)))))
(defun largest-palindrome-of-3 ()
(multiple-value-bind (left right)
(suficiently-large-palindrome-of-3)
(loop with largest = (* left right)
for i from right downto left do
(loop for j from 100 to 999
for maybe-larger = (* i j) do
(when (and (> maybe-larger largest)
(palindrome-p (number-to-list maybe-larger)))
(setf largest maybe-larger)))
finally (return largest)))) ; 906609
它还试图以检查数字是否为回文的方式对其进行一些优化,以增加内存开销。它还使用稍长的代码将数字拆分为一个列表,但进行的除法较少(这在计算上有点昂贵)
整个想法是基于这样一个概念,即最大的回文将位于更靠近。。。最大的乘数,所以,从99*99开始,你会有很多糟糕的匹配。相反,它试图从999*999开始,首先找到一些看起来不错的回文,以一种“草率”的方式。然后,它努力改进最初的发现。巴纳尔的解决方案很好,但是有一个小的输入错误,返回的结果应该是:
(defun largest-palindrome (n)
(loop with start = (expt 10 (1- n))
and end = (1- (expt 10 n))
for i from start to end
maximize (loop for j from i to end
for num = (* i j)
when (palindrome? num)
maximize num)))
还要注意,APPLY不一定适用于大型列表。改用REDUCE。这可能属于CodeReviewKay,非常感谢!由于额外的内存开销,中间列表是一个糟糕的选择吗?我认为如果我这样划分函数,O(n)时间不会有太大变化。由
all-products-of-n-digit-nums
创建的列表是O(n^2)。在你不需要的时候创建所有这些列表似乎是错误的。从大的数字开始,而不是从小的数字开始,你可以省去昂贵的回文检查,如果产品不大于发现的最大值,那么如果你有一个X
回文数作为目前为止最好的,并且第一个索引是i
那么您只需要检查从(/xi)
到999的第二个索引。节省了很多钱。非常感谢!我不太确定如何在许多其他事情中向后迭代,所以我只做了一个非常基本的解决方案
(defun largest-palindrome (n)
(loop with start = (expt 10 (1- n))
and end = (1- (expt 10 n))
for i from start to end
maximize (loop for j from i to end
for num = (* i j)
when (palindrome? num)
maximize num)))