5

I am working on an interview question which I was asked in which I was supposed to write a program to find the largest palindrome from product of two three digit numbers.

Here is the question

I came up with this brute force approach which starts from bottom.

public class LargestPalindromeQuestion {

    public static void main(String[] args) {
        int value = 0;
        for (int i = 100; i <= 999; i++) {
            for (int j = i; j <= 999; j++) {
                int value1 = i * j;
                if (isPalindrome(value1) && value < value1) {
                    value = value1;
                }
            }
        }
        System.out.println(value);
    }

    private static boolean isPalindrome(final int product) {
        int p = product;
        int reverse = 0;
        while (p != 0) {
            reverse *= 10;
            reverse += p % 10;
            p /= 10;
        }
        return reverse == product;
    }
}

They asked me what are the optimizations I can do in this program? I mentioned that we can try pruning the search space and optimize checking step for each item in the search space but then I am confuse how would I make this work in my above solution?

What are the optimizations we can do in this program? Right now it is executing 810000 steps to find the largest palindrome.

What is the least number of steps we can execute to find the largest palindrome in two three digit numbers?

john
  • 11,311
  • 40
  • 131
  • 251
  • this might help (i'm just working on the same problem, so i haven't read it yet) http://www.mathblog.dk/project-euler-problem-4/ –  Apr 29 '15 at 01:38
  • Assuming that leading zeros are not allowed to be part of the palindrome, any numbers ending in zero cannot be palindromes. So you can skip all values of i and j where i % 10 == 0 or j % 10 == 0, because multiplying by a value ending in 0 gives a result ending in 0. – samgak Apr 29 '15 at 01:59

5 Answers5

4

The program looks very good to me. I would make the i loop count from 999 down to 100, and I would only check j values that would actually give a larger product than the current maximum.

This program is able to finish surprisingly soon, at i == 952 to be precise. The mathematical reason for this is that once the solution 906609 (993 * 913) is found, it will no longer be possible to find a larger palindrome where the larger factor is less than the square-root of 906609, which is 952.160....

public static void main(String[] args) {
    int value = 0;
    for (int i = 999; i >= 100; i--) {
        int r = value / i;
        if (r >= i) {
            System.out.println("We broke at i = " + i);
            break;
        }
        for (int j = i; j > r; j--) {
            int value1 = i * j;
            if (isPalindrome(value1)) {
                value = value1;
                break;
            }
        }
    }
    System.out.println(value);
}
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • Can you explain me this line `"The mathematical reason for this is that once the solution 906609 (993 * 913) is found, it will no longer be possible to find a larger palindrome"`? I am not able to understand why we won't be able to find larger palindrome after that? – john Apr 29 '15 at 02:31
  • @david In this code, `i` means the larger of the two 3 digit numbers. If `i <= 952` then `j` will also be `<= 952` (because it is less than or equal to `i`), so `i * j` will be less than `906609`, which means there's no point carrying on. – Paul Boddington Apr 29 '15 at 02:35
1

One pretty simple way of optimizing this would be to simply start with the highest 3-digit numbers instead of the smallest. Since the solution will most likely be closer to the pair (999 , 999) than to (100 , 100).

  • This is good. If you use a `break;` statement when you do find the first palindrome, this will cut down the number of steps. You won't be able to get a good estimate, but it will cut down on the unnecessary steps for checking low numbers like < 500. – MLavrentyev Apr 29 '15 at 02:04
  • 2
    If you count down i from 999 to 100, you can break after you have found a palindrome and i * 999 is less than the highest palindrome you have found, because after that it's impossible to find a higher one. You can also break in the inner loop (but not the outer) if j is counting down once i * j is less than the highest found so far – samgak Apr 29 '15 at 02:06
  • 1
    There is no proof for this statement , it also be possible that maximum palindrome occurs at <500 so then this method will take longer time. This answer has been reverse engineered might fail for generalized case of n*n palindrome. – Vikram Bhat Apr 29 '15 at 08:22
  • @Vikram Bhat i've tried it. and it's **far** closer to (999 , 999) than (100 , 100). Apart from that there are a lot of pairs (n , n) that generate palindromes. You're right it's only a guess, but by taking a look at the numbers you might notice that it's reasonable. –  Apr 29 '15 at 12:48
  • @VikramBhat if you count upwards then you cant ever break because you never know if you are going to find a higher one. So this way is faster even if it is < 500 – samgak May 09 '15 at 08:35
  • @samgak agree with that statement but not the reason mentioned in the answer – Vikram Bhat May 12 '15 at 10:44
