J'ai du code qui résout le problème de coloration du graphe (largement défini comme le problème de l'attribution de "couleurs" à un graphe non orienté, en veillant à ce que deux sommets connectés par un Edge n'aient pas la même couleur). J'essaie d'implémenter une solution utilisant la propagation de contraintes pour améliorer l'efficacité d'un algorithme de retour arrière récursif standard, mais je rencontre l'erreur suivante:
File "C:\Users\danisg\Desktop\coloring\Solver.py",
line 99, in solve
for color in self.domains[var]:
RuntimeError: Set changed size during iteration
Ici, pour chaque sommet, je garde un set
de valeurs particulières possibles pour ce sommet particulier:
self.domains = { var: set(self.colors) for var in self.vars }
Après avoir effectué une affectation, je propage cette contrainte aux domaines voisins, pour limiter l'espace de recherche:
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)
Ce n'est pas là que l'erreur réelle est levée (dans mon code, j'indique où se trouve le problème dans un try-except
block), mais peut être à l'origine du problème.
Ai-je la bonne idée, ici, sinon la bonne mise en œuvre? Plus précisément, comment puis-je résoudre ce problème? Est-il également nécessaire de conserver un dictionnaire domains
distinct? Ou pourrions-nous faire de domain
une propriété de chaque nœud du graphique?
Voici la fonction solve
où ce code est appelé:
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
Mon implémentation utilise un objet 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 }
Voici deux autres fonctions utilisées/utiles:
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
Le graphique d'exemple que j'utilise peut être trouvé ici .
La fonction de lecture des données:
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
Il devrait être appelé comme suit:
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
Je pense que le problème est ici:
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.
si key == var
lorsque self.domains[key].remove(color)
est appelée, vous changez la taille de l'ensemble sur lequel vous êtes en train d'itérer. Vous pouvez éviter cela en utilisant
for color in self.domains[var].copy():
L'utilisation de copy () vous permettra d'itérer sur une copie de l'ensemble, tout en supprimant des éléments de l'original.