From d5bfe6662da172d6ea3a1b06fd5af8a71febb569 Mon Sep 17 00:00:00 2001 From: GGn0 Date: Fri, 9 Oct 2020 12:16:18 +0200 Subject: [PATCH 1/6] Add solution to problem 74 --- project_euler/problem_74/__init__.py | 0 project_euler/problem_74/sol1.py | 116 +++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 project_euler/problem_74/__init__.py create mode 100644 project_euler/problem_74/sol1.py diff --git a/project_euler/problem_74/__init__.py b/project_euler/problem_74/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_74/sol1.py b/project_euler/problem_74/sol1.py new file mode 100644 index 000000000000..da273a61d06b --- /dev/null +++ b/project_euler/problem_74/sol1.py @@ -0,0 +1,116 @@ +""" projectEuler problem 74 + Starting from any positive integer number + it is possible to attain another one summing the factorial of its digits. + + Repeating this step, we can build chains of numbers. + It is not difficult to prove that EVERY starting number + will eventually get stuck in a loop. + + The request is to find how many numbers less than one million + produce a chain with exactly 60 non repeating items. + + Approach: + This solution simply consists in a loop that generates + the chains of non repeating items. + The generation of the chain stops before a repeating item + or if the size of the chain is greater then the desired one. + After generating each chain, the length is checked and the counter increases. +""" + +# The desired number of items to check for in the non repeating chains +EXACT_CHAIN_LENGTH = 60 + +# The maximum number to check in te solution +MAX_NUMBER = 1000000 + + +def factorial(a: int) -> int: + """Returns the factorial of the input a + >>> factorial(5) + 120 + + >>> factorial(6) + 720 + + >>> factorial(0) + 1 + """ + + # doctest.testmod() + + # The factorial function is not defined for negative numbers + if a < 0: + raise ValueError("Invalid negative input!", a) + + # The case of 0! is handled separately + if a == 0: + return 1 + else: + # use a temporary support variable to store the computation + temporary_computation = 1 + + while a > 0: + temporary_computation *= a + a -= 1 + + return temporary_computation + + +def factorial_sum(a: int) -> int: + """Function to perform the sum of the factorial + of all the digits in a + + >>> factorial_sum(69) + 363600 + """ + + # Prepare a variable to hold the computation + fact_sum = 0 + + """ Convert a in string to iterate on its digits + convert the digit back into an int + and add its factorial to fact_sum. + """ + for i in str(a): + fact_sum += factorial(int(i)) + + return fact_sum + + +def solution() -> int: + """Returns the number of numbers that produce + chains with exactly 60 non repeating elements. + >>> solution() + 402 + """ + + # the counter for the chains with the exact desired length + chain_counter = 0 + + for i in range(1, MAX_NUMBER + 1): + + # The temporary list will contain the elements of the chain + chain_list = [i] + + # The new element of the chain + new_chain_element = factorial_sum(chain_list[-1]) + + """ Stop computing the chain when you find a repeating item + or the length it greater then the desired one. + """ + while not (new_chain_element in chain_list) and ( + len(chain_list) <= EXACT_CHAIN_LENGTH + ): + chain_list += [new_chain_element] + + new_chain_element = factorial_sum(chain_list[-1]) + + """ If the while exited because the chain list contains the exact amount of elements + increase the counter + """ + chain_counter += len(chain_list) == EXACT_CHAIN_LENGTH + + return chain_counter + + +print(solution()) From bafe7b47b4115bef1caae4dbadf78e2577475b3e Mon Sep 17 00:00:00 2001 From: GGn0 Date: Fri, 9 Oct 2020 12:26:42 +0200 Subject: [PATCH 2/6] Fix typo --- project_euler/problem_74/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_74/sol1.py b/project_euler/problem_74/sol1.py index da273a61d06b..d05b99c46068 100644 --- a/project_euler/problem_74/sol1.py +++ b/project_euler/problem_74/sol1.py @@ -20,7 +20,7 @@ # The desired number of items to check for in the non repeating chains EXACT_CHAIN_LENGTH = 60 -# The maximum number to check in te solution +# The maximum number to check in the solution MAX_NUMBER = 1000000 From e59d8cf1fa57fbf50a29c21d1a977c58c4aee3d4 Mon Sep 17 00:00:00 2001 From: GGn0 Date: Fri, 9 Oct 2020 16:26:44 +0200 Subject: [PATCH 3/6] Edit unnecessary comment --- project_euler/problem_74/sol1.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/project_euler/problem_74/sol1.py b/project_euler/problem_74/sol1.py index d05b99c46068..ff992c113448 100644 --- a/project_euler/problem_74/sol1.py +++ b/project_euler/problem_74/sol1.py @@ -36,8 +36,6 @@ def factorial(a: int) -> int: 1 """ - # doctest.testmod() - # The factorial function is not defined for negative numbers if a < 0: raise ValueError("Invalid negative input!", a) From 4722bff0e94c24394599b1fd40f28831302d8122 Mon Sep 17 00:00:00 2001 From: GGn0 Date: Mon, 26 Oct 2020 12:00:14 +0100 Subject: [PATCH 4/6] Rename folder, add default params in solution() --- .../{problem_74 => problem_074}/__init__.py | 0 .../{problem_74 => problem_074}/sol1.py | 28 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) rename project_euler/{problem_74 => problem_074}/__init__.py (100%) rename project_euler/{problem_74 => problem_074}/sol1.py (86%) diff --git a/project_euler/problem_74/__init__.py b/project_euler/problem_074/__init__.py similarity index 100% rename from project_euler/problem_74/__init__.py rename to project_euler/problem_074/__init__.py diff --git a/project_euler/problem_74/sol1.py b/project_euler/problem_074/sol1.py similarity index 86% rename from project_euler/problem_74/sol1.py rename to project_euler/problem_074/sol1.py index ff992c113448..1491202779f7 100644 --- a/project_euler/problem_74/sol1.py +++ b/project_euler/problem_074/sol1.py @@ -1,4 +1,6 @@ -""" projectEuler problem 74 +""" + Project Euler Problem 074: https://projecteuler.net/problem=74 + Starting from any positive integer number it is possible to attain another one summing the factorial of its digits. @@ -9,7 +11,7 @@ The request is to find how many numbers less than one million produce a chain with exactly 60 non repeating items. - Approach: + Solution approach: This solution simply consists in a loop that generates the chains of non repeating items. The generation of the chain stops before a repeating item @@ -17,12 +19,6 @@ After generating each chain, the length is checked and the counter increases. """ -# The desired number of items to check for in the non repeating chains -EXACT_CHAIN_LENGTH = 60 - -# The maximum number to check in the solution -MAX_NUMBER = 1000000 - def factorial(a: int) -> int: """Returns the factorial of the input a @@ -75,17 +71,17 @@ def factorial_sum(a: int) -> int: return fact_sum -def solution() -> int: +def solution(chain_length: int = 60, number_limit: int = 1000000) -> int: """Returns the number of numbers that produce chains with exactly 60 non repeating elements. >>> solution() - 402 + ... """ # the counter for the chains with the exact desired length chain_counter = 0 - for i in range(1, MAX_NUMBER + 1): + for i in range(1, number_limit + 1): # The temporary list will contain the elements of the chain chain_list = [i] @@ -97,7 +93,7 @@ def solution() -> int: or the length it greater then the desired one. """ while not (new_chain_element in chain_list) and ( - len(chain_list) <= EXACT_CHAIN_LENGTH + len(chain_list) <= chain_length ): chain_list += [new_chain_element] @@ -106,9 +102,13 @@ def solution() -> int: """ If the while exited because the chain list contains the exact amount of elements increase the counter """ - chain_counter += len(chain_list) == EXACT_CHAIN_LENGTH + chain_counter += len(chain_list) == chain_length return chain_counter -print(solution()) +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{solution()}") From faf6dc39cfc105192adb3d0ca67ec5976d55ea66 Mon Sep 17 00:00:00 2001 From: GGn0 Date: Mon, 26 Oct 2020 12:04:13 +0100 Subject: [PATCH 5/6] Rename file to solve conflicts --- project_euler/problem_074/{sol1.py => sol2.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename project_euler/problem_074/{sol1.py => sol2.py} (100%) diff --git a/project_euler/problem_074/sol1.py b/project_euler/problem_074/sol2.py similarity index 100% rename from project_euler/problem_074/sol1.py rename to project_euler/problem_074/sol2.py From b3bce98b752a9a085d305a5e7392e04ca25e8c2a Mon Sep 17 00:00:00 2001 From: GGn0 Date: Mon, 26 Oct 2020 13:08:35 +0100 Subject: [PATCH 6/6] Fix doctests --- project_euler/problem_074/sol2.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/project_euler/problem_074/sol2.py b/project_euler/problem_074/sol2.py index 1491202779f7..0348ef1a6628 100644 --- a/project_euler/problem_074/sol2.py +++ b/project_euler/problem_074/sol2.py @@ -74,8 +74,10 @@ def factorial_sum(a: int) -> int: def solution(chain_length: int = 60, number_limit: int = 1000000) -> int: """Returns the number of numbers that produce chains with exactly 60 non repeating elements. - >>> solution() - ... + >>> solution(60,1000000) + 402 + >>> solution(15,1000000) + 17800 """ # the counter for the chains with the exact desired length