【问题标题】:Making code that is both iterative and recursive purely iterative使既可迭代又可递归的代码纯迭代
【发布时间】:2018-06-14 18:02:06
【问题描述】:

我有大量列表(在大多数情况下 > 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 中了。

【问题讨论】:

  • 所以使用队列或堆栈。你这里没有尾递归。
  • 递归有多深?我没有看到锚。
  • @fafl 与它所在的集群的大小一样深——这显然是可变的。如果没有邻居,则终止。请注意,由于删除了单元格,因此两个单元格不会“竞争”到同一个邻居。
  • 尾递归不是语言特性。 Python 很好地支持尾递归。 Python 和其他一些语言 的是优化 尾递归函数。这是一个很大的区别。见What Is Tail Call Optimization?Does Python optimize tail recursion?

标签: python recursion iteration


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 2018-08-16
    • 1970-01-01
    • 2016-09-22
    • 2012-10-17
    • 2019-12-10
    • 2020-09-30
    • 2021-02-11
    • 1970-01-01
    • 2016-12-25
    相关资源
    最近更新 更多