递归:如何避免 Python set changed set 在迭代 RuntimeError 期间
Posted
技术标签:
【中文标题】递归:如何避免 Python set changed set 在迭代 RuntimeError 期间【英文标题】:Recursion: how to avoid Python set changed set during iteration RuntimeError 【发布时间】:2014-05-15 19:37:55 【问题描述】:背景及问题描述:
我有一些代码可以解决图形着色问题(广义上定义为将“颜色”分配给无向图的问题,确保没有两个由边连接的顶点具有相同的颜色)。我正在尝试使用约束传播来实现解决方案,以提高标准递归回溯算法的效率,但遇到以下错误:
File "C:\Users\danisg\Desktop\coloring\Solver.py",
line 99, in solve
for color in self.domains[var]:
RuntimeError: Set changed size during iteration
在这里,对于每个顶点,我为该特定顶点保留了一个 set
可能的特定值:
self.domains = var: set(self.colors) for var in self.vars
完成分配后,我将此约束传播到相邻域,以限制搜索空间:
for key in node.neighbors: # list of keys corresponding to adjacent vertices
if color in self.domains[key]: # remove now to prune possible choices
self.domains[key].remove(color)
这不是引发实际错误的地方(在我的代码中,我在try-except
块中指出了问题所在),但可能是问题的根源。
我的问题:
我是否有正确的想法,如果没有正确的实施?更重要的是,我该如何解决这个问题?另外,是否有必要保留一个单独的domains
字典?或者我们可以让domain
成为图中每个节点的属性吗?
我的代码:
这是调用此代码的 solve
函数:
def solve(self):
uncolored = [var for var in self.vars if self.map[var].color == None]
if len(uncolored) == 0:
return True
var = min(uncolored, key = lambda x: len(self.domains[var]))
node = self.map[var]
old = var: set(self.domains[var]) for var in self.vars
for color in self.domains[var]:
if not self._valid(var, color):
continue
self.map[var].color = color
for key in node.neighbors:
if color in self.domains[key]:
self.domains[key].remove(color)
try:
if self.solve():
return True
except:
print('happening now')
self.map[var].color = None
self.domains = old
return False
我的实现使用Node
对象:
class Solver:
class Node:
def __init__(self, var, neighbors, color = None, domain = set()):
self.var = var
self.neighbors = neighbors
self.color = color
self.domain = domain
def __str__(self):
return str((self.var, self.color))
def __init__(self, graph, K):
self.vars = sorted( graph.keys(), key = lambda x: len(graph[x]), reverse = True ) # sort by number of links; start with most constrained
self.colors = range(K)
self.map = var: self.Node(var, graph[var]) for var in self.vars
self.domains = var: set(self.colors) for var in self.vars
这里有另外两个有用/有用的功能:
def validate(self):
for var in self.vars:
node = self.map[var]
for key in node.neighbors:
if node.color == self.map[key].color:
return False
return True
def _valid(self, var, color):
node = self.map[var]
for key in node.neighbors:
if self.map[key].color == None:
continue
if self.map[key].color == color:
return False
return True
代码失败的数据和示例:
我使用的示例图可以在here找到。
读取数据的函数:
def read_and_make_graph(input_data):
lines = input_data.split('\n')
first_line = lines[0].split()
node_count = int(first_line[0])
edge_count = int(first_line[1])
graph =
for i in range(1, edge_count + 1):
line = lines[i]
parts = line.split()
node, edge = int(parts[0]), int(parts[1])
if node in graph:
graph[node].add(edge)
if edge in graph:
graph[edge].add(node)
if node not in graph:
graph[node] = edge
if edge not in graph:
graph[edge] = node
return graph
应该这样调用:
file_location = 'C:\\Users\\danisg\\Desktop\\coloring\\data\\gc_50_3'
input_data_file = open(file_location, 'r')
input_data = ''.join(input_data_file.readlines())
input_data_file.close()
graph = read_and_make_graph(input_data)
solver = Solver(graph, 6) # a 6 coloring IS possible
print(solver.solve()) # True if we solved; False if we didn't
【问题讨论】:
您的堆栈跟踪提到了_solve
和for color in domain:
,但您发布的代码中的任何地方都没有这些。如果没有可证明问题的可运行测试用例,很难给出比“在迭代时不要从集合中remove
”更具体的建议
@Kevin:这些名称用于更长的代码版本(我删除了过多的代码以尝试制作最简单的问题实例)。我提供的数据是产生错误的测试用例。一切都应该是可运行的;抱歉,堆栈跟踪与提供的代码不一致。
【参考方案1】:
我认为问题出在这里:
for color in self.domains[var]:
if not self._valid(var, color):
continue
self.map[var].color = color
for key in node.neighbors:
if color in self.domains[key]:
self.domains[key].remove(color) # This is potentially bad.
如果在调用self.domains[key].remove(color)
时key == var
,您将更改当前正在迭代的集合的大小。您可以通过使用来避免这种情况
for color in self.domains[var].copy():
使用 copy() 将允许您迭代集合的副本,同时从原始中删除项目。
【讨论】:
虽然这是一个很好的建议,但这在提供的代码中实际上并没有发生(刚刚验证过)。域的映射也是var: possible_colors
。你能解释一下for color in self.domains[var].keys():
应该放在哪里吗?
我的建议是将solve()方法中的for color in self.domains[var]:
替换为for color in self.domains[var].keys():
。那应该消除您发布的回溯。
self.domains[var].keys()
产生以下内容:for color in self.domains[var].keys(): AttributeError: 'set' object has no attribute 'keys'
啊,对不起,我还以为是字典。您可以改用 .copy()。
我已将原始答案编辑为使用 .copy() 而不是 .keys()。以上是关于递归:如何避免 Python set changed set 在迭代 RuntimeError 期间的主要内容,如果未能解决你的问题,请参考以下文章
Appium1.10.1+python3如何避免每次安装AppiumUnlock,appium Settings
Python面试题目之(针对dict或者set数据类型)边遍历 边修改 报错dictionary changed size during iteration