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

时间:2012-12-22 21:56:12

标签: lisp common-lisp

我刚开始学习常见的lisp,所以我一直在研究项目的euler问题。这是我的解决方案(在https://github.com/qlkzy/project-euler-cl的帮助下)。你们对风格的变化有什么建议吗?有什么建议让它变得更加流行?

; 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))

3 个答案:

答案 0 :(得分:1)

Barnar的解决方案非常棒,但只有一个小错字,要返回应该是的结果:

(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)))

答案 1 :(得分:0)

(setq list (cons thing list))

可以简化为:

(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)))

LOOP有一个您可以使用的功能:MAXIMIZE。因此,您可以:

,而不是使用COLLECT收集列表中的所有palindrom
(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*NN*M的冗余。

答案 2 :(得分:0)

下面的例子有点人为,但它比原始方法的迭代次数少得多:

(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开始,并首先找到一些看起来很好的回文,这样做是以“马虎”的方式进行的。然后它努力改进最初的发现。