web-dev-qa-db-ja.com

再帰:回避方法Python set変更されたセットの反復RuntimeError

背景と問題の説明:

グラフの色付けの問題を解決するコードがあります(広義には、無向グラフに「色」を割り当てる問題として定義され、エッジによって接続された2つの頂点が同じ色を持たないようにします)。制約伝搬を使用して標準の再帰的バックトラッキングアルゴリズムの効率を向上させるソリューションを実装しようとしていますが、次のエラーが発生しています。

  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 }

使用されている/役立つその他の2つの関数を次に示します。

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
25
rookie

私は問題がここにあると思います:

_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()を使用すると、元のアイテムを削除しながら、セットのコピーを反復処理できます。

43
dano