Project Euler – Problem 4 (and a short disclaimer)

Well, actually the disclaimer comes first. At least it is short, I promise!

Disclaimer

Problems proposed on Project Euler site are intended as exercises to practice and improve maths and coding skills; for that reason users cannot access any hints or forum discussions on the (possible) solution until they have provided the correct result. For the same reason it is also recommended not to share solutions outside the forums: too late, I just did it… So the point is: if you came here for code snippets and examples, regardless of Project Euler, you’re welcome and encouraged to provide feedback. Instead, if you came here looking for the solution to a specific problem, well… you’re reading a crime novel starting from the last page, where the culprit is revealed (it’s the butler, most of times 😉 ). You’re not doing yourself a favor and you’d better try to solve the problem by yourself, because figuring how to solve the problem is part of the fun.

That’s all, and now let’s move on to Problem 4!

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.

So… just multiply all numbers between 100 and 999… nope! I’d deal with a set of N = 900 elements, taken twice: (at most) 900 * 900 = 810000 (N2) multiplications to do and check. Instead, it suffices to multiply each number p of the set for all numbers q in the subset [100..p], trimming down the number of operation to ~50%: all operations with swapped factors (p * q = q * p) would never be executed. There will be then 900 elements to multiply for p = 100, 899 for p = 101 and so on, for a grand total of (at most) $\displaystyle \sum_{i=1}^{900} i= {i \times (i+1) \over 2} = {900 \times 901 \over 2} = 405450$

I am adding an “educated guess” (or an outright assumption, however you name it) to the first draft code: the solution will be a 6-digit number, therefore shaving off all numbers p for which p * q < 100000: unfortunately only 100 can be excluded (100 * 999 = 99900 while 101 * 999 = 100899), saving only 900 multiplications (updated count is now 405450 – 900 = 404550).

Lastly, I am doing backward loops on the elements  of the set to start with larger numbers. It worked but there is an obvious lack in the logic you might easily spot (otherwise, read further).

#!/bin/bash
# Euler problem n. 4
maxpal=0
for i in {999..100}; do
for ((j=999; j>=$i; j-- )); do prod=$((i*j))
# a number is a string of digits!
revnum=$(rev<<<$prod)
if [[ "$prod" == "$revnum" && "$prod" -gt "$maxpal" ]]; then
maxpal=$prod factors="$i x $j" fi done done echo "$factors = $maxpal is the largest palindrome number." exit 0 Let this thing run wild for a while and there you have 913 * 993 = 906609, nice and clean. BUT… Here comes the improved version, that might be named “What is seen cannot be unseen”. You’ve read my disclaimer, didn’t you? Someone had a cleverer way to solve the problem: as soon as I read it, I understood how to implement it in my code. Very nice from coding standpoint: I happily found out I was able to manage and improve my tiny code contraption. Very bad, though, from the maths/riddle standpoint: I relied on someone else’s skills to improve my semi-brute force implementation, and part of the fun was lost. However, here you are the explanation and the slightly modified code. A 6-digit palindromic number can be generically written as ABCCBA (each letter representing a digit) or, using powers of 10: $A \times 10^5 + B \times 10^4 + C \times 10^3 + C \times 10^2 + B \times 10 + A =$ 100001*A + 10010*B + 1100*C = 11*(9091*A + 910*B + 100*C) = M*N with M and N integers. That means at least one of the two integers is a multiple of 11 (say M): I can then loop only on multiples of 11 (outer loop), while having the inner loop on numbers starting from 999 and going backwards. I’ll add the “break” statement since the first palindromic number I find will of course be the largest one. #!/bin/bash maxpal=0 for i in {990..110..11}; do for ((j=999; j>=$i; j-- )); do
prod=$((i*j)) revnum=$(rev<<<$prod) # a number is a string is a number if [[ "$prod" == "$revnum" && "$prod" -gt "$maxpal" ]]; then maxpal=$prod factors="$i x$j"
break
fi
done
done
echo "$factors =$maxpal is the largest palindrome number."
exit 0

I’ll let numbers speak for themselves, when comparing the running times of the two scripts (euler4_opt is of course the optimized version I’ve just reported above): At least number of seconds happened to be the same…

That  should be all for now: you got a disclaimer, some number-crunching code and a bunch of other useless words all neatly packaged into one nice box. See ya next time and don’t cheat with Euler!

Annunci

1 Comment

I commenti sono chiusi.