2021-10-16 17:08:41 +00:00
|
|
|
"""
|
|
|
|
Project Euler Problem 092: https://projecteuler.net/problem=92
|
|
|
|
Square digit chains
|
|
|
|
A number chain is created by continuously adding the square of the digits in
|
|
|
|
a number to form a new number until it has been seen before.
|
|
|
|
For example,
|
|
|
|
44 → 32 → 13 → 10 → 1 → 1
|
|
|
|
85 → 89 → 145 → 42 → 20 → 4 → 16 → 37 → 58 → 89
|
|
|
|
Therefore any chain that arrives at 1 or 89 will become stuck in an endless loop.
|
|
|
|
What is most amazing is that EVERY starting number will eventually arrive at 1 or 89.
|
|
|
|
How many starting numbers below ten million will arrive at 89?
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2022-01-30 19:29:54 +00:00
|
|
|
DIGITS_SQUARED = [digit**2 for digit in range(10)]
|
2021-10-31 10:38:28 +00:00
|
|
|
|
|
|
|
|
2021-10-16 17:08:41 +00:00
|
|
|
def next_number(number: int) -> int:
|
|
|
|
"""
|
|
|
|
Returns the next number of the chain by adding the square of each digit
|
2021-10-31 10:38:28 +00:00
|
|
|
to form a new number.
|
|
|
|
For example, if number = 12, next_number() will return 1^2 + 2^2 = 5.
|
2021-10-17 06:07:45 +00:00
|
|
|
Therefore, 5 is the next number of the chain.
|
2021-10-16 17:08:41 +00:00
|
|
|
>>> next_number(44)
|
|
|
|
32
|
|
|
|
>>> next_number(10)
|
|
|
|
1
|
|
|
|
>>> next_number(32)
|
|
|
|
13
|
|
|
|
"""
|
2021-10-17 06:07:45 +00:00
|
|
|
sum_of_digits_squared = 0
|
|
|
|
while number:
|
2021-10-31 10:38:28 +00:00
|
|
|
sum_of_digits_squared += DIGITS_SQUARED[number % 10]
|
2021-10-17 06:07:45 +00:00
|
|
|
number //= 10
|
2021-10-16 17:08:41 +00:00
|
|
|
|
2021-10-17 06:07:45 +00:00
|
|
|
return sum_of_digits_squared
|
2021-10-16 17:08:41 +00:00
|
|
|
|
|
|
|
|
2021-10-31 10:38:28 +00:00
|
|
|
CHAINS = {1: True, 58: False}
|
|
|
|
|
|
|
|
|
2021-10-16 17:08:41 +00:00
|
|
|
def chain(number: int) -> bool:
|
|
|
|
"""
|
2021-10-17 06:07:45 +00:00
|
|
|
The function generates the chain of numbers until the next number is 1 or 89.
|
|
|
|
For example, if starting number is 44, then the function generates the
|
|
|
|
following chain of numbers:
|
|
|
|
44 → 32 → 13 → 10 → 1 → 1.
|
|
|
|
Once the next number generated is 1 or 89, the function returns whether
|
2021-10-31 10:38:28 +00:00
|
|
|
or not the next number generated by next_number() is 1.
|
2021-10-16 17:08:41 +00:00
|
|
|
>>> chain(10)
|
|
|
|
True
|
|
|
|
>>> chain(58)
|
|
|
|
False
|
|
|
|
>>> chain(1)
|
|
|
|
True
|
|
|
|
"""
|
2021-10-31 10:38:28 +00:00
|
|
|
if number in CHAINS:
|
|
|
|
return CHAINS[number]
|
|
|
|
|
|
|
|
number_chain = chain(next_number(number))
|
|
|
|
CHAINS[number] = number_chain
|
2021-10-16 17:08:41 +00:00
|
|
|
|
2021-10-31 10:38:28 +00:00
|
|
|
return number_chain
|
2021-10-16 17:08:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
def solution(number: int = 10000000) -> int:
|
|
|
|
"""
|
2021-10-17 06:07:45 +00:00
|
|
|
The function returns the number of integers that end up being 89 in each chain.
|
2021-10-16 17:08:41 +00:00
|
|
|
The function accepts a range number and the function checks all the values
|
|
|
|
under value number.
|
|
|
|
|
|
|
|
>>> solution(100)
|
|
|
|
80
|
|
|
|
>>> solution(10000000)
|
|
|
|
8581146
|
|
|
|
"""
|
2021-10-17 06:07:45 +00:00
|
|
|
return sum(1 for i in range(1, number) if not chain(i))
|
2021-10-16 17:08:41 +00:00
|
|
|
|
|
|
|
|
2021-10-17 06:07:45 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
import doctest
|
2021-10-16 17:08:41 +00:00
|
|
|
|
2021-10-17 06:07:45 +00:00
|
|
|
doctest.testmod()
|
2021-10-16 17:08:41 +00:00
|
|
|
|
|
|
|
print(f"{solution() = }")
|