关于euler 4项目lisp程序的反馈

关于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

我刚开始学习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 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)))