From 0f75804451cbb0b1ada4f2896131d90b88dfc4bc Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 14 Oct 2023 21:21:54 +0530 Subject: [PATCH 1/3] Performance: 80% faster Project Euler145 --- project_euler/problem_145/sol1.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py index e9fc1a199161..c69a1440e543 100644 --- a/project_euler/problem_145/sol1.py +++ b/project_euler/problem_145/sol1.py @@ -30,6 +30,10 @@ def reversible_numbers( >>> 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 + if remaining_length == 0: if digits[0] == 0 or digits[-1] == 0: return 0 @@ -94,3 +98,6 @@ def solution(max_power: int = 9) -> int: if __name__ == "__main__": print(f"{solution() = }") + + # for i in range(1, 15): + # print(f"{i}. {reversible_numbers(i, 0, [0]*i, i)}") From bf77690f36f6fe6b7a5d01dd909d35173ea7a4b8 Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 14 Oct 2023 22:32:31 +0530 Subject: [PATCH 2/3] Added timeit benchmark --- project_euler/problem_145/sol1.py | 71 ++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py index c69a1440e543..7f761d834973 100644 --- a/project_euler/problem_145/sol1.py +++ b/project_euler/problem_145/sol1.py @@ -17,23 +17,19 @@ 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 """ - # There exist no reversible 1, 5, 9, 13 (ie. 4k+1) digit numbers - if (length - 1) % 4 == 0: - return 0 - if remaining_length == 0: if digits[0] == 0 or digits[-1] == 0: return 0 @@ -55,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 @@ -71,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, @@ -80,6 +76,42 @@ def reversible_numbers( return result +def slow_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 += 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() @@ -96,8 +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)}") From c1dfb5a26f3c4afceef3b76568d15b6e502b078d Mon Sep 17 00:00:00 2001 From: Manpreet Singh <63737630+ManpreetSingh2004@users.noreply.github.com> Date: Sat, 14 Oct 2023 22:44:24 +0530 Subject: [PATCH 3/3] >>> slow_solution() doctest --- project_euler/problem_145/sol1.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project_euler/problem_145/sol1.py b/project_euler/problem_145/sol1.py index 7f761d834973..71b851178fdb 100644 --- a/project_euler/problem_145/sol1.py +++ b/project_euler/problem_145/sol1.py @@ -79,11 +79,11 @@ def slow_reversible_numbers( def slow_solution(max_power: int = 9) -> int: """ To evaluate the solution, use solution() - >>> solution(3) + >>> slow_solution(3) 120 - >>> solution(6) + >>> slow_solution(6) 18720 - >>> solution(7) + >>> slow_solution(7) 68720 """ result = 0