递归:如何避免在迭代RuntimeError期间更改Python集 [英] Recursion: how to avoid Python set changed set during iteration RuntimeError

查看:107
本文介绍了递归:如何避免在迭代RuntimeError期间更改Python集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码可以解决图形着色问题(广泛定义为将颜色分配给无向图的问题,确保通过边连接的两个顶点都不具有相同的颜色)。我正在尝试使用约束传播来实现解决方案,以提高标准递归回溯算法的效率,但遇到以下错误:

I have some code that solves the graph coloring problem (broadly defined as the problem of assigning "colors" to an undirected graph, making sure that no two vertices connected by an edge have the same color). I'm trying to implement a solution using constraint propagation to improve on the efficiency of a standard recursive backtracking algorithm, but am running into the following error:

  File "C:\Users\danisg\Desktop\coloring\Solver.py", 
  line 99, in solve
  for color in self.domains[var]:
  RuntimeError: Set changed size during iteration

在这里,对于每个顶点,我保留一个设置该特定顶点的可能特定值:

Here, for each vertex, I keep a set of possible particular values for that particular vertex:

  self.domains = { var: set(self.colors) for var in self.vars }

分配,我将此约束传播到相邻域,以限制搜索空间:

After I make an assignment, I propagate this constraint to the neighboring domains, to limit the search space:

  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 块中),但这可能是问题的根源。

This isn't where the actual error is thrown (in my code, I indicate where the problem is in a try-except block), but may be the source of the problem.

如果不是正确的实现方法,我是否有正确的想法?更重要的是,我该如何解决?另外,是否有必要保留单独的词典?还是可以将 domain 设置为图中每个节点的属性?

Do I have the right idea, here, if not the right implementation? More to the point, how can I fix this? Also, is it necessary to keep a separate domains dictionary? Or could we make domain a property of each node in the graph?

这是 solve 函数这段代码被称为:

Here's the solve function where this code is called:

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 }

其他两个已使用/有用的功能:

Here are two other functions that are used/are helpful:

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



代码失败的数据和示例:



我正在使用的示例图可在此处 a>。

读取数据的功能:

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


推荐答案

,我认为问题是此处:

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.

如果 key == var self.domains [key] .remove(color)被调用,您可以更改当前正在迭代的集合的大小。您可以通过以下方法来避免这种情况:

if key == var when self.domains[key].remove(color) is called, you change the size of the set you're currently iterating over. You can avoid this by using

for color in self.domains[var].copy():

使用copy()将允许您遍历集合的副本,同时从原始对象中删除项目。

Using copy() will allow you to iterate over a copy of the set, while removing items from the original.

这篇关于递归:如何避免在迭代RuntimeError期间更改Python集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