1

One useful mechanism to prune the search tree is to notice that the highest digit of the product a * b doesn't change often. E.g.

a = 111;   b = 112   a*b = 12432
       ;   b = 113   a*b = 12543
       ;   b = 114   a*b = 12654
       ;   ...
       ;   b = 180   a*b = 19980
       ;   b = 181   a*b = 20091 = (19980 + a)

Thus, for all the values in between (a = 111, a < b < 181), one already knows the MSB, which must equal to the LSB or (a % 10) * (b % 10) % 10 == MSB.

 e.g.
 LSB = 1    --> a % 10 == 1, b % 10 == 1
            OR  a % 10 == 3, b % 10 == 7
            OR  a % 10 == 7, b % 10 == 3
            OR  a % 10 == 9, b % 10 == 9

Most of the time there's either none, or just one candidate in set 'b' to be checked for any pair MSB, a % 10.

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57
0

The least number of steps I could get to is 375. Consider multiplying the three-digit number, a1a2a3, by the three-digit number, b1b2b3:

JavaScript code:

var modHash = new Array(10);
var iterations = 0;

for (var i=1; i<10; i++){
  modHash[i] = {0: [0]}
  for (var j=1; j<10; j++){
    iterations ++;
    var r = i * j % 10;
    if (modHash[i][r])
      modHash[i][r].push(j);
    else 
      modHash[i][r] = [j];
  }
}

var highest = 0;

function multiples(x,y,carry,mod){

  for (var i in modHash[x]){
    var m = (10 + mod - i - carry) % 10;

    if (modHash[y][m]){
      for (var j in modHash[x][i]){
        for (var k in modHash[y][m]){
          iterations ++;
          var palindrome = num(9,modHash[y][m][k],x,9,modHash[x][i][k],y);

          if (x == 3 && mod == 0){
            console.log(x + " * " + modHash[x][i][j] + " + " 
                        + y + " * " + modHash[y][m][k] + ": " + palindrome);
          }

          var str = String(palindrome);

          if (str == str.split("").reverse().join("") && palindrome > highest){
            highest = palindrome;
          }
        }
      }
    }
  }
}

function num(a1,a2,a3,b1,b2,b3){
  return (100*a1 + 10*a2 + a3)
       * (100*b1 + 10*b2 + b3);
}

var a3b3s = [[7,7,4],[9,1,0],[3,3,0]];

for (var i in a3b3s){
  for (var mod=0; mod<10; mod++){
    var x = a3b3s[i][0],
        y = a3b3s[i][1],
        carry = a3b3s[i][2];
    multiples(x,y,carry,mod);
  }
}

console.log(highest);
console.log("iterations: " + iterations);

Output:

3 * 0 + 3 * 0: 815409 
3 * 7 + 3 * 3: 907809 
3 * 4 + 3 * 6: 908109 
3 * 1 + 3 * 9: 906609 
3 * 8 + 3 * 2: 907309 
3 * 5 + 3 * 5: 908209 
3 * 2 + 3 * 8: 907309 
3 * 9 + 3 * 1: 906609 
3 * 6 + 3 * 4: 908109 
3 * 3 + 3 * 7: 907809
906609
iterations: 375
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
0

First optimize isPalindrome by seperating 6 digits as 3 digits. i.e. N = ABCDEF => a = ABC = N/1000, b = DEF = N%1000; Then reverse b and return a==reversed_b;

Secondly while producing palindromes loop through till max_palindrome_so_far/999 which is the minimum value that you would use. max_palindrome_so_far is initially equals N.

public class Solution {

    public static boolean isPalindrome(int n){
        int a = n/1000;
        int b = n%1000;
        int d, r = 0, i = 3;
        while(i-- > 0){
            d = b%10;
            r = r*10 + d;
            b = b/10;
        }
        if (a == r)
            return true;
        return false;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int t = in.nextInt();
        for(int a0 = 0; a0 < t; a0++){
            int n = in.nextInt();
            int r=0, m=n;
            int i,j;
            for(i = 999;i>=100;i--){
                for(j = 999;j>=m/999;j--){
                    if (i*j < n && i*j > 100000 && isPalindrome(i*j)){
                        r = Math.max(i*j, r);
                        m = r;
                    }
                }
            }

           // System.out.println(i + " * " + j + " = " + i*j);

            System.out.println(r);
        }
    }
}
ugur
  • 3,604
  • 3
  • 26
  • 57