3
\$\begingroup\$

I've just written a palindrome generator and would like to know what you think about it.

Especially:

  • code style
  • Is it possible to get the same result without making a special case for 1-digit palindromes?
  • efficiency

#!/usr/bin/env python

def getPalindrome():
    """
        Generator for palindromes.
        Generates palindromes, starting with 0.
        A palindrome is a number which reads the same in both directions.
    """
    # First print all one-digit palindromes
    for i in range(10):
        yield i

    length = 2
    while True:
        # Do the symmetric part
        for i in range(10**(length//2-1), 10**(length//2)):
            a = str(i)
            r = a + a[::-1]
            yield int(r)
        length += 1

        # Do the unsymmetric part
        exponent = (length-1)//2
        for prefix in range(10**(exponent-1), 10**exponent):
            prefix = str(prefix)
            for i in range(10):
                result = prefix + str(i) + prefix[::-1]
                yield int(result)
        length += 1

if __name__ == "__main__":
    palindromGenerator = getPalindrome()
    for i, palindromeNumber in enumerate(palindromGenerator):
        print("%i-th palindrome: %i" % (i, palindromeNumber))
        if i >= 500:
            break
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$
  1. I'd implement this function like this:

    from itertools import count
    
    def palindromes():
        """Generate numbers that are palindromes in base 10."""
        yield 0
        for digits in count(1):
            first = 10 ** ((digits - 1) // 2)
            for s in map(str, range(first, 10 * first)):
                yield int(s + s[-(digits % 2)-1::-1])
    

    (In Python 2, you should replace range with xrange and map with itertools.imap.)

    The insight is that instead of building an odd-length palindrome from the parts prefix + middle + xiferp, we can build it as prefix + iferp: that is, with the middle digit being supplied by the last digit of the prefix.

  2. In your main loop, you arrange to generate the first 501 palindromes:

    for i, palindromeNumber in enumerate(palindromGenerator):
        ...
        if i >= 500:
            break
    

    but it would be simpler to use itertools.islice:

    for i, palindrome in islice(enumerate(palindromeGenerator), 501):
        ...
    
  3. As for your question about efficiency, it is impossible to answer without knowing the purpose of this code. If all you are going to do is to print the first 501 palindromes, then your code is fine: the runtime will be dominated by printing the output.

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.