16
16
the chains of non repeating items.
17
17
The generation of the chain stops before a repeating item
18
18
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.
20
21
"""
21
22
23
+ factorial_cache = {}
24
+ factorial_sum_cache = {}
25
+
22
26
23
27
def factorial (a : int ) -> int :
24
28
"""Returns the factorial of the input a
@@ -36,18 +40,23 @@ def factorial(a: int) -> int:
36
40
if a < 0 :
37
41
raise ValueError ("Invalid negative input!" , a )
38
42
43
+ if a in factorial_cache :
44
+ return factorial_cache [a ]
45
+
39
46
# The case of 0! is handled separately
40
47
if a == 0 :
41
- return 1
48
+ factorial_cache [ a ] = 1
42
49
else :
43
50
# use a temporary support variable to store the computation
51
+ temporary_number = a
44
52
temporary_computation = 1
45
53
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
49
57
50
- return temporary_computation
58
+ factorial_cache [a ] = temporary_computation
59
+ return factorial_cache [a ]
51
60
52
61
53
62
def factorial_sum (a : int ) -> int :
@@ -57,7 +66,8 @@ def factorial_sum(a: int) -> int:
57
66
>>> factorial_sum(69)
58
67
363600
59
68
"""
60
-
69
+ if a in factorial_sum_cache :
70
+ return factorial_sum_cache [a ]
61
71
# Prepare a variable to hold the computation
62
72
fact_sum = 0
63
73
@@ -67,17 +77,15 @@ def factorial_sum(a: int) -> int:
67
77
"""
68
78
for i in str (a ):
69
79
fact_sum += factorial (int (i ))
70
-
80
+ factorial_sum_cache [ a ] = fact_sum
71
81
return fact_sum
72
82
73
83
74
84
def solution (chain_length : int = 60 , number_limit : int = 1000000 ) -> int :
75
85
"""Returns the number of numbers that produce
76
86
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
81
89
"""
82
90
83
91
# 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:
86
94
for i in range (1 , number_limit + 1 ):
87
95
88
96
# 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
90
100
91
101
# 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
108
118
109
119
return chain_counter
110
120
0 commit comments