正如 mbeckish 所提到的,您可以将其视为 BFS 遍历,与自下而上的 DP 方法相比,它具有明显更好的时间和空间复杂性。您还可以在遍历中应用各种分支定界 (B&B) 启发式算法,以便在我们之前已经看到标记值的节点处修剪树的分支。与实际的 B&B 启发式不同,这不会删除最优解,因为它不涉及对最优解可能在哪里的任何有根据的猜测。我将给出一个直观的示例,并将算法降低到 0 以更好地说明。
这是一个将 10 减少到 0 的完整操作树:
--------10---------
5 -----9----
---4--- -3- ------8------
2 -3- 1 2 --4-- 7
1 1 2 0 1 2 -3- -----6------
0 0 1 0 1 1 2 2 -3- 5
0 0 0 1 1 1 2 --4--
0 0 0 1 2 -3-
0 1 1 2
0 0 1
0
由于我们正在执行 BFS,我们实际上会像下面这样在第一个零处停止,而不是构建树的更深部分:
--------10------
5 -----9--------
---4--- -3- ------8------
2 -3- 1 2 4 7
1 1 2 0
但是,我们可以通过 B&B 启发式进一步减少分支的数量,使其看起来像这样(这对大量数字产生了巨大的影响):
--------10------
5 -----9--------
4 3 8
2 1 7
0
时间复杂度: O(log n) 空间复杂度: O(log n) (我认为)
以下是输入为 1 googol (10^100) 的 python 3 代码,在我的计算机上运行大约需要 8 秒,内存约为 350 MB。您也可以通过https://repl.it/B3Oq/76在线运行它
from collections import deque
def number_of_steps(i):
Q = deque()
seen_before = set()
steps = 0
Q.append((i, steps))
while True:
j, steps = Q.popleft()
if j == 1:
return steps
if j % 3 == 0:
branch(Q, seen_before, steps, j // 3)
if j % 2 == 0:
branch(Q, seen_before, steps, j // 2)
branch(Q, seen_before, steps, j - 1)
def branch(Q, seen_before, steps, k):
if k not in seen_before:
seen_before.add(k)
Q.append((k, steps + 1))
import time
n = 10**100
print('input:', n)
start = time.time()
steps = number_of_steps(n)
end = time.time()
print('runtime (seconds):', end - start)
print('number of steps:', steps)