-
-
Notifications
You must be signed in to change notification settings - Fork 46.6k
/
Copy pathcoloring.py
178 lines (147 loc) · 5.82 KB
/
coloring.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"""
Graph Coloring (also called the "m coloring problem") is the problem of
assigning at most 'm' colors to the vertices of a graph such that
no two adjacent vertices share the same color.
Wikipedia: https://en.wikipedia.org/wiki/Graph_coloring
"""
def valid_coloring(
neighbours: list[int], colored_vertices: list[int], color: int
) -> bool:
"""
Check if a given vertex can be assigned the specified color
without violating the graph coloring constraints (i.e., no two adjacent vertices
have the same color).
Procedure:
For each neighbour check if the coloring constraint is satisfied
If any of the neighbours fail the constraint return False
If all neighbours validate the constraint return True
Parameters:
neighbours: The list representing which vertices
are adjacent to the current vertex.
1 indicates an edge between the current vertex
and the neighbour.
colored_vertices: List of current color assignments for all vertices
(-1 means uncolored).
color: The color we are trying to assign to the current vertex.
Returns:
True if the vertex can be safely colored with the given color,
otherwise False.
Examples:
>>> neighbours = [0, 1, 0, 1, 0]
>>> colored_vertices = [0, 2, 1, 2, 0]
>>> color = 1
>>> valid_coloring(neighbours, colored_vertices, color)
True
>>> color = 2
>>> valid_coloring(neighbours, colored_vertices, color)
False
>>> neighbors = [1, 0, 1, 0]
>>> colored_vertices = [-1, -1, -1, -1]
>>> color = 0
>>> valid_coloring(neighbors, colored_vertices, color)
True
"""
# Check if any adjacent vertex has already been colored with the same color
return not any(
neighbour == 1 and colored_vertices[i] == color
for i, neighbour in enumerate(neighbours)
)
def util_color(
graph: list[list[int]], max_colors: int, colored_vertices: list[int], index: int
) -> bool:
"""
Recursive function to try and color the graph using backtracking.
Base Case:
1. Check if coloring is complete
1.1 If complete return True (meaning that we successfully colored the graph)
Recursive Step:
2. Iterates over each color:
Check if the current coloring is valid:
2.1. Color given vertex
2.2. Do recursive call, check if this coloring leads to a solution
2.4. if current coloring leads to a solution return
2.5. Uncolor given vertex
Parameters:
graph: Adjacency matrix representing the graph.
graph[i][j] is 1 if there is an edge
between vertex i and j.
max_colors: Maximum number of colors allowed (m in the m-coloring problem).
colored_vertices: Current color assignments for each vertex.
-1 indicates that the vertex has not been colored
yet.
index: The current vertex index being processed.
Returns:
True if the graph can be colored using at most max_colors, otherwise False.
Examples:
>>> graph = [[0, 1, 0, 0, 0],
... [1, 0, 1, 0, 1],
... [0, 1, 0, 1, 0],
... [0, 1, 1, 0, 0],
... [0, 1, 0, 0, 0]]
>>> max_colors = 3
>>> colored_vertices = [0, 1, 0, 0, 0]
>>> index = 3
>>> util_color(graph, max_colors, colored_vertices, index)
True
>>> max_colors = 2
>>> util_color(graph, max_colors, colored_vertices, index)
False
"""
# Base Case: If all vertices have been assigned a color, we have a valid solution
if index == len(graph):
return True
# Try each color for the current vertex
for color in range(max_colors):
# Check if it's valid to color the current vertex with 'color'
if valid_coloring(graph[index], colored_vertices, color):
colored_vertices[index] = color # Assign color
# Recur to color the rest of the vertices
if util_color(graph, max_colors, colored_vertices, index + 1):
return True
# Backtrack if no solution found with the current assignment
colored_vertices[index] = -1
return False # Return False if no valid coloring is possible
def color(graph: list[list[int]], max_colors: int) -> list[int]:
"""
Attempt to color the graph with at most max_colors colors such that no two adjacent
vertices have the same color.
If it is possible, returns the list of color assignments;
otherwise, returns an empty list.
Parameters:
graph: Adjacency matrix representing the graph.
max_colors: Maximum number of colors allowed.
Returns:
List of color assignments if the graph can be colored using max_colors.
Each index in the list represents the color assigned
to the corresponding vertex.
If coloring is not possible, returns an empty list.
Examples:
>>> graph = [[0, 1, 0, 0, 0],
... [1, 0, 1, 0, 1],
... [0, 1, 0, 1, 0],
... [0, 1, 1, 0, 0],
... [0, 1, 0, 0, 0]]
>>> max_colors = 3
>>> color(graph, max_colors)
[0, 1, 0, 2, 0]
>>> max_colors = 2
>>> color(graph, max_colors)
[]
>>> graph = [[0, 1], [1, 0]] # Simple 2-node graph
>>> max_colors = 2
>>> color(graph, max_colors)
[0, 1]
>>> graph = [[0, 1, 1], [1, 0, 1], [1, 1, 0]] # Complete graph of 3 vertices
>>> max_colors = 2
>>> color(graph, max_colors)
[]
>>> max_colors = 3
>>> color(graph, max_colors)
[0, 1, 2]
"""
# Initialize all vertices as uncolored (-1)
colored_vertices = [-1] * len(graph)
# Use the utility function to try and color the graph starting from vertex 0
if util_color(graph, max_colors, colored_vertices, 0):
return colored_vertices # The successful color assignment
return [] # No valid coloring is possible