Project Euler Problem 4 Statement
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.
Solution
Using a hammer
print (max(i*j for i in range(901, 1000, 2) for j in range(i, 1000, 2) if str(i*j)==str(i*j)[::-1]))
As above, we can quickly brute force a solution by checking all 3–digit factor pairs. We take advantage of the commutative property of multiplication to reduce redundant checks. For example, $997\times 909$ has the same product as $909\times 997$, so the second loop starts from the current index of the first loop.
We also call upon the principal of the self–fulfilling prophecy. This means that we don’t know for sure that the largest palindrome will start with 9, but if it did that would make for a much smaller search range. This would allow us to improve the search significantly by following two very important observations:
1. Odd Factors Only: If the palindrome starts and ends with 9, the product must result from odd factors. Thus, we can increment the loop by two.
2. Search Range: We only need to consider factors between 901 and 999, as these are the only three-digit numbers that can produce a product beginning with 9.
If this assumption fails (i.e., the largest palindrome does not starts with 9), we will extend the search, which is trivial for three-digit factors but more complex for larger factor sets.
Getting real with HackerRank
I'm going to concentrate on the HackerRank version because all this simple trickery doesn’t work when we are searching for any palindrome in a given search range, especially when several queries are made.
Now, we need to generate all palindromes from two 3–digit factors and save them to a list, sorted in ascending order.
A post "Going further with Project Euler 4" demonstrates how to speed things up significantly. It summarizes that if a six digit number is palindromic it must follow the form:
$$ 10^5 \cdot a + 10^4 \cdot b + 10^3 \cdot c + 100 \cdot c + 10 \cdot b + a\\ 100001a + 10010b + 1100c\\ 11(9091a + 910b + 100c) $$Where $a$, $b$, and $c$ are integers such that $1\le a\le 9, b\le 9, c\le 9$
This means that one of our target factors has to be divisible by 11 and allows us to increment our inside loop's search index, j, by 11 when i is not divisible by 11. Whenever i is divisible by 11 we have to increment j by 1.
The solution performs a comprehensive exploration of all possible products of three-digit numbers to identify palindromic products. Each palindromic product is stored in a list. Once all iterations are complete, the list is sorted in descending order based on the product values. This approach ensures that the results are readily available for multiple queries, allowing quick retrieval of the largest palindrome within any specified search limit.
Here is a Python idiom for searching a descending sorted list for the biggest value not meeting nor exceeding a target value, K. So, for example, K=13; lst=[120,14,13,12,9,7,5,4,3,2,1]
the result would be 12.
q = min(lst, key=lambda x:x>=K)
One subtle optimization was to check the minimum product, p, first, as the is_palindromic()
function is computationally more expensive. This is because when the first logical and
condition of an if
statement is false then all following conditions are ignored.
if (p:= i * j) >= 101101 and is_palindromic(p)
HackerRank version
HackerRank Project Euler Problem 4 runs 100 test cases and asks us to find the nearest palindrome product less than a limit, 101101 < K < 106
Python Source Code
s_palindromic = lambda n: str(n)==str(n)[::-1]
dd = sorted([i * j for i in range(101, 1000)
for j in range(121, 1000, (1 if i % 11 == 0 else 11))
if (p:= i * j) >= 101101 and is_palindromic(p)],
reverse=True)
for _ in range(int(input())):
K = int(input())
q = min(dd, key=lambda x:x>=K)
print(q)
Last Word
- There are only 279 6–digit palindromes which are products of two 3-digit numbers.
- The largest palindrome for some other cases:
# Digits factor 1 factor 2 product 4 9999 9901 99000099 5 99979 99681 9966006699 6 999999 999001 999000000999 7 9997647 9998017 99956644665999 8 99999999 99990001 9999000000009999 9 999920317 999980347 999900665566009999 10 9999986701 9999996699 99999834000043899999