问题
Problem
Given n
, find the smallest positive x
such that f(x) = n
.
f(x)
is the sum of the digit sum of the factorials of the digits of x
.
For example,
f(15)
= digit_sum(1!) + digit_sum(5!)
= digit_sum(1) + digit_sum(120)
= (1) + (1 + 2 + 0)
= 4
Breath first search can find the answer. Are there faster ways?
Breath First Search
def bfs(target, d_map):
# Track which values of f(x) have we visited
visited = set([0])
# f(x) of the current level of the search tree
todo = [0]
# Digits of x for the current level of the search tree
paths = [[0] * 10]
while True:
new_visited = set()
# Discard old visited every 9 level.
# This is because the worst case for pruning is
# nine steps of 8! finally is equivalent to 1
# step of 9!
for i in range(9):
# For holding new nodes of the next level:
new_todo = []
new_paths = []
# Visit the next level
for old_fx, old_digits in zip(todo, paths):
# Visit the 9 new digits in the order of
# large to small. This is because reaching
# the target with just one big digit is
# better than reaching the target with
# several small digits. For example,
# f(9) is same as f(888888888) but
# x = 9 is definite smaller than
# x = 888888888. Therefore, we visit
# the big digits first.
for d in [9, 6, 5, 3, 2, 1]:
new_fx = old_fx + d_map[d]
if new_fx not in visited:
# Update the set of visited values of f(x)
new_visited.add(new_fx) # for pruning visited
visited.add(new_fx)
# Make digits for the new x
new_p = old_digits.copy()
new_p[d] += 1
# Record the digits of the new x
new_todo.append(new_fx)
new_paths.append(new_p)
# Stop if we reach our target
if new_fx == target:
return new_p
# Discard record of the nodes of the previous level.
todo = new_todo
paths = new_paths
visited = new_visited # prune visited every 11 levels
def main():
# map a digit to f(digit)
d_map = {0: 1, 1: 1, 2: 2, 3: 6, 4: 6, 5: 3, 6: 9, 7: 9, 8: 9, 9: 27}
d_map = {1: 1, 2: 2, 3: 6, 5: 3, 6: 9, 9: 27}
print(bfs(1000000, d_map))
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 37037]
# That means 1 one followed by 37037 nines
main()
回答1:
def find_x(target):
t = [0, 1, 2, 5, 15, 25, 3, 13, 23, 6, 16,
26, 56, 156, 256, 36, 136, 236, 66, 166,
266, 566, 1566, 2566, 366, 1366, 2366]
r = target % 27
n_nine = (target // 27) * 3
if r != 0:
return str(t[r]) + str(9) * n_nine
else:
return str(9) * n_nine
回答2:
Here is my solution.
def digit_sum (n):
return sum([int(i) for i in str(n)])
def search (target):
factorials = [1]
digit_sums = [0]
for i in range(1, 10):
factorials.append(i * factorials[i-1])
digit_sums.append(digit_sum(factorials[i]))
last_digit = {0: None}
todo = [0]
pos = 0
while target not in last_digit:
for i in range(1, 10):
ds = digit_sums[i]
x = todo[pos] + ds
if x not in last_digit:
last_digit[x] = i
todo.append(x)
pos += 1
answer = []
x = target
while 0 < x:
answer.append(last_digit[x])
x -= digit_sums[last_digit[x]]
return int("".join([str(d) for d in reversed(answer)]))
answer = search(1000000)
print((answer[0:10], len(answer))
来源:https://stackoverflow.com/questions/64826841/speed-up-search-for-the-smallest-x-such-that-fx-target