Finalized algorithm

This commit is contained in:
Andreas Fruhwirt 2024-01-15 13:20:49 +01:00
parent 34b7a83d1a
commit d58421f97c
2 changed files with 105 additions and 124 deletions

View File

@ -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
View File

@ -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()