Finalized algorithm
This commit is contained in:
parent
34b7a83d1a
commit
d58421f97c
14
Readme.md
14
Readme.md
@ -14,4 +14,16 @@ uncolored, and proceeds with the next iteration.*
|
||||
The code may be written in any programming language. Submit your code via
|
||||
GIT (link the repository in your submission) including instructions on how to run it.
|
||||
Additionally provide several example inputs (on at least a few hundred nodes) and a
|
||||
test case that your algorithm computes a proper vertex coloring.
|
||||
test case that your algorithm computes a proper vertex coloring.
|
||||
|
||||
Run the program with the following statement:
|
||||
```
|
||||
$ python main.py <#nodes>
|
||||
```
|
||||
or run it with a graph that has 100 to 300 nodes:
|
||||
```
|
||||
$ python main.py
|
||||
```
|
||||
All the edges are generated with a random percentage chance between 0% and 100%.
|
||||
|
||||
Every execution the program checks if the algorithm ran correctly.
|
||||
215
main.py
215
main.py
@ -3,148 +3,117 @@ from random import randint, random
|
||||
import networkx as nx
|
||||
import sys
|
||||
|
||||
class Node(object):
|
||||
DEBUG = False
|
||||
|
||||
def __init__(self, id, g):
|
||||
self.graph = g
|
||||
self.id = id
|
||||
self.color = None
|
||||
self.candidate_list = None
|
||||
self.queue = "start"
|
||||
self.next_tick = None
|
||||
self.edges = []
|
||||
self.done = False
|
||||
class NodeInformation:
|
||||
def __init__(self, color_amt):
|
||||
self.available_colors = [i for i in range(0, color_amt)]
|
||||
self.neighbor_colors = []
|
||||
self.is_done = False
|
||||
self.assigned_color = None
|
||||
|
||||
def add_edge(self, node):
|
||||
self.edges.append(node)
|
||||
color_amt = None
|
||||
ni = {}
|
||||
g = None
|
||||
|
||||
def print(self):
|
||||
print(f"Node {self.id} = (c:{self.color}, #e:{len(self.edges)})")
|
||||
def run_tick():
|
||||
global color_amt, ni, g
|
||||
if DEBUG: print("===== RUN TICK =====")
|
||||
# run for each node
|
||||
for n in g.nodes:
|
||||
if ni[n].is_done:
|
||||
continue
|
||||
neighbors = list(g.adj[n])
|
||||
|
||||
def run_tick(self):
|
||||
if not self.done:
|
||||
# candidate list of colors
|
||||
self.candidate_list = self.graph.possible_colors.copy()
|
||||
if not self.next_tick == "start":
|
||||
# if its not the first iteration w/o data, filter out all neighbor colors from candidate list
|
||||
neighbor_candidates = self.next_tick.copy()
|
||||
for colore in neighbor_candidates:
|
||||
if colore in self.candidate_list:
|
||||
self.candidate_list.remove(colore)
|
||||
# each uncolored node selects random candidate color
|
||||
available_colors = ni[n].available_colors
|
||||
ni[n].assigned_color = ni[n].available_colors[randint(0, len(available_colors)-1)]
|
||||
if DEBUG: print(f"{n} | assigned_color: {ni[n].assigned_color}")
|
||||
if DEBUG: print(f"{n} | neighbors: {neighbors}")
|
||||
|
||||
# if color is in candidate list (no neighbors has this color), finish.
|
||||
# do not run this at start
|
||||
if self.color in self.candidate_list and self.color is not None:
|
||||
self.done = True
|
||||
self.queue = "stop"
|
||||
#print(f"{self.id} | is done")
|
||||
return
|
||||
|
||||
# pick random color from candidate list
|
||||
self.color = self.candidate_list[randint(0, len(self.candidate_list) - 1)]
|
||||
# nodes exchange candidate colors
|
||||
for neigh in neighbors:
|
||||
if not ni[neigh].is_done:
|
||||
ni[neigh].neighbor_colors.append(ni[n].assigned_color)
|
||||
|
||||
#print(f"{self.id} | possible colors: {self.candidate_list}, current color: {self.color}")
|
||||
# run again, but only after neighbor_colors assigned for all nodes
|
||||
for n in g.nodes:
|
||||
if ni[n].is_done:
|
||||
continue
|
||||
neighbors = list(g.adj[n])
|
||||
|
||||
# send data to the other neighbors
|
||||
for n in self.edges:
|
||||
if n.queue != "stop":
|
||||
n.queue.append(self.color)
|
||||
#print(f"{self.id} => sending {self.color} to N[{n.id}]")
|
||||
if DEBUG: print(f"{n} | neighbor_colors: {ni[n].neighbor_colors}, assigned_color: {ni[n].assigned_color}")
|
||||
|
||||
# check if assigned color is used by neighbor
|
||||
if ni[n].assigned_color in ni[n].neighbor_colors:
|
||||
# reset
|
||||
ni[n].assigned_color = None
|
||||
ni[n].neighbor_colors = []
|
||||
else:
|
||||
# get permantly colored
|
||||
ni[n].is_done = True
|
||||
# remove color from available colors of neighbor
|
||||
for neigh in neighbors:
|
||||
if not ni[neigh].is_done:
|
||||
if ni[n].assigned_color in ni[neigh].available_colors:
|
||||
ni[neigh].available_colors.remove(ni[n].assigned_color)
|
||||
|
||||
class Graph(object):
|
||||
is_done = True
|
||||
for n in g.nodes:
|
||||
is_done &= ni[n].is_done
|
||||
|
||||
return 0 if is_done else 1
|
||||
|
||||
def __init__(self):
|
||||
self.V = None
|
||||
self.max_degree = None
|
||||
|
||||
def load_graph_file(self, graph_file):
|
||||
with open(graph_file, "r") as f:
|
||||
V_amt, E_amt = map(int, f.readline().split(" "))
|
||||
self.V = [Node(x, self) for x in range(1, V_amt+1)]
|
||||
self.E = {}
|
||||
self.max_degree = 0
|
||||
for i in range(0, E_amt):
|
||||
a, b = map(int, f.readline().split(" "))
|
||||
a -= 1
|
||||
b -= 1
|
||||
self.V[a].add_edge(self.V[b])
|
||||
self.V[b].add_edge(self.V[a])
|
||||
self.max_degree = max(self.max_degree, len(self.V[a].edges), len(self.V[b].edges))
|
||||
|
||||
self.possible_colors = [x for x in range(1, self.max_degree + 2)]
|
||||
|
||||
def load_graph_networkx(self, graph):
|
||||
self.V = [Node(x, self) for x in range(1, graph.number_of_nodes() + 1)]
|
||||
self.E = {}
|
||||
self.max_degree = 0
|
||||
for a, b in graph.edges:
|
||||
self.V[a].add_edge(self.V[b])
|
||||
self.V[b].add_edge(self.V[a])
|
||||
self.max_degree = max(self.max_degree, len(self.V[a].edges), len(self.V[b].edges))
|
||||
|
||||
self.possible_colors = [x for x in range(1, self.max_degree + 2)]
|
||||
|
||||
def run_tick(self):
|
||||
print("Next Tick")
|
||||
# send data
|
||||
for n in self.V:
|
||||
n.next_tick = n.queue
|
||||
n.queue = []
|
||||
# run tick for each
|
||||
for n in self.V:
|
||||
n.run_tick()
|
||||
|
||||
done = True
|
||||
for n in self.V:
|
||||
done &= n.done
|
||||
|
||||
if done:
|
||||
print("Stop")
|
||||
return -1
|
||||
pass
|
||||
|
||||
def print_graph(self):
|
||||
print(f"Max Degree: {self.max_degree}")
|
||||
for n in self.V:
|
||||
n.print()
|
||||
|
||||
def check_correctness(self):
|
||||
for n in self.V:
|
||||
color = n.color
|
||||
for neighbor in n.edges:
|
||||
if neighbor.color == color:
|
||||
print("=====================")
|
||||
n.print()
|
||||
neighbor.print()
|
||||
print(f"Neighbor {neighbor.id} has same color as {n.id}")
|
||||
print("=====================")
|
||||
return False
|
||||
return True
|
||||
def check_correctness():
|
||||
global color_amt, ni, g
|
||||
total_colors = []
|
||||
for n in g.nodes:
|
||||
if ni[n].assigned_color not in total_colors:
|
||||
total_colors.append(ni[n].assigned_color)
|
||||
neighbors = list(g.adj[n])
|
||||
for neigh in neighbors:
|
||||
if ni[n].assigned_color == ni[neigh].assigned_color:
|
||||
print(f"FAIL: {n} and {neigh} have same color {ni[n].assigned_color}")
|
||||
assert False
|
||||
|
||||
if len(total_colors) > color_amt:
|
||||
print(f"FAIL: color_amt not correct: {len(total_colors)} <= {color_amt}")
|
||||
assert False
|
||||
|
||||
def main():
|
||||
g = Graph()
|
||||
global color_amt, ni, g
|
||||
amt_nodes = randint(100, 300)
|
||||
if len(sys.argv) == 2:
|
||||
filename = sys.argv[1]
|
||||
g.load_graph(filename)
|
||||
else:
|
||||
G = nx.erdos_renyi_graph(randint(100, 300), random())
|
||||
g.load_graph_networkx(G)
|
||||
|
||||
g.print_graph()
|
||||
amt_nodes = int(sys.argv[1])
|
||||
|
||||
for i in range(0, 10):
|
||||
ret = g.run_tick()
|
||||
if ret == -1:
|
||||
g = nx.erdos_renyi_graph(amt_nodes, random())
|
||||
|
||||
# find out max degree
|
||||
max_degree = 0
|
||||
for n in g.nodes:
|
||||
max_degree = max(max_degree, g.degree[n])
|
||||
|
||||
color_amt = max_degree + 1
|
||||
print(f"Amount of Colors: {color_amt}")
|
||||
|
||||
for n in g.nodes:
|
||||
ni[n] = NodeInformation(color_amt)
|
||||
|
||||
ticks = 0
|
||||
while True:
|
||||
ret = run_tick()
|
||||
ticks += 1
|
||||
if ret == 0:
|
||||
# print info
|
||||
print("Final colors:")
|
||||
for n in g.V:
|
||||
n.print()
|
||||
for n in g.nodes:
|
||||
print(f"{n}[{ni[n].assigned_color}] ", end="")
|
||||
print()
|
||||
print(f"Amount of Ticks: {ticks}")
|
||||
|
||||
# assert if correct
|
||||
assert g.check_correctness()
|
||||
check_correctness()
|
||||
exit(0)
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
x
Reference in New Issue
Block a user