使迭代和递归的代码纯粹是迭代的

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使迭代和递归的代码纯粹是迭代的相关的知识,希望对你有一定的参考价值。

我有大量的列表(大多数情况下> 1,000,000)被分成n x n网格。这些列表有一些坐标。我定义了点之间的相邻关系 - 如果它们在彼此的范围内,它们就是“邻居”并被放入“集群”。我们可以向这些集群添加单元,因此单元A是单元B的邻居,B是C的邻居,C不是A的邻居.A,B和C将在同一集群中。

鉴于该背景,我有以下代码尝试在Python 3.6中为集群分配点:

for i in range(n):
    for j in range(n):
        tile = image[i][j]
        while tile:
            cell = tile.pop()
            cluster = create_and_return_cluster(cell, image_clusters[i][j], (i, j))
            clusterise(cell, cluster, (i, j), image, n, windows, image_clusters)

def clusterise(cell, cluster, tile_index, image, n, windows, image_clusters):
    neighbouring_windows, neighbouring_indices = get_neighbouring_windows(tile_index[0], tile_index[1], n, windows)
    neighbours = get_and_remove_all_neighbours(cell, image, tile_index, neighbouring_windows, neighbouring_indices)
    if neighbours:
        for neighbour, (n_i, n_j) in neighbours:
            add_to_current_cluster(cluster, image_clusters, neighbour, (n_j, n_j))
            clusterise(neighbour, cluster, (n_i, n_j), image, n, windows, image_clusters)

由于列表的大小,我遇到了RecursionError的问题,并且已经在互联网上寻找关于尾递归的建议。问题是该算法需要从邻居分支以获取这些邻居的邻居,依此类推。可以想象,就堆栈帧而言,这相当快。

我的问题是:是否有可能使这个算法使用尾递归,或者如何使其尾递归?我知道在这种情况下cluster参数本质上是一个累加器,但考虑到列表如何收缩以及clusterise()中令人讨厌的for循环,我不知道如何成功转换为尾递归。有没有人有任何想法?理想情况下支持解释。

注意:我很清楚Python默认不优化尾递归,是的我知道其他语言优化了尾递归。我的问题是在这种情况下是否可以使用Python来完成。如果我不是必须的话,我不想改变,而且我的其他许多代码已经在Python中了。

答案

只需使用队列或堆栈来跟踪下一个要处理的邻居;以下函数迭代地执行与递归函数完全相同的工作:

from collections import deque

def clusterise(cell, cluster, tile_index, image, n, windows, image_clusters):
    to_process = deque([(cell, tile_index)])
    while to_process:
        cell, tile_index = to_process.pop()
        neighbouring_windows, neighbouring_indices = get_neighbouring_windows(tile_index[0], tile_index[1], n, windows)
        neighbours = get_and_remove_all_neighbours(cell, image, tile_index, neighbouring_windows, neighbouring_indices)
        if neighbours:
            for neighbour, (n_i, n_j) in neighbours:
                add_to_current_cluster(cluster, image_clusters, neighbour, (n_j, n_j))
                to_process.append((neighbour, (n_i, n_j))

因此,我们不是使用Python堆栈来跟踪仍然需要处理的内容,而是将不同的参数(celltile_index)移动到由函数管理的deque堆栈,而不像Python堆栈那样绑定。您还可以将其用作队列(从头开始而不是使用to_process.popleft()结束)以获得广度优先处理顺序。请注意,您的递归解决方案是深度优先处理单元格。

作为旁注:是的,您也可以使用常规Python list作为堆栈,但由于列表对象的生成和动态收缩的性质,对于堆栈,deque链表实现更有效。以这种方式在堆栈和队列之间切换更容易。见Raymond Hettinger's remarks on deque performance

以上是关于使迭代和递归的代码纯粹是迭代的的主要内容,如果未能解决你的问题,请参考以下文章

递归和迭代

二叉树的遍历(递归+迭代)

二叉树的遍历(递归+迭代)

对递归和迭代的认识

递归,三目运算,匿名函数,迭代器

C++经典算法问题:背包问题(迭代+递归算法)!含源码示例