Python/project_euler/problem_145/sol1.py
Maxim Smolskiy c45fb3c294
perf: Project Euler problem 145 solution 1 (#6259)
Improve solution (~30 times - from 900+ seconds to ~30 seconds)

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
2022-07-23 07:23:46 +05:30

97 lines
2.6 KiB
Python

"""
Project Euler problem 145: https://projecteuler.net/problem=145
Author: Vineet Rao, Maxim Smolskiy
Problem statement:
Some positive integers n have the property that the sum [ n + reverse(n) ]
consists entirely of odd (decimal) digits.
For instance, 36 + 63 = 99 and 409 + 904 = 1313.
We will call such numbers reversible; so 36, 63, 409, and 904 are reversible.
Leading zeroes are not allowed in either n or reverse(n).
There are 120 reversible numbers below one-thousand.
How many reversible numbers are there below one-billion (10^9)?
"""
EVEN_DIGITS = [0, 2, 4, 6, 8]
ODD_DIGITS = [1, 3, 5, 7, 9]
def reversible_numbers(
remaining_length: int, remainder: int, digits: list[int], length: int
) -> int:
"""
Count the number of reversible numbers of given length.
Iterate over possible digits considering parity of current sum remainder.
>>> reversible_numbers(1, 0, [0], 1)
0
>>> reversible_numbers(2, 0, [0] * 2, 2)
20
>>> reversible_numbers(3, 0, [0] * 3, 3)
100
"""
if remaining_length == 0:
if digits[0] == 0 or digits[-1] == 0:
return 0
for i in range(length // 2 - 1, -1, -1):
remainder += digits[i] + digits[length - i - 1]
if remainder % 2 == 0:
return 0
remainder //= 10
return 1
if remaining_length == 1:
if remainder % 2 == 0:
return 0
result = 0
for digit in range(10):
digits[length // 2] = digit
result += reversible_numbers(
0, (remainder + 2 * digit) // 10, digits, length
)
return result
result = 0
for digit1 in range(10):
digits[(length + remaining_length) // 2 - 1] = digit1
if (remainder + digit1) % 2 == 0:
other_parity_digits = ODD_DIGITS
else:
other_parity_digits = EVEN_DIGITS
for digit2 in other_parity_digits:
digits[(length - remaining_length) // 2] = digit2
result += reversible_numbers(
remaining_length - 2,
(remainder + digit1 + digit2) // 10,
digits,
length,
)
return result
def solution(max_power: int = 9) -> int:
"""
To evaluate the solution, use solution()
>>> solution(3)
120
>>> solution(6)
18720
>>> solution(7)
68720
"""
result = 0
for length in range(1, max_power + 1):
result += reversible_numbers(length, 0, [0] * length, length)
return result
if __name__ == "__main__":
print(f"{solution() = }")