Optimizing code – Part 3
The next thing we can optimize is the fact that the result of the powers calculation does not change during the same length of the numbers. In order words, 3 ** 3 does not change for the numbers 123, 345, 543 and any other number containing a 3 in the range of 100 – 999.
So we make a lookup table and put all the powers in there in a nice way. If the position is the same as the value contained within then it is really easy to calculate the next power. It is just multiply the value by the index it is located at. This process is called memoization. It is when you store or cache the results of expensive calculations and just look them up the next time you need them.
import sys
from datetime import datetime
POWERS = [0,1,2,3,4,5,6,7,8,9]
LENGTH = 1
def recalculate_powers() -> None:
global POWERS
for i,v in enumerate(POWERS):
POWERS[i] = i * v
def is_narcissistic(x: int) -> bool:
global LENGTH, POWERS
org = x
digits = []
total = 0
while x > 0:
_, x = digits.append(x % 10), x // 10
cur_length = len(digits)
if cur_length != LENGTH:
LENGTH = cur_length
recalculate_powers()
for i in digits:
total += POWERS[i]
return total == org
def find_narcissistic_numbers(desired: int) -> None:
for x in range(1,sys.maxsize):
if is_narcissistic(x):
desired -= 1
if desired == 0:
return
start = datetime.utcnow()
find_narcissistic_numbers(28)
print(datetime.utcnow() - start)
The time for this code to run on my laptop: 0:06:23.868587