Search code examples
pythoneulers-number

Project Euler: Largest palindrome product of two 3-digit number


I get 580085, why is it wrong? And is there another way to check whether a number is a palindrome?

def reverse(str, aux=''):
    count = len(str)
    while count > 0:
        aux += str[count-1]
        count -= 1
    return aux

def palindrome(num):
    j = str(num)
    if j == reverse(j):
        return 1
    else:
        return 0

i = 999
found = 0
while i > 99 and not found:
    j = 999
    while j > 99 and not found:
        if palindrome(i * j):
            found = 1
            print(i * j)
        else:
            j -= 1
    if not found:
        i -= 1

My post is mostly code, but I don't have anything else to say.


Solution

  • Took some time and studied the problem. I tried taking the reversed approach (starting from palindrome numbers, and find their dividers (if any)). Using Py354 on Win10.

    code.py:

    import time
    from math import sqrt
    
    
    def reverse(str, aux=''):
        count = len(str)
        while count > 0:
            aux += str[count-1]
            count -= 1
        return aux
    
    
    def palindrome(num):
        j = str(num)
        if j == reverse(j):
            return 1
        else:
            return 0
    
    
    def question_makhfi_0():
        ret = 0
        i = 999
        found = 0
        while i > 99 and not found:
            j = 999
            while j > 99 and not found:
                if palindrome(i * j):
                    found = 1
                    ret = i * j
                else:
                    j -= 1
            if not found:
                i -= 1
        return ret, i, j
    
    
    def answer_makhfi_0():
        i = 999
        _max = 0
        while i > 99:
            j = 999
            while j > 99:
                num = i * j
                if palindrome(num):
                    if num > _max:
                        _max = num
                        factor0, factor1 = i, j
                j -= 1
            i -= 1
        return _max, factor0, factor1
    
    
    """
    def answer_makhfi__0_improved_0():
        i = j = 999
        prod = i * j
        step = 0
        while prod > 100000:
            if step % 2:
                i -= 1
            else:
                j -= 1
            prod = i * j
            prod_str = str(prod)
            if prod_str == prod_str[::-1]:
                return prod
            step += 1
    """
    
    
    def answer_cfati_0():
        pal = 999999
        while pal >= 900009:
            if pal % 10 == 9:
                pal_str = str(pal)
                if pal_str == pal_str[::-1]:
                    pal_sqrt = sqrt(pal)
                    for factor0 in range(101, int(pal_sqrt) + 1):
                        if pal % factor0 == 0:
                            factor1 = int(pal / factor0)
                            if 100 <= factor1 <= 999:
                                return pal, factor0, factor1
            pal -= 10
        #return pal
    
    
    def time_func(f, *args):
        t0 = time.time()
        res = f(*args)
        t1 = time.time()
        return t1 - t0, res
    
    
    if __name__ == "__main__":
        for func in [question_makhfi_0, answer_makhfi_0, answer_cfati_0]:
            print("\nStarting: {}".format(func.__name__))
            #print(func.__name__)
            res = time_func(func)
            print("  Time: {:.6f}\n  Result: {}".format(*res))
    

    Notes:

    • Turned your code into functions (doing all required adjustments, and omitting everything that involves performance)
    • Added an additional func that measures execution time
    • answer_makhfi_0:
      • replaced max by _max to avoid shadowing builtin names
      • added a return statement at the end (highly inefficient, anyway)
    • answer_cfati_0:
      • The code assumes there's a palindrome number in the [900009, 999999] range, that can be expressed as a 2 (3 digit) numbers product. It's fair to assume that (tests support this). However if aiming for bullet proof code, this form won't do it, I'll have to adjust it a bit (it will be a loss performance-wise)
      • It uses all kinds of mathematical/logical "shortcuts" to avoid computing uselessly (I think code could also be improved on that direction).

    Output:

    "e\Work\Dev\StackOverflow\q47999634>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.py
    
    Starting: question_makhfi_0
      Time: 0.008551
      Result: (580085, 995, 583)
    
    Starting: answer_makhfi_0
      Time: 1.457818
      Result: (906609, 993, 913)
    
    Starting: answer_cfati_0
      Time: 0.012599
      Result: (906609, 913, 993)
    

    According to the output, differences between original and new code (for one run, as times vary):

    • Largest palindrome number: 906609 - 906609
    • Time to calculate it: 1.457818 - 0.012599

    @EDIT0:

    • Thanks to your comment, I found a (critical) logical error (and some small ones) in my code: it was returning 998899 (781 * 1279). After fixing them, the performance decreased by one order of magnitude, but it's still fast
    • Modified functions to also return the factors (for checking)