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] ODD_DIGITS = [1, 3, 5, 7, 9]
def reversible_numbers( def slow_reversible_numbers(
remaining_length: int, remainder: int, digits: list[int], length: int remaining_length: int, remainder: int, digits: list[int], length: int
) -> int: ) -> int:
""" """
Count the number of reversible numbers of given length. Count the number of reversible numbers of given length.
Iterate over possible digits considering parity of current sum remainder. Iterate over possible digits considering parity of current sum remainder.
>>> reversible_numbers(1, 0, [0], 1) >>> slow_reversible_numbers(1, 0, [0], 1)
0 0
>>> reversible_numbers(2, 0, [0] * 2, 2) >>> slow_reversible_numbers(2, 0, [0] * 2, 2)
20 20
>>> reversible_numbers(3, 0, [0] * 3, 3) >>> slow_reversible_numbers(3, 0, [0] * 3, 3)
100 100
""" """
if remaining_length == 0: if remaining_length == 0:
@ -51,7 +51,7 @@ def reversible_numbers(
result = 0 result = 0
for digit in range(10): for digit in range(10):
digits[length // 2] = digit digits[length // 2] = digit
result += reversible_numbers( result += slow_reversible_numbers(
0, (remainder + 2 * digit) // 10, digits, length 0, (remainder + 2 * digit) // 10, digits, length
) )
return result return result
@ -67,7 +67,7 @@ def reversible_numbers(
for digit2 in other_parity_digits: for digit2 in other_parity_digits:
digits[(length - remaining_length) // 2] = digit2 digits[(length - remaining_length) // 2] = digit2
result += reversible_numbers( result += slow_reversible_numbers(
remaining_length - 2, remaining_length - 2,
(remainder + digit1 + digit2) // 10, (remainder + digit1 + digit2) // 10,
digits, digits,
@ -76,6 +76,42 @@ def reversible_numbers(
return result 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: def solution(max_power: int = 9) -> int:
""" """
To evaluate the solution, use solution() To evaluate the solution, use solution()
@ -92,5 +128,25 @@ def solution(max_power: int = 9) -> int:
return result 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__": 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)}")