2012-11-14

Project Euler - Problem 4

Here's my solution to Project Euler problem 4:

# handy function to check if a number is palindrome
def is_palindrome(i):
    return str(i) == str(i)[::-1]

# what's the max?
max_p = 0

# multiply all numbers `i` between 100 and 998, with all between i+1 and 999
for i in xrange(100, 999):
    for j in xrange(i+1, 1000):
        p = i * j
        if is_palindrome(p) and p > max_p:
            max_p = p

print max_p

Comments are welcome.

Update: fixed as per Alex comment.

13 comments:

  1. Drop the if, please, just return the condition.

    if x:
    return True
    else:
    return False

    drives me batty. return x already!

    ReplyDelete
  2. @Alex: oh god, you're so damn right! updating

    ReplyDelete
  3. Would it be faster to check only the firesst half versus the last half instead of the whole string ?

    ReplyDelete
  4. Mines basically algorithmicly the same, just with a single list comprehension.

    https://bitbucket.org/prologic/euler/src/bc50ae139f3027d22e32f4fdf925abf8153e42a0/4.py?at=default

    cheers
    James

    ReplyDelete
  5. @Kontre: probably yes, but then you'll have to handle when len is odd (so the central element should either be ignored or included in both the sub-strings)

    @James: yep, same logic, just one note: you're comparing both A*B and B*A which for a long list would take some time

    ReplyDelete
  6. Why do you skip squares? There is no constraint that the numbers have to be different.

    ReplyDelete
  7. Rather than count up, count down. Find a max palindrome, then you can limit how small a number you need to multiply by. This saves a lot of multiplies.

    Sorry about the lack of indentation, but I can't figure out how to post code.
    --

    # handy function to check if a number is palindrome
    def is_palindrome(i):
    return str(i) == str(i)[::-1]

    # what's the max?
    max_p = 0
    min_mult = 99

    # start at 999, work your way down
    for i in xrange(999, 99, -1):
    if max_p != 0:
    min_mult = max_p/i
    if min_mult > i:
    break
    for j in xrange(i, min_mult, -1):
    p = i * j
    if is_palindrome(p) and p > max_p:
    max_p = p
    break

    print max_p

    ---

    I called yours pal0.py, mine pal1.py.


    $ time python pal0.py
    906609

    real 0m0.558s
    user 0m0.488s
    sys 0m0.031s
    J$ time python pal1.py
    906609

    real 0m0.068s
    user 0m0.033s
    sys 0m0.023s
    $

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. Or you reverse the order: you first find the potential palindromes, then you find if there is a matching product of two 3-digits numbers. As a one-liner:

    iter(pal for pal in (num for num in xrange(999 * 999, 100 * 100, -1) if str(num) == str(num)[::-1]) if any((i * j == pal) for i in xrange(999, 99, -1) for j in xrange(i, pal / i - 1, -1))).next()

    This only works well because Python allows the palindrome enumeration to be written as a generator expression, and act as a co-routine to the outside dividers search criteria.

    ReplyDelete
  10. I will avoid the nitpicks and try to make an interesting suggestion. Palindromes are endian independent, that means that if you take the bytearray and convert to int using little endian or big endian you'll get the same number.

    It could be interesting to solve a problem about numbers using it's own properties instead of converting them to strings.

    ReplyDelete
  11. two signifant improvements on sylvain's solution:
    A)
    - 999*999 = 998001, so the biggest possible palindrome is 997799
    - incidentally, 231*481 = 111111, so it's a valid candidate
    - so the solution is between 111111 and 997799, hence it has 6 digits

    - 6 digit palindromes can be generated from numbers from 999 to 100:
    999[999], 998[899], 997[799]...

    So the outer loop in sylvain's solution can be written this way:

    for pal in (int(str(num) + str(num[::-1])) for num in xrange(997,99,-1)):
    ...

    B) less significant improvement: the inner loop can start at math.floor(math.sqrt(pal)) instead of 999

    ReplyDelete
  12. @Rafael Augusto: I'm not sure how you mean to use that property, but note that a base 10 palindrome is not a base 2 palindrome (e.g. 11 (base10) = 1011 (base2))

    ReplyDelete
  13. @mpomme: Ahahaha... You are absolutely right -- I didn't give enough thoughts about the specific meaning of what is a 6 digits base 10 palindrome; your enumeration is a significant improvement.

    ReplyDelete