我有大量列表(在大多数情况下大于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中了。
答案 0 :(得分:4)
只需使用队列或堆栈来跟踪下一个要处理的邻居;以下函数迭代地执行与递归函数完全相同的工作:
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堆栈来跟踪仍需要处理的内容,而是将不同的参数(cell
和tile_index
)移动到由函数管理的deque
堆栈中。 ,它不像Python堆栈那样绑定。您还可以将其用作队列(从头开始而不是使用to_process.popleft()
结束)以获得广度优先处理顺序。请注意,您的递归解决方案是深度优先处理单元格。
作为旁注:是的,您也可以使用常规Python list
作为堆栈,但由于列表对象的生成和动态收缩的性质,对于堆栈 deque
链接列表实现更有效。以这种方式在堆栈和队列之间切换更容易。请参阅Raymond Hettinger's remarks on deque performance。