1616 the chains of non repeating items.
1717 The generation of the chain stops before a repeating item
1818 or if the size of the chain is greater then the desired one.
19- After generating each chain, the length is checked and the counter increases.
19+ After generating each chain, the length is checked and the
20+ counter increases.
2021"""
2122
23+ factorial_cache = {}
24+ factorial_sum_cache = {}
25+
2226
2327def factorial (a : int ) -> int :
2428 """Returns the factorial of the input a
@@ -36,18 +40,23 @@ def factorial(a: int) -> int:
3640 if a < 0 :
3741 raise ValueError ("Invalid negative input!" , a )
3842
43+ if a in factorial_cache :
44+ return factorial_cache [a ]
45+
3946 # The case of 0! is handled separately
4047 if a == 0 :
41- return 1
48+ factorial_cache [ a ] = 1
4249 else :
4350 # use a temporary support variable to store the computation
51+ temporary_number = a
4452 temporary_computation = 1
4553
46- while a > 0 :
47- temporary_computation *= a
48- a -= 1
54+ while temporary_number > 0 :
55+ temporary_computation *= temporary_number
56+ temporary_number -= 1
4957
50- return temporary_computation
58+ factorial_cache [a ] = temporary_computation
59+ return factorial_cache [a ]
5160
5261
5362def factorial_sum (a : int ) -> int :
@@ -57,7 +66,8 @@ def factorial_sum(a: int) -> int:
5766 >>> factorial_sum(69)
5867 363600
5968 """
60-
69+ if a in factorial_sum_cache :
70+ return factorial_sum_cache [a ]
6171 # Prepare a variable to hold the computation
6272 fact_sum = 0
6373
@@ -67,17 +77,15 @@ def factorial_sum(a: int) -> int:
6777 """
6878 for i in str (a ):
6979 fact_sum += factorial (int (i ))
70-
80+ factorial_sum_cache [ a ] = fact_sum
7181 return fact_sum
7282
7383
7484def solution (chain_length : int = 60 , number_limit : int = 1000000 ) -> int :
7585 """Returns the number of numbers that produce
7686 chains with exactly 60 non repeating elements.
77- >>> solution(60,1000000)
78- 402
79- >>> solution(15,1000000)
80- 17800
87+ >>> solution(10, 1000)
88+ 26
8189 """
8290
8391 # the counter for the chains with the exact desired length
@@ -86,25 +94,27 @@ def solution(chain_length: int = 60, number_limit: int = 1000000) -> int:
8694 for i in range (1 , number_limit + 1 ):
8795
8896 # The temporary list will contain the elements of the chain
89- chain_list = [i ]
97+ chain_set = {i }
98+ len_chain_set = 1
99+ last_chain_element = i
90100
91101 # The new element of the chain
92- new_chain_element = factorial_sum (chain_list [ - 1 ] )
93-
94- """ Stop computing the chain when you find a repeating item
95- or the length it greater then the desired one.
96- """
97- while not ( new_chain_element in chain_list ) and (
98- len ( chain_list ) <= chain_length
99- ):
100- chain_list += [ new_chain_element ]
101-
102- new_chain_element = factorial_sum (chain_list [ - 1 ] )
103-
104- """ If the while exited because the chain list contains the exact amount of elements
105- increase the counter
106- """
107- chain_counter += len ( chain_list ) == chain_length
102+ new_chain_element = factorial_sum (last_chain_element )
103+
104+ # Stop computing the chain when you find a repeating item
105+ # or the length it greater then the desired one.
106+
107+ while new_chain_element not in chain_set and len_chain_set <= chain_length :
108+ chain_set . add ( new_chain_element )
109+
110+ len_chain_set += 1
111+ last_chain_element = new_chain_element
112+ new_chain_element = factorial_sum (last_chain_element )
113+
114+ # If the while exited because the chain list contains the exact amount
115+ # of elements increase the counter
116+ if len_chain_set == chain_length :
117+ chain_counter += 1
108118
109119 return chain_counter
110120
0 commit comments