It is worth pointing out that if you are trying to improve the performance of your code, you can focus your code on exactly the problem you want to solve.
Standardize your timing
If I told you a function ran in 400 ms on my machine would you prefer it over your 311 ms version? What if my machine is an OOOOOOLD 32-bit laptop?
Always provide a timing framework. That way I can check the performance of my code versus your code, using your framework. I've included one below based on the performance
module.
Focus on the problem
You aren't looking for "any palindrome" or "any number that is a palindrome." You are looking for "any number that is the product of two 3-digit numbers which is a palindrome." Take advantage of that!
You know that the smallest 3-digit number is 100, and the largest is 999. So the smallest product will be 10,000 and the largest product will be <1,000,000. Thus, the palindrome in question is either a 5-digit number or a 6-digit number. Write your code to that specification:
const is_palindrome = num => { if (num >= 100000) { let t0 = num % 10; let t5 = num / 100000 | 0; if (t5 != t0) return false; let t1 = num / 10 % 10 | 0; let t4 = num / 10000 % 10 | 0; if (t4 != t1) return false; let t2 = num / 100 % 10 | 0; let t3 = num / 1000 % 10 | 0; if (t3 != t2) return false; } else { let t0 = num % 10; let t4 = num / 10000 | 0; if (t4 != t0) return false; let t1 = num / 10 % 10 | 0; let t3 = num / 1000 % 10 | 0; if (t3 != t1) return false; } return true; }
Be lazy
As Jorge F. points out, you are looking for the largest palindromic number. So why start at the small end of the range? For some product X * Y, you know that if Y1 > Y2, then X * Y1 > X * Y2. So always try the higher number first! And if you find a palindrome, then you're done with all the other Y2's that might be smaller than your Y1, so move on to the next X.
for (var x = 999; x >= 100; --x) for (var y = 999; y >= x; --y) {
Laziness: fail early
Also as Jorge shows, you can quit if the product ever gets smaller than your present largest value:
for (var x = 999; x >= 100; --x) { if (x * 999 <= largest) break; for (var y = 999; y >= x; --y) { if (x * y <= largest) break;
Explore the optimizer!
Finally, one thing worth considering is that you don't really know how the particular optimizer your javascript engine is running will work. So it's worth trying to manually perform some improvements to see if it helps. One improvement you could perform is to notice that you are always multiplying by one less number:
x * 999 x * 998 x * 997
Which is really just the same as subtracting x
from the previous result:
product = x * 999 product -= x product -= x
Why not make that your inner loop and see if performance improves? FWIW, on my machine, it made a small difference (sometimes -2, sometimes -1 from _jf
version). I'm not sure if that's real, or just a result of getting things into the cache. Let me know what your timings look like.
Results: Function OK? Timing(ms) Eagle Y 2095 Jorge Fernández Y 17 Blindman67 Y 10 aghast_bm Y 5 aghast_jf Y 4 aghast_nomult Y 2
function profile(name, fn) { let t0 = performance.now(); let result = fn(); // Run some more, for more timing! fn(); fn(); fn(); let t1 = performance.now(); return [name, result, (t1-t0)]; } function p4_eagle(){ let product = 1; let largest = 1; for(let i = 100; i<1000; i++){ for(let j = i; j<1000; j++){ product = i*j; if(("" + product) == ("" + product).split("").reverse().join("")){ largest = Math.max(largest, product);} } } return largest; } function p4_jorge_fernandez(){ let product = 1; let largest = 1; for(let i = 999; i>=100; i--){ for(let j = 999; j>=i && i*j>largest; j--){ product = i*j; if(("" + product) == ("" + product).split("").reverse().join("")){ largest = Math.max(largest, product);} } } return largest; } function p4_blindman67(){ const minNum = 100, range = 899; const isPalindrome = num => { var top = Math.pow(10, Math.log10(num) | 0), bot = 1; while (top >= bot) { if ((num / top % 10 | 0) !== (num / bot % 10 | 0)) { return false } top /= 10; bot *= 10; } return true; } var i = range, max = minNum * minNum, j, iVal; while (i--) { iVal = i + minNum; j = i + 1; if (iVal * (j - 1 + minNum) < max) { break } while (j--) { const product = iVal * (j + minNum); if (product <= max) { break } if (isPalindrome(product)) { max = Math.max(max, product) } } } return max; } function p4_aghast_bm(){ const is_palindrome = num => { if (num >= 100000) { let t0 = num % 10; let t5 = num / 100000 | 0; if (t5 != t0) return false; let t1 = num / 10 % 10 | 0; let t4 = num / 10000 % 10 | 0; if (t4 != t1) return false; let t2 = num / 100 % 10 | 0; let t3 = num / 1000 % 10 | 0; if (t3 != t2) return false; } else { let t0 = num % 10; let t4 = num / 10000 | 0; if (t4 != t0) return false; let t1 = num / 10 % 10 | 0; let t3 = num / 1000 % 10 | 0; if (t3 != t1) return false; } return true; } const minNum = 100, range = 899; var i = range, max = minNum * minNum, j, iVal; while (i--) { iVal = i + minNum; j = i + 1; if (iVal * (j - 1 + minNum) < max) { break } while (j--) { const product = iVal * (j + minNum); if (product <= max) { break } if (is_palindrome(product)) { max = Math.max(max, product) } } } return max; } function p4_aghast_jf(){ const is_palindrome = num => { if (num >= 100000) { let t0 = num % 10; let t5 = num / 100000 | 0; if (t5 != t0) return false; let t1 = num / 10 % 10 | 0; let t4 = num / 10000 % 10 | 0; if (t4 != t1) return false; let t2 = num / 100 % 10 | 0; let t3 = num / 1000 % 10 | 0; if (t3 != t2) return false; } else { let t0 = num % 10; let t4 = num / 10000 | 0; if (t4 != t0) return false; let t1 = num / 10 % 10 | 0; let t3 = num / 1000 % 10 | 0; if (t3 != t1) return false; } return true; } let product = 1; let largest = 1; for(let i = 999; i>=100; i--){ for(let j = 999; j>=i && i*j>largest; j--){ product = i*j; if (is_palindrome(product)) { largest = Math.max(largest, product); } } } return largest; } function p4_aghast_nomult(){ const is_palindrome = num => { if (num >= 100000) { let t0 = num % 10; let t5 = num / 100000 | 0; if (t5 != t0) return false; let t1 = num / 10 % 10 | 0; let t4 = num / 10000 % 10 | 0; if (t4 != t1) return false; let t2 = num / 100 % 10 | 0; let t3 = num / 1000 % 10 | 0; if (t3 != t2) return false; } else { let t0 = num % 10; let t4 = num / 10000 | 0; if (t4 != t0) return false; let t1 = num / 10 % 10 | 0; let t3 = num / 1000 % 10 | 0; if (t3 != t1) return false; } return true; } let largest = 1; for (let i = 999; i >= 100; --i) { if (i * 999 <= largest) break; for (let product = i * 999; product >= i * i; product -= i) { if (product <= largest) break; if (is_palindrome(product)) { largest = product; break; } } } return largest; } //////////////////////////////////////////////////////////// var details = [ ['Eagle', p4_eagle], ['Jorge Fernández', p4_jorge_fernandez], ['Blindman67', p4_blindman67], ['aghast_bm', p4_aghast_bm], ['aghast_jf', p4_aghast_jf], ['aghast_nomult', p4_aghast_nomult], ]; var results = []; for (deets of details) { let [name, fn] = deets; results.push(profile(name, fn)); } let correct = results[0][1]; const tab = "\t"; const pad = " ".repeat(20); var msg = "Results:\n" + ("Function" + pad).slice(0, 20) + "OK?" + tab + "Timing(ms)\n"; for (res of results) { let [name, answer, time] = res; msg += (name + pad).slice(0, 20) + (correct == answer ? "Y" : "No!") + tab + time + "\n"; } //alert(msg); console.log(msg);