如何将递归算法变成迭代算法
how to turn a recursive algorithms into an iterative one
我有自己写的算法,但我真的不知道是否可以将其转换为迭代算法。我正在尝试为立方体形状的每个节点获取邻接节点。相邻节点必须满足两个条件:
- 是灰色节点
- 它在
distance
的半径范围内
def find_continumm(seed, node, row, gray, xyz, distance):
"""
seed: the nodes we want to find the adjacent nodes for.
node: the candidate nodes to be in the adjacency.
row: save the nodes that are adjacent.
gray: boolean array that tells if a node is a gray or not.
xyz: the 3 dim of the shape.
distance: the radius
"""
node_ravel = np.ravel_multi_index(node, xyz)
if node_ravel in row or ~gray[node_ravel] or math.dist(node, seed) > distance:
return
row.add(node_ravel)
if node[0] < xyz[0]:
node[0] = node[0] + 1
find_continumm(seed, node, row, gray, xyz, distance)
node[0] = node[0] - 1
if node[0] > 0:
node[0] = node[0] - 1
find_continumm(seed, node, row, gray, xyz, distance)
node[0] = node[0] + 1
if node[1] < xyz[1]:
node[1] = node[1] + 1
find_continumm(seed, node, row, gray, xyz, distance)
node[1] = node[1] - 1
if node[1] > 0:
node[1] = node[1] - 1
find_continumm(seed, node, row, gray, xyz, distance)
node[1] = node[1] + 1
if node[2] < xyz[2]:
node[2] = node[2] + 1
find_continumm(seed, node, row, gray, xyz, distance)
node[2] = node[2] - 1
if node[2] > 0:
node[2] = node[2] - 1
find_continumm(seed, node, row, gray, xyz, distance)
node[2] = node[2] + 1
是的,总是可以将递归算法变成迭代算法。这样做的一般过程是切换到 continuation passing style, applying defunctionalization, and then applying tail-call elimination。这三个变换的组合,会把一个递归函数变成一个迭代函数,可能需要栈。
在将其应用到您的代码之前,我将按如下方式简要重写您的代码:
def find_continumm(seed, node, row, gray, xyz, distance):
def helper():
node_ravel = np.ravel_multi_index(node, xyz)
if node_ravel in row or ~gray[node_ravel] or math.dist(node, seed) > distance:
return
row.add(node_ravel)
for i in range(3):
if node[i] < xyz[i]:
node[i] += 1
helper()
node[i] -= 1
if node[i] > 0:
node[i] -= 1
helper()
node[i] += 1
helper()
你可以自己看看,这相当于你的代码版本。我将做最后一次重写以使用 while
-loop 而不是 for
-loop:
def find_continumm(seed, node, row, gray, xyz, distance):
def helper():
node_ravel = np.ravel_multi_index(node, xyz)
if node_ravel in row or ~gray[node_ravel] or math.dist(node, seed) > distance:
return
row.add(node_ravel)
i = 0
while i < 3:
if node[i] < xyz[i]:
node[i] += 1
helper()
node[i] -= 1
if node[i] > 0:
node[i] -= 1
helper()
node[i] += 1
i += 1
helper()
这极大地简化了代码并使将其转换为迭代版本变得更加简单。
生成的迭代版本是:
beginning = 0
entering_loop = 1
finishing_first_call = 2
enter_second_if = 3
finishing_second_call = 4
increment_i = 5
# the actual values of the above variables don't matter
# so long as they're different
def find_continumm(seed, node, row, gray, xyz, distance):
stack = []
add_to_stack = lambda tag, data : stack.append((tag, data))
back_to_beginning = lambda : add_to_stack(beginning, None)
back_to_beginning()
while stack:
tag, i = stack.pop()
if tag is beginning:
node_ravel = np.ravel_multi_index(node, xyz)
if node_ravel in row or ~gray[node_ravel] or math.dist(node, seed) > distance:
pass
else:
row.add(node_ravel)
add_to_stack(entering_loop, 0)
elif tag is entering_loop:
if i < 3:
if node[i] < xyz[i]:
node[i] += 1
add_to_stack(finishing_first_call, i)
back_to_beginning()
else:
add_to_stack(enter_second_if, i)
elif tag is finishing_first_call:
node[i] -= 1
add_to_stack(enter_second_if, i)
elif tag is enter_second_if:
if node[i] > 0:
node[i] += 1
add_to_stack(finishing_second_call, i)
back_to_beginning()
else:
add_to_stack(increment_i, i)
elif tag is finishing_second_call:
node[i] -= 1
add_to_stack(increment_i, i)
elif tag is increment_i:
add_to_stack(entering_loop, i + 1)
如果您看一下迭代版本,您会发现它与带有 while
循环的递归版本非常接近。每个标签对应于我们“跳回”的这个递归版本中的特定代码行。
我有自己写的算法,但我真的不知道是否可以将其转换为迭代算法。我正在尝试为立方体形状的每个节点获取邻接节点。相邻节点必须满足两个条件:
- 是灰色节点
- 它在
distance
的半径范围内
def find_continumm(seed, node, row, gray, xyz, distance):
"""
seed: the nodes we want to find the adjacent nodes for.
node: the candidate nodes to be in the adjacency.
row: save the nodes that are adjacent.
gray: boolean array that tells if a node is a gray or not.
xyz: the 3 dim of the shape.
distance: the radius
"""
node_ravel = np.ravel_multi_index(node, xyz)
if node_ravel in row or ~gray[node_ravel] or math.dist(node, seed) > distance:
return
row.add(node_ravel)
if node[0] < xyz[0]:
node[0] = node[0] + 1
find_continumm(seed, node, row, gray, xyz, distance)
node[0] = node[0] - 1
if node[0] > 0:
node[0] = node[0] - 1
find_continumm(seed, node, row, gray, xyz, distance)
node[0] = node[0] + 1
if node[1] < xyz[1]:
node[1] = node[1] + 1
find_continumm(seed, node, row, gray, xyz, distance)
node[1] = node[1] - 1
if node[1] > 0:
node[1] = node[1] - 1
find_continumm(seed, node, row, gray, xyz, distance)
node[1] = node[1] + 1
if node[2] < xyz[2]:
node[2] = node[2] + 1
find_continumm(seed, node, row, gray, xyz, distance)
node[2] = node[2] - 1
if node[2] > 0:
node[2] = node[2] - 1
find_continumm(seed, node, row, gray, xyz, distance)
node[2] = node[2] + 1
是的,总是可以将递归算法变成迭代算法。这样做的一般过程是切换到 continuation passing style, applying defunctionalization, and then applying tail-call elimination。这三个变换的组合,会把一个递归函数变成一个迭代函数,可能需要栈。
在将其应用到您的代码之前,我将按如下方式简要重写您的代码:
def find_continumm(seed, node, row, gray, xyz, distance):
def helper():
node_ravel = np.ravel_multi_index(node, xyz)
if node_ravel in row or ~gray[node_ravel] or math.dist(node, seed) > distance:
return
row.add(node_ravel)
for i in range(3):
if node[i] < xyz[i]:
node[i] += 1
helper()
node[i] -= 1
if node[i] > 0:
node[i] -= 1
helper()
node[i] += 1
helper()
你可以自己看看,这相当于你的代码版本。我将做最后一次重写以使用 while
-loop 而不是 for
-loop:
def find_continumm(seed, node, row, gray, xyz, distance):
def helper():
node_ravel = np.ravel_multi_index(node, xyz)
if node_ravel in row or ~gray[node_ravel] or math.dist(node, seed) > distance:
return
row.add(node_ravel)
i = 0
while i < 3:
if node[i] < xyz[i]:
node[i] += 1
helper()
node[i] -= 1
if node[i] > 0:
node[i] -= 1
helper()
node[i] += 1
i += 1
helper()
这极大地简化了代码并使将其转换为迭代版本变得更加简单。
生成的迭代版本是:
beginning = 0
entering_loop = 1
finishing_first_call = 2
enter_second_if = 3
finishing_second_call = 4
increment_i = 5
# the actual values of the above variables don't matter
# so long as they're different
def find_continumm(seed, node, row, gray, xyz, distance):
stack = []
add_to_stack = lambda tag, data : stack.append((tag, data))
back_to_beginning = lambda : add_to_stack(beginning, None)
back_to_beginning()
while stack:
tag, i = stack.pop()
if tag is beginning:
node_ravel = np.ravel_multi_index(node, xyz)
if node_ravel in row or ~gray[node_ravel] or math.dist(node, seed) > distance:
pass
else:
row.add(node_ravel)
add_to_stack(entering_loop, 0)
elif tag is entering_loop:
if i < 3:
if node[i] < xyz[i]:
node[i] += 1
add_to_stack(finishing_first_call, i)
back_to_beginning()
else:
add_to_stack(enter_second_if, i)
elif tag is finishing_first_call:
node[i] -= 1
add_to_stack(enter_second_if, i)
elif tag is enter_second_if:
if node[i] > 0:
node[i] += 1
add_to_stack(finishing_second_call, i)
back_to_beginning()
else:
add_to_stack(increment_i, i)
elif tag is finishing_second_call:
node[i] -= 1
add_to_stack(increment_i, i)
elif tag is increment_i:
add_to_stack(entering_loop, i + 1)
如果您看一下迭代版本,您会发现它与带有 while
循环的递归版本非常接近。每个标签对应于我们“跳回”的这个递归版本中的特定代码行。