90 lines
2.3 KiB
Python

#!/usr/bin/env python3
# The number 145 is well known for the property that the sum of the factorial of its digits is equal to 145:
#
# 1! + 4! + 5! = 1 + 24 + 120 = 145
#
# Perhaps less well known is 169, in that it produces the longest chain of numbers that link back to 169; it turns out that there are
# only three such loops that exist:
#
# 169 → 363601 → 1454 → 169
# 871 → 45361 → 871
# 872 → 45362 → 872
#
# It is not difficult to prove that EVERY starting number will eventually get stuck in a loop. For example,
#
# 69 → 363600 → 1454 → 169 → 363601 (→ 1454)
# 78 → 45360 → 871 → 45361 (→ 871)
# 540 → 145 (→ 145)
#
# Starting with 69 produces a chain of five non-repeating terms, but the longest non-repeating chain with a starting number
# below one million is sixty terms.
#
# How many chains, with a starting number below one million, contain exactly sixty non-repeating terms?
from math import factorial
from projecteuler import timing
N = 1000000
chains = [0] * N
def len_chain(n):
chain = [0] * 60
count = 0
finished = 0
value = n
chain[count] = value
while not finished:
tmp = 0
count = count + 1
# Generate the next number of the chain by taking
# the digits of the current value, calculating the
# factorials and adding them.
while value != 0:
tmp = tmp + factorial(value % 10)
value = value // 10
# If the chain length for the new value has already been
# calculated before, use the saved value (only chains for
# values smaller than N are saved).
if tmp < N and chains[tmp] != 0:
return count + chains[tmp]
value = tmp
# If the current value is already present in the chain,
# the chain is finished.
for i in range(count):
if chain[i] == value:
finished = 1
chain[count] = value
chains[n] = count
return count
@timing
def p074():
count = 0
# Simple brute force approach, for every number calculate
# the length of the chain.
for i in range(3, N):
if len_chain(i) == 60:
count = count + 1
print('Project Euler, Problem 74')
print(f'Answer: {count}')
if __name__ == '__main__':
p074()