【问题标题】:how to turn a recursive algorithms into an iterative one如何将递归算法变成迭代算法
【发布时间】:2021-08-23 08:29:09
【问题描述】:

我有我写的这个算法,但我真的不知道是否可以将它转换为迭代算法。我正在尝试以立方体形状的每个节点获取邻接节点。相邻节点必须满足两个条件:

  1. 这是一个灰色节点。
  2. 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

【问题讨论】:

    标签: recursion iteration


    【解决方案1】:

    是的,将递归算法转换为迭代算法总是可能的。执行此操作的一般过程是切换到continuation passing style,应用defunctionalization,然后应用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-loop 的递归版本非常接近。每个标签对应于我们“跳回”的递归版本中的特定代码行。

    【讨论】:

    • 太棒了!只是一个小bug,elif tag is finishing_second_call下,应该是node[i] += 1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 2016-06-14
    相关资源
    最近更新 更多