Performance: 80% faster Project Euler 145 (#10445)

* Performance: 80% faster Project Euler145

* Added timeit benchmark

* >>> slow_solution() doctest
This commit is contained in:
Manpreet Singh 2023-10-14 23:05:01 +05:30 committed by GitHub
parent 3ba2338479
commit 1969259868
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -17,17 +17,17 @@ EVEN_DIGITS = [0, 2, 4, 6, 8]
ODD_DIGITS = [1, 3, 5, 7, 9]
def reversible_numbers(
def slow_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)
>>> slow_reversible_numbers(1, 0, [0], 1)
0
>>> reversible_numbers(2, 0, [0] * 2, 2)
>>> slow_reversible_numbers(2, 0, [0] * 2, 2)
20
>>> reversible_numbers(3, 0, [0] * 3, 3)
>>> slow_reversible_numbers(3, 0, [0] * 3, 3)
100
"""
if remaining_length == 0:
@ -51,7 +51,7 @@ def reversible_numbers(
result = 0
for digit in range(10):
digits[length // 2] = digit
result += reversible_numbers(
result += slow_reversible_numbers(
0, (remainder + 2 * digit) // 10, digits, length
)
return result
@ -67,7 +67,7 @@ def reversible_numbers(
for digit2 in other_parity_digits:
digits[(length - remaining_length) // 2] = digit2
result += reversible_numbers(
result += slow_reversible_numbers(
remaining_length - 2,
(remainder + digit1 + digit2) // 10,
digits,
@ -76,6 +76,42 @@ def reversible_numbers(
return result
def slow_solution(max_power: int = 9) -> int:
"""
To evaluate the solution, use solution()
>>> slow_solution(3)
120
>>> slow_solution(6)
18720
>>> slow_solution(7)
68720
"""
result = 0
for length in range(1, max_power + 1):
result += slow_reversible_numbers(length, 0, [0] * length, length)
return result
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
"""
# There exist no reversible 1, 5, 9, 13 (ie. 4k+1) digit numbers
if (length - 1) % 4 == 0:
return 0
return slow_reversible_numbers(length, 0, [0] * length, length)
def solution(max_power: int = 9) -> int:
"""
To evaluate the solution, use solution()
@ -92,5 +128,25 @@ def solution(max_power: int = 9) -> int:
return result
def benchmark() -> None:
"""
Benchmarks
"""
# Running performance benchmarks...
# slow_solution : 292.9300301000003
# solution : 54.90970860000016
from timeit import timeit
print("Running performance benchmarks...")
print(f"slow_solution : {timeit('slow_solution()', globals=globals(), number=10)}")
print(f"solution : {timeit('solution()', globals=globals(), number=10)}")
if __name__ == "__main__":
print(f"{solution() = }")
print(f"Solution : {solution()}")
benchmark()
# for i in range(1, 15):
# print(f"{i}. {reversible_numbers(i, 0, [0]*i, i)}")