グラフの色付けの問題を解決するコードがあります(広義には、無向グラフに「色」を割り当てる問題として定義され、エッジによって接続された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
私は問題がここにあると思います:
_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()を使用すると、元のアイテムを削除しながら、セットのコピーを反復処理できます。