patternpythonMinor
The Palindromic Odometer Puzzler: A Programmatic Solution
Viewed 0 times
theodometerprogrammaticsolutionpuzzlerpalindromic
Problem
That is a programmatic solution to a puzzler from Car Talk, its summary is:
"I noticed that the last 4 digits were palindromic. I drove a mile,
and the last 5 were palindromic. I drove another mile and the middle 4
were palindromic, and the ends were not involved. And then one mile
later, all 6 digits were palindromic."
Notes:
"I noticed that the last 4 digits were palindromic. I drove a mile,
and the last 5 were palindromic. I drove another mile and the middle 4
were palindromic, and the ends were not involved. And then one mile
later, all 6 digits were palindromic."
def is_palindrome(string):
"""Finds if a string is a plaindrome or not.
string: str
returns: bool
"""
return string == string[::-1]
def find_puzzle_number():
"""Finds the number which was first seen.
prints: int
"""
guess = 100000
# the odometer shows only 6 numbers,
# therefore 999999 is the last number to test.
while guess < 1000000:
if is_palindrome(str(guess)[2:]): # last 4 numbers.
if is_palindrome(str(guess + 1)[1:]): # last 5 numbers.
if is_palindrome(str(guess + 2)[1:5]): # middle 4 numbers.
if is_palindrome(str(guess + 3)):
print(guess)
guess = guess + 1
print('These are the possible odometer readings: ')
find_puzzle_number()Notes:
- I'm a hobbyist and a beginner.
- I don't only want to optimize the code, but also my skills and style. Therefore any notes, advice and suggestions aren't only welcomed, but also encouraged!
Solution
The documentation
is pretty obvious. I would skip it. The rest of the documentation can be more idiomatically written as
(note
I would change the documentation to specify the rules the output satisfy.
The
Then the
Now note something bad:
Further, you don't know that
If you care about speed, PyPy makes the code run in about a tenth the time.
Note that there are simple optimizations you can apply. For instance, you can easily iterate over only those values for which
This makes the code basically instantaneous, so if optimizations were pointless to apply before they're most certainly pointless to apply now.
string: str
returns: boolis pretty obvious. I would skip it. The rest of the documentation can be more idiomatically written as
"""Return whether input sequence is a palindrome."""(note
palindrome, not plaindrome). I removed the term string because many sequence types can be palindromic, like [1, 2, 3, 2, 1].find_puzzle_number should yield, not print, its values.I would change the documentation to specify the rules the output satisfy.
def find_puzzle_number():
"""Generates 6-digit integers, x, where all of
* the last 4 digits of x,
* the last 5 digits of x+1,
* the middle 4 digits of x+2, and
* all 6 digits of x+3
are palindromes.
"""
guess = 100000
# the odometer shows only 6 numbers,
# therefore 999999 is the last number to test.
while guess < 1000000:
if is_palindrome(str(guess)[2:]): # last 4 numbers.
if is_palindrome(str(guess + 1)[1:]): # last 5 numbers.
if is_palindrome(str(guess + 2)[1:5]): # middle 4 numbers.
if is_palindrome(str(guess + 3)):
yield guess
guess = guess + 1The
while should become a for# 6 digit numbers
for guess in range(10**5, 10**6):Then the
if chain of is_palindromes can be simplified:valid = (
is_palindrome(str(guess + 0)[2:]) and
is_palindrome(str(guess + 1)[1:]) and
is_palindrome(str(guess + 2)[1:5]) and
is_palindrome(str(guess + 3))
)
if valid:
yield guessNow note something bad:
guess + 3, can overflow. Either you want to clamp the code so that guess + 3 < 10**6, or you want to perform modular arithmetic.Further, you don't know that
10**5 is a valid lower bound: 000000 is a valid reading. You can use format(guess, "06") to pad like this.If you care about speed, PyPy makes the code run in about a tenth the time.
def is_palindrome(seq):
"""Return whether input sequence is a palindrome."""
return seq == seq[::-1]
def find_puzzle_number():
"""Generates 6-digit integers, x, where all of
* the last 4 digits of x,
* the last 5 digits of x+1,
* the middle 4 digits of x+2, and
* all 6 digits of x+3
are palindromes.
"""
def as_reading(x):
return format(x % 10**6, "06")
# 6 digit numbers
for guess in range(10**6):
valid = (
is_palindrome(as_reading(guess + 0)[2:]) and
is_palindrome(as_reading(guess + 1)[1:]) and
is_palindrome(as_reading(guess + 2)[1:5]) and
is_palindrome(as_reading(guess + 3))
)
if valid:
yield guess
print('These are the possible odometer readings: ')
for solution in find_puzzle_number():
print(solution)Note that there are simple optimizations you can apply. For instance, you can easily iterate over only those values for which
guess + 3 is a palindrome by construction - this reduces the values to iterate over from \$10^6\$ to \$10^3\$.for guess_digits in range(10**3):
# guess + 3 is a palindrome
# NOTE: relies on correct handling of wrapping
guess = guess_digits + int(format(guess_digits, "06")[::-1]) - 3This makes the code basically instantaneous, so if optimizations were pointless to apply before they're most certainly pointless to apply now.
Code Snippets
string: str
returns: bool"""Return whether input sequence is a palindrome."""def find_puzzle_number():
"""Generates 6-digit integers, x, where all of
* the last 4 digits of x,
* the last 5 digits of x+1,
* the middle 4 digits of x+2, and
* all 6 digits of x+3
are palindromes.
"""
guess = 100000
# the odometer shows only 6 numbers,
# therefore 999999 is the last number to test.
while guess < 1000000:
if is_palindrome(str(guess)[2:]): # last 4 numbers.
if is_palindrome(str(guess + 1)[1:]): # last 5 numbers.
if is_palindrome(str(guess + 2)[1:5]): # middle 4 numbers.
if is_palindrome(str(guess + 3)):
yield guess
guess = guess + 1# 6 digit numbers
for guess in range(10**5, 10**6):valid = (
is_palindrome(str(guess + 0)[2:]) and
is_palindrome(str(guess + 1)[1:]) and
is_palindrome(str(guess + 2)[1:5]) and
is_palindrome(str(guess + 3))
)
if valid:
yield guessContext
StackExchange Code Review Q#136794, answer score: 7
Revisions (0)
No revisions yet.