Jump to Table of Contents Pop Out Sidebar

Problem 4

More details about this document
Create Date:
Publish Date:
Update Date:
2023-11-06 21:15
Creator:
Emacs 29.2 (Org mode 9.6.15)
License:
This work is licensed under CC BY-SA 4.0

1. Problem

Largest palindrome product

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.

最大回文乘积

回文数是指从前往后读和从后往前读都一样的数。由两个 2 位数相乘得到的回文数中,最大的是 9009 = 91 * 99。

求最大的由两个 3 位数相乘得到的回文数。

2. Solution

首先我们需要一个判定数字是否为回文数的函数:

(defun eu4-pap (num)
  (let* ((str (number-to-string num))
	 (len (length str)))
    (named-let f ((i 0) (j (1- len)))
      (cond
       ((>= i j) t)
       ((= (aref str i) (aref str j))
	(f (1+ i) (1- j)))
       (t nil)))))

最直接的思路就是从 100 到 999 遍历找到最大的回文数:

(let ((big 0))
  (cl-loop for i from 100 to 999
	   do (cl-loop for j from 100 to 999
		       do (if (eu4-pap (* i j))
			      (if (> (* i j) big) (setq big (* i j))))))
  big)
;; 906609

当然这种方法很蠢,存在许多重复,我们可以让内层循环从外层循环值开始,这样能快一倍。考虑到我们要找的是最大数字,我们也应该从大到小循环而不是从小到大(不过这里我没有改):

(let ((big 0))
  (cl-loop for i from 100 to 999
	   do (cl-loop for j from i to 999
		       do (if (eu4-pap (* i j))
			      (if (> (* i j) big) (setq big (* i j))))))
  big))

另一种完全不同的思路是从数学出发。考虑到 3 位数相乘的最小值是 10000 ,最大值是 998001 ,而最小 6 位回文数是 111111 ,所以我们可以只考虑六位回文数。由于它是回文数,我们可以列出如下式子:

P = 100000x + 10000y + 1000z + 100z + 10y + x
  = 100001x + 10010y + 1100z
  = 11(9091x + 910y + 100z)

这也就是说该回文数是 11 的倍数,因此两个 3 位数中至少有一个是 11 的倍数。根据这个规律,我们可以写出如下代码:

;; very fast
(let ((big 0))
  (cl-loop for i from 999 downto 100
	   do (let ((b (if (zerop (% i 11)) 999 990))
		    (d (if (zerop (% i 11)) 1   11)))
		(cl-loop for j from b downto i by d
			 do (progn
			      (when (< (* i j) big) (cl-return))
			      (when (eu4-pap (* i j))
				(when (> (* i j) big)
				  (setq big (* i j)))))))
	   finally return big))