lead/gcdata/gc.py

1640 lines
55 KiB
Python
Raw Normal View History

2025-03-17 16:40:01 +08:00
import numpy as np
def greedy_coloring(adj_matrix):
"""简单贪心算法
按顺序为每个顶点分配可用的最小颜色编号
"""
n = adj_matrix.shape[0]
colors = [-1] * n
# 为第一个顶点分配颜色0
colors[0] = 0
# 为剩余顶点着色
for u in range(1, n):
# 标记已用于邻接点的颜色
used = [False] * n
for v in range(n):
if adj_matrix[u][v] and colors[v] != -1:
used[colors[v]] = True
# 找到第一个未使用的颜色
for c in range(n):
if not used[c]:
colors[u] = c
break
return colors
def welsh_powell_coloring(adj_matrix):
"""Welsh-Powell算法
按度数降序排列顶点,依次为不相邻的顶点分配相同颜色
"""
n = adj_matrix.shape[0]
colors = [-1] * n
# 计算每个顶点的度数
degrees = [(i, sum(adj_matrix[i])) for i in range(n)]
# 按度数降序排序
degrees.sort(key=lambda x: x[1], reverse=True)
color = 0
while -1 in colors:
# 找到未着色的第一个顶点
first = next(v for v,d in degrees if colors[v] == -1)
colors[first] = color
# 为所有可能的不相邻顶点分配相同颜色
for v,_ in degrees:
if colors[v] == -1:
# 检查是否与已分配该颜色的顶点相邻
can_color = True
for u in range(n):
if colors[u] == color and adj_matrix[v][u]:
can_color = False
break
if can_color:
colors[v] = color
color += 1
return colors
def brute_force_coloring(adj_matrix):
"""暴力搜索算法
尝试所有可能的颜色组合直到找到合法解
"""
n = adj_matrix.shape[0]
colors = [-1] * n
def is_safe(v, c):
"""检查顶点v是否可以着色为c"""
for i in range(n):
if adj_matrix[v][i] and colors[i] == c:
return False
return True
def color_graph(v):
"""递归为顶点v及之后的顶点着色"""
if v == n:
return True
for c in range(n):
if is_safe(v, c):
colors[v] = c
if color_graph(v + 1):
return True
colors[v] = -1
return False
if not color_graph(0):
raise Exception("无法找到合法着色方案")
return colors
def dsatur_coloring(adj_matrix):
"""DSATUR算法
基于顶点饱和度的动态着色算法
"""
n = adj_matrix.shape[0]
colors = [-1] * n
def get_saturation(v):
"""计算顶点v的饱和度(邻接顶点使用的不同颜色数)"""
adjacent_colors = set()
for i in range(n):
if adj_matrix[v][i] and colors[i] != -1:
adjacent_colors.add(colors[i])
return len(adjacent_colors)
def get_uncolored_vertex():
"""选择饱和度最大的未着色顶点"""
max_sat = -1
max_deg = -1
selected = -1
for v in range(n):
if colors[v] == -1:
sat = get_saturation(v)
deg = sum(adj_matrix[v])
if sat > max_sat or (sat == max_sat and deg > max_deg):
max_sat = sat
max_deg = deg
selected = v
return selected
# 为第一个最大度数的顶点着色
first = max(range(n), key=lambda i: sum(adj_matrix[i]))
colors[first] = 0
# 为剩余顶点着色
for _ in range(n-1):
v = get_uncolored_vertex()
# 找到最小可用颜色
used = [False] * n
for u in range(n):
if adj_matrix[v][u] and colors[u] != -1:
used[colors[u]] = True
for c in range(n):
if not used[c]:
colors[v] = c
break
return colors
def graph_coloring_v1(adj_matrix):
"""基于度数排序的贪心算法"""
n = adj_matrix.shape[0]
colors = [-1] * n
degree = np.sum(adj_matrix, axis=1)
nodes = np.argsort(-degree)
for node in nodes:
available_colors = [True] * n
for neighbor in range(n):
if adj_matrix[node][neighbor] == 1 and colors[neighbor] != -1:
available_colors[colors[neighbor]] = False
for color in range(n):
if available_colors[color]:
colors[node] = color
break
return colors
def graph_coloring_v2(adj_matrix):
"""基于饱和度的贪心算法"""
n = adj_matrix.shape[0]
colors = np.full(n, -1)
def get_saturation_degree(vertex):
return len(set(colors[v] for v in range(n) if adj_matrix[vertex][v] == 1 and colors[v] != -1))
def find_next_vertex():
max_saturation = -1
candidate_vertex = -1
for vertex in range(n):
if colors[vertex] == -1:
saturation_degree = get_saturation_degree(vertex)
degree = sum(adj_matrix[vertex])
if saturation_degree > max_saturation or (saturation_degree == max_saturation and degree > sum(adj_matrix[candidate_vertex]) if candidate_vertex != -1 else degree):
max_saturation = saturation_degree
candidate_vertex = vertex
return candidate_vertex
for _ in range(n):
u = find_next_vertex()
if u == -1:
break
available_colors = set(range(n))
for v in range(n):
if adj_matrix[u][v] == 1 and colors[v] != -1:
available_colors.discard(colors[v])
if available_colors:
color_usage = np.zeros(n, dtype=int)
for v in range(n):
if adj_matrix[u][v] == 1 and colors[v] != -1:
color_usage[colors[v]] += 1
min_color = min(available_colors, key=lambda c: (color_usage[c], c))
colors[u] = min_color
return colors
def graph_coloring_v3(adj_matrix):
"""带回溯的混合算法"""
n = adj_matrix.shape[0]
colors = np.full(n, -1)
degree_list = sorted(range(n), key=lambda x: sum(adj_matrix[x]), reverse=True)
def can_color(vertex, color):
return all(adj_matrix[vertex][neighbor] == 0 or colors[neighbor] != color for neighbor in range(n))
def backtrack(vertex):
if vertex == n:
return True
for color in range(n):
if can_color(vertex, color):
colors[vertex] = color
if backtrack(vertex + 1):
return True
colors[vertex] = -1
return False
for u in degree_list:
available_colors = set(range(n))
for v in range(n):
if adj_matrix[u][v] == 1 and colors[v] != -1:
available_colors.discard(colors[v])
if available_colors:
color_usage = np.zeros(n, dtype=int)
for v in range(n):
if adj_matrix[u][v] == 1 and colors[v] != -1:
color_usage[colors[v]] += 1
min_color = min(available_colors, key=lambda c: (color_usage[c], c))
colors[u] = min_color
else:
if not backtrack(u):
raise Exception("无法为图着色")
return colors
def graph_coloring_v4(adj_matrix):
"""混合进化算法"""
import numpy as np
import heapq
from dataclasses import dataclass
from typing import List, Set
import random
@dataclass
class Move:
vertex: int
color: int
class TabuSearch:
def __init__(self, n_vertices, n_colors):
self.n_vertices = n_vertices
self.n_colors = n_colors
self.adj_color_table = np.zeros((n_vertices, n_colors), dtype=int)
self.tabu_tenure = np.zeros((n_vertices, n_colors), dtype=int)
self.conflict = 0
self.best_conflict = 0
self.iteration = 0
def find_move(self, solution, adj_list):
min_delta = float('inf')
tabu_delta = float('inf')
equal_nontabu = []
equal_tabu = []
aspiration = self.best_conflict - self.conflict
for v in range(self.n_vertices):
curr_color = solution[v]
if self.adj_color_table[v][curr_color] > 0:
for c in range(self.n_colors):
if curr_color != c:
delta = self.adj_color_table[v][c] - self.adj_color_table[v][curr_color]
if self.tabu_tenure[v][c] <= self.iteration:
if delta < min_delta:
min_delta = delta
equal_nontabu = [(v, c)]
elif delta == min_delta:
equal_nontabu.append((v, c))
else:
if delta < tabu_delta:
tabu_delta = delta
equal_tabu = [(v, c)]
elif delta == tabu_delta:
equal_tabu.append((v, c))
if tabu_delta < aspiration and tabu_delta < min_delta:
v, c = random.choice(equal_tabu)
return Move(v, c), tabu_delta
else:
v, c = random.choice(equal_nontabu)
return Move(v, c), min_delta
def make_move(self, solution, move, adj_list):
self.conflict += move[1]
if self.conflict < self.best_conflict:
self.best_conflict = self.conflict
old_color = solution[move[0].vertex]
solution[move[0].vertex] = move[0].color
self.tabu_tenure[move[0].vertex][old_color] = (
self.iteration + self.conflict + random.randint(1,10)
)
for neighbor in adj_list[move[0].vertex]:
self.adj_color_table[neighbor][old_color] -= 1
self.adj_color_table[neighbor][move[0].color] += 1
def search(self, solution, adj_list, max_iter=20000):
# 计算初始冲突
self.conflict = 0
for v in range(self.n_vertices):
color = solution[v]
for u in adj_list[v]:
if solution[u] == color:
self.conflict += 1
self.adj_color_table[v][solution[u]] += 1
self.conflict //= 2
self.best_conflict = self.conflict
self.iteration = 0
while self.iteration < max_iter and self.conflict > 0:
self.iteration += 1
move = self.find_move(solution, adj_list)
self.make_move(solution, move, adj_list)
class Population:
def __init__(self, n_vertices, n_colors, pop_size):
self.solutions = []
self.conflicts = []
self.min_conflict = float('inf')
self.best_solution = None
for _ in range(pop_size):
sol = np.random.randint(0, n_colors, size=n_vertices)
self.solutions.append(sol)
def update(self, new_sol, new_conflict):
if new_conflict < self.min_conflict:
self.min_conflict = new_conflict
self.best_solution = new_sol.copy()
# 替换最差解
max_idx = np.argmax(self.conflicts)
self.solutions[max_idx] = new_sol
self.conflicts[max_idx] = new_conflict
# 主算法流程
n = adj_matrix.shape[0]
n_colors = max(np.sum(adj_matrix, axis=1))
pop_size = 10
# 构建邻接表
adj_list = [[] for _ in range(n)]
for i in range(n):
for j in range(n):
if adj_matrix[i][j]:
adj_list[i].append(j)
# 初始化种群
pop = Population(n, n_colors, pop_size)
tabu = TabuSearch(n, n_colors)
# 对初始种群进行禁忌搜索优化
for sol in pop.solutions:
tabu.search(sol, adj_list)
pop.conflicts.append(tabu.conflict)
if tabu.conflict < pop.min_conflict:
pop.min_conflict = tabu.conflict
pop.best_solution = sol.copy()
# 主循环
max_gen = 1000
gen = 0
while gen < max_gen and pop.min_conflict > 0:
# 选择父代
p1, p2 = random.sample(range(pop_size), 2)
sol1, sol2 = pop.solutions[p1], pop.solutions[p2]
# 交叉
child = np.zeros(n, dtype=int)
for i in range(n_colors):
if i % 2 == 0:
mask = (sol1 == i)
else:
mask = (sol2 == i)
child[mask] = i
# 对未着色顶点随机着色
uncolored = (child == 0)
child[uncolored] = np.random.randint(0, n_colors, size=np.sum(uncolored))
# 禁忌搜索优化新解
tabu.search(child, adj_list)
# 更新种群
pop.update(child, tabu.conflict)
gen += 1
return pop.best_solution
def graph_coloring_v5(adj_matrix):
"""优化的饱和度贪心算法
1. 使用堆维护顶点优先级
2. 缓存并动态更新饱和度
3. 优化邻接颜色集合计算
4. 改进颜色选择策略
"""
import heapq
n = adj_matrix.shape[0]
colors = np.full(n, -1)
# 预计算并缓存每个顶点的度数
degrees = np.sum(adj_matrix, axis=1)
# 缓存每个顶点的邻接顶点列表
adj_lists = [np.where(adj_matrix[i] == 1)[0] for i in range(n)]
# 维护每个顶点的邻接颜色集合
adj_colors = [set() for _ in range(n)]
# 初始化优先队列 (-饱和度, -度数, 顶点ID)
vertex_heap = [(0, -degrees[i], i) for i in range(n)]
heapq.heapify(vertex_heap)
# 标记顶点是否已着色
colored = np.zeros(n, dtype=bool)
def update_saturation(vertex):
"""更新顶点的饱和度并返回新的优先级"""
saturation = len(adj_colors[vertex])
return (-saturation, -degrees[vertex], vertex)
def update_neighbors(vertex, color):
"""更新邻居的饱和度"""
for neighbor in adj_lists[vertex]:
if not colored[neighbor]:
adj_colors[neighbor].add(color)
while vertex_heap:
# 获取优先级最高的未着色顶点
_, _, vertex = heapq.heappop(vertex_heap)
# 如果顶点已着色,跳过
if colored[vertex]:
continue
# 计算可用颜色集合
used_colors = adj_colors[vertex]
available_colors = set(range(n)) - used_colors
if available_colors:
# 统计邻接顶点中各颜色的使用频率
color_usage = np.zeros(n, dtype=int)
for neighbor in adj_lists[vertex]:
if colored[neighbor]:
color_usage[colors[neighbor]] += 1
# 选择使用频率最低的可用颜色
min_color = min(available_colors, key=lambda c: (color_usage[c], c))
colors[vertex] = min_color
colored[vertex] = True
# 更新邻居的饱和度并重新入堆
update_neighbors(vertex, min_color)
# 将受影响的未着色邻居重新入堆
for neighbor in adj_lists[vertex]:
if not colored[neighbor]:
heapq.heappush(vertex_heap, update_saturation(neighbor))
return colors
def graph_coloring_v6(adj_matrix):
"""高性能混合算法
1. 结合DSATUR和禁忌搜索
2. 使用局部搜索优化初始解
3. 采用自适应参数调整
4. 并行处理大规模数据
"""
import heapq
from concurrent.futures import ThreadPoolExecutor
import threading
n = adj_matrix.shape[0]
best_colors = np.full(n, -1)
best_num_colors = n
# 预处理:计算关键数据
degrees = np.sum(adj_matrix, axis=1)
adj_lists = [np.where(adj_matrix[i] == 1)[0] for i in range(n)]
max_degree = np.max(degrees)
initial_colors = max(max_degree + 1, n // 2) # 估计所需颜色数
# 线程安全的更新机制
solution_lock = threading.Lock()
class EnhancedDSATUR:
def __init__(self):
self.colors = np.full(n, -1)
self.colored = np.zeros(n, dtype=bool)
self.saturation = np.zeros(n, dtype=int)
self.adj_colors = [set() for _ in range(n)]
def get_next_vertex(self):
max_sat = -1
max_deg = -1
selected = -1
for v in range(n):
if not self.colored[v]:
sat = self.saturation[v]
deg = degrees[v]
if sat > max_sat or (sat == max_sat and deg > max_deg):
max_sat = sat
max_deg = deg
selected = v
return selected
def color_vertex(self, vertex, max_color):
used = set()
for u in adj_lists[vertex]:
if self.colored[u]:
used.add(self.colors[u])
# 优化颜色选择策略
color_freq = np.zeros(max_color, dtype=int)
for u in adj_lists[vertex]:
if self.colored[u]:
color_freq[self.colors[u]] += 1
# 选择频率最低的可用颜色
available = set(range(max_color)) - used
if available:
color = min(available, key=lambda c: (color_freq[c], c))
self.colors[vertex] = color
self.colored[vertex] = True
# 更新邻居的饱和度
for u in adj_lists[vertex]:
if not self.colored[u]:
self.adj_colors[u].add(color)
self.saturation[u] = len(self.adj_colors[u])
return True
return False
def local_search(solution, num_colors, max_iterations=1000):
"""增强的局部搜索优化
1. 使用禁忌搜索避免循环
2. 采用Kempe链交换
3. 动态邻域大小
4. 自适应步长控制
"""
conflicts = np.zeros(n, dtype=int)
tabu_list = {} # 禁忌表
tabu_tenure = 10 # 禁忌期限
# 计算冲突
def calculate_conflicts():
conflicts.fill(0)
total = 0
for v in range(n):
color = solution[v]
for u in adj_lists[v]:
if solution[u] == color:
conflicts[v] += 1
total += 1
return total // 2
# Kempe链交换
def kempe_chain_interchange(vertex, color1, color2):
chain = {vertex}
queue = [vertex]
old_colors = solution.copy()
while queue:
v = queue.pop(0)
current_color = old_colors[v]
new_color = color2 if current_color == color1 else color1
solution[v] = new_color
for u in adj_lists[v]:
if u not in chain and old_colors[u] in (color1, color2):
chain.add(u)
queue.append(u)
# 评估交换效果
new_conflicts = calculate_conflicts()
if new_conflicts > total_conflicts:
# 如果没有改善则恢复
for v in chain:
solution[v] = old_colors[v]
return False
return True
# 寻找最佳移动
def find_best_move(iteration):
best_delta = float('inf')
best_moves = []
# 动态调整搜索范围
sample_size = min(100 + iteration // 10, n)
vertices = np.random.choice(n, size=sample_size, replace=False)
for v in vertices:
if conflicts[v] == 0:
continue
current_color = solution[v]
# 考虑颜色交换和Kempe链
for c in range(num_colors):
if c != current_color:
# 计算常规移动增量
delta = 0
for u in adj_lists[v]:
if solution[u] == c:
delta += 1
if solution[u] == current_color:
delta -= 1
# 检查禁忌状态
move_key = (v, c)
if move_key in tabu_list and iteration < tabu_list[move_key]:
# 除非这是最好的解
if delta >= best_delta:
continue
if delta <= best_delta:
if delta < best_delta:
best_moves = []
best_delta = delta
best_moves.append((v, c, "normal"))
# 尝试Kempe链交换
if np.random.random() < 0.3: # 控制Kempe链使用频率
temp_sol = solution.copy()
if kempe_chain_interchange(v, current_color, c):
k_delta = calculate_conflicts() - total_conflicts
if k_delta <= best_delta:
if k_delta < best_delta:
best_moves = []
best_delta = k_delta
best_moves.append((v, c, "kempe"))
solution[:] = temp_sol
if not best_moves:
return None, None, None, float('inf')
# 随机选择一个最佳移动
v, c, move_type = best_moves[np.random.randint(len(best_moves))]
return v, c, move_type, best_delta
total_conflicts = calculate_conflicts()
iterations = 0
no_improve = 0
while total_conflicts > 0 and iterations < max_iterations:
v, c, move_type, delta = find_best_move(iterations)
if v is None:
break
# 执行移动
if move_type == "normal":
old_color = solution[v]
solution[v] = c
# 更新冲突
for u in adj_lists[v]:
if solution[u] == old_color:
conflicts[u] -= 1
conflicts[v] -= 1
if solution[u] == c:
conflicts[u] += 1
conflicts[v] += 1
# 更新禁忌表
tabu_list[(v, old_color)] = iterations + tabu_tenure
total_conflicts += delta
iterations += 1
# 自适应参数调整
if delta >= 0:
no_improve += 1
if no_improve >= 20:
# 增加扰动
for _ in range(3):
v = np.random.randint(n)
c = np.random.randint(num_colors)
solution[v] = c
total_conflicts = calculate_conflicts()
no_improve = 0
else:
no_improve = 0
return total_conflicts == 0
def solve_with_colors(num_colors):
"""使用指定颜色数尝试求解"""
dsatur = EnhancedDSATUR()
success = True
# 按DSATUR顺序为顶点着色
for _ in range(n):
v = dsatur.get_next_vertex()
if v == -1:
break
if not dsatur.color_vertex(v, num_colors):
success = False
break
if success:
# 应用局部搜索优化
solution = dsatur.colors.copy()
if local_search(solution, num_colors):
nonlocal best_colors, best_num_colors
with solution_lock:
if num_colors < best_num_colors:
best_colors = solution.copy()
best_num_colors = num_colors
return True
return False
# 并行尝试不同的颜色数
color_range = range(max_degree, initial_colors + 1)
with ThreadPoolExecutor(max_workers=4) as executor:
executor.map(solve_with_colors, color_range)
return best_colors
def graph_coloring_v7(adj_matrix):
"""改进的Welsh-Powell算法
1. 动态度数排序
2. 颜色交换优化
3. 有限深度回溯
4. 颜色合并
5. 特殊结构识别
"""
n = adj_matrix.shape[0]
colors = np.full(n, -1)
# 预处理:识别特殊结构
def find_cliques():
"""识别图中的极大团"""
cliques = []
for i in range(n):
neighbors = set(np.where(adj_matrix[i] == 1)[0])
if not neighbors:
continue
# 检查邻居是否形成团
is_clique = True
for u in neighbors:
for v in neighbors:
if u != v and adj_matrix[u][v] == 0:
is_clique = False
break
if not is_clique:
break
if is_clique:
cliques.append(neighbors.union({i}))
return cliques
# 计算初始度数
degrees = [(i, np.sum(adj_matrix[i])) for i in range(n)]
# 构建邻接表以加速计算
adj_lists = [np.where(adj_matrix[i] == 1)[0] for i in range(n)]
# 计算有效度数(只考虑未着色的邻居)
def effective_degree(v):
return sum(1 for u in adj_lists[v] if colors[u] == -1)
# 颜色交换优化
def kempe_chain_exchange(v, c1, c2):
"""尝试Kempe链交换以减少颜色冲突"""
if c1 == c2:
return False
# 保存原始颜色
original_colors = colors.copy()
# 构建Kempe链
chain = {v}
queue = [v]
while queue:
current = queue.pop(0)
for u in adj_lists[current]:
if colors[u] in (c1, c2) and u not in chain:
chain.add(u)
queue.append(u)
# 交换颜色
for u in chain:
if colors[u] == c1:
colors[u] = c2
elif colors[u] == c2:
colors[u] = c1
# 检查交换后是否有冲突
for u in chain:
for v in adj_lists[u]:
if colors[u] == colors[v] and colors[u] != -1:
# 恢复原始颜色
colors[:] = original_colors
return False
return True
# 颜色合并
def try_merge_colors(c1, c2):
"""尝试将颜色c1合并到c2"""
vertices_with_c1 = np.where(colors == c1)[0]
# 检查每个颜色为c1的顶点是否可以改为c2
can_merge = True
for v in vertices_with_c1:
for u in adj_lists[v]:
if colors[u] == c2:
can_merge = False
break
if not can_merge:
break
if can_merge:
colors[vertices_with_c1] = c2
return True
return False
# 有限深度回溯
def backtrack_coloring(start_vertex, depth=3):
"""从指定顶点开始进行有限深度回溯着色"""
if depth == 0:
return True
# 获取可用颜色
used_colors = set()
for u in adj_lists[start_vertex]:
if colors[u] != -1:
used_colors.add(colors[u])
# 尝试每种可能的颜色
available_colors = [c for c in range(n) if c not in used_colors]
for c in available_colors:
colors[start_vertex] = c
# 找到下一个未着色顶点
next_vertex = -1
for v in range(n):
if colors[v] == -1:
next_vertex = v
break
if next_vertex == -1 or backtrack_coloring(next_vertex, depth-1):
return True
# 回溯
colors[start_vertex] = -1
return False
# 主着色算法
def enhanced_welsh_powell():
max_color = -1
uncolored = set(range(n))
while uncolored:
# 动态更新度数并排序
sorted_vertices = sorted(uncolored,
key=lambda v: (effective_degree(v), sum(adj_matrix[v])),
reverse=True)
# 选择当前度数最大的顶点
current_color = max_color + 1
max_color = current_color
# 为第一个顶点着色
first_vertex = sorted_vertices[0]
colors[first_vertex] = current_color
uncolored.remove(first_vertex)
# 为其他可能的顶点着色
for v in sorted_vertices[1:]:
if v not in uncolored:
continue
# 检查是否可以使用当前颜色
can_use_color = True
for u in adj_lists[v]:
if colors[u] == current_color:
can_use_color = False
break
if can_use_color:
colors[v] = current_color
uncolored.remove(v)
# 识别团并预先着色
cliques = find_cliques()
cliques.sort(key=len, reverse=True)
# 对最大团进行预着色
if cliques:
largest_clique = list(cliques[0])
for i, v in enumerate(largest_clique):
colors[v] = i
# 主着色过程
enhanced_welsh_powell()
# 后处理优化
# 1. 尝试颜色合并
used_colors = sorted(set(colors))
for i in range(len(used_colors)):
for j in range(i+1, len(used_colors)):
try_merge_colors(used_colors[j], used_colors[i])
# 2. 尝试Kempe链交换优化
for v in range(n):
neighbor_colors = set(colors[u] for u in adj_lists[v])
all_colors = set(colors)
for c in all_colors - neighbor_colors:
if c < colors[v]: # 尝试用更小的颜色替换
kempe_chain_exchange(v, colors[v], c)
# 3. 对关键顶点应用有限深度回溯
# 关键顶点:使用了最大颜色编号的顶点
max_color_used = max(colors)
critical_vertices = np.where(colors == max_color_used)[0]
for v in critical_vertices:
# 暂时取消着色
original_color = colors[v]
colors[v] = -1
# 尝试用更少的颜色重新着色
if not backtrack_coloring(v):
# 如果失败,恢复原始颜色
colors[v] = original_color
# 重新映射颜色以确保连续
color_map = {}
new_colors = np.zeros_like(colors)
next_color = 0
for i, c in enumerate(colors):
if c not in color_map:
color_map[c] = next_color
next_color += 1
new_colors[i] = color_map[c]
return new_colors
def graph_coloring_v9(adj_matrix):
"""高级混合图着色算法
结合多种算法优点使用数学优化和多阶段策略尽可能减少所需颜色数
1. 图分解与重组
2. 多阶段混合策略
3. 高级预处理与结构识别
4. 迭代颜色减少
5. 自适应参数调整
"""
import numpy as np
import random
import heapq
import time
from collections import defaultdict, deque
from itertools import combinations
start_time = time.time()
max_time = 60 # 最大运行时间(秒)
n = adj_matrix.shape[0]
best_coloring = np.full(n, -1)
best_color_count = n
# 构建邻接表以加速计算
adj_lists = [np.where(adj_matrix[i] == 1)[0] for i in range(n)]
# 计算顶点度数
degrees = np.sum(adj_matrix, axis=1)
max_degree = np.max(degrees)
# 1. 高级预处理与结构识别
def find_max_clique():
"""使用Bron-Kerbosch算法寻找最大团"""
def bron_kerbosch(r, p, x, max_clique):
if len(p) == 0 and len(x) == 0:
if len(r) > len(max_clique[0]):
max_clique[0] = r.copy()
return
# 选择枢轴点以优化分支
if p:
pivot = max(p, key=lambda v: len(set(adj_lists[v]) & p))
p_minus_n_pivot = p - set(adj_lists[pivot])
else:
p_minus_n_pivot = p
for v in list(p_minus_n_pivot):
neighbors_v = set(adj_lists[v])
bron_kerbosch(r | {v}, p & neighbors_v, x & neighbors_v, max_clique)
p.remove(v)
x.add(v)
# 提前终止条件
if time.time() - start_time > max_time / 10:
return
max_clique = [set()]
vertices = set(range(n))
# 使用度数排序启发式加速
sorted_vertices = sorted(range(n), key=lambda v: degrees[v], reverse=True)
for v in sorted_vertices[:min(100, n)]: # 限制初始顶点数量
if time.time() - start_time > max_time / 10:
break
r = {v}
p = set(adj_lists[v]) & vertices
x = vertices - p - r
bron_kerbosch(r, p, x, max_clique)
return list(max_clique[0])
def identify_independent_sets():
"""识别大的独立集"""
independent_sets = []
remaining = set(range(n))
while remaining and time.time() - start_time < max_time / 10:
# 选择度数最小的顶点开始
start_vertex = min(remaining, key=lambda v: degrees[v])
ind_set = {start_vertex}
candidates = remaining - {start_vertex} - set(adj_lists[start_vertex])
# 贪心扩展独立集
while candidates:
# 选择度数最小的顶点加入
v = min(candidates, key=lambda v: degrees[v])
ind_set.add(v)
candidates -= {v} | set(adj_lists[v])
independent_sets.append(ind_set)
remaining -= ind_set
return independent_sets
def find_bipartite_subgraphs():
"""识别二分图子结构"""
bipartite_subgraphs = []
visited = np.zeros(n, dtype=bool)
def bfs_bipartite(start):
queue = deque([(start, 0)]) # (vertex, color)
coloring = {start: 0}
component = {start}
while queue and time.time() - start_time < max_time / 10:
v, color = queue.popleft()
next_color = 1 - color
for u in adj_lists[v]:
if u not in coloring:
coloring[u] = next_color
component.add(u)
queue.append((u, next_color))
elif coloring[u] == color: # 冲突,不是二分图
return None
# 分离两个部分
part0 = {v for v, c in coloring.items() if c == 0}
part1 = {v for v, c in coloring.items() if c == 1}
return (part0, part1)
# 寻找所有连通的二分子图
for i in range(n):
if not visited[i] and time.time() - start_time < max_time / 10:
result = bfs_bipartite(i)
if result:
part0, part1 = result
bipartite_subgraphs.append((part0, part1))
for v in part0 | part1:
visited[v] = True
return bipartite_subgraphs
# 2. 图分解技术
def decompose_graph():
"""将图分解为更易处理的组件"""
# 寻找割点和桥
articulation_points = find_articulation_points()
# 移除割点后的连通分量
components = []
visited = np.zeros(n, dtype=bool)
def dfs(v, component):
visited[v] = True
component.add(v)
for u in adj_lists[v]:
if not visited[u] and u not in articulation_points:
dfs(u, component)
# 找出所有连通分量
for i in range(n):
if not visited[i] and i not in articulation_points:
component = set()
dfs(i, component)
if component:
components.append(component)
# 如果没有找到有效分解,返回整个图
if not components:
return [set(range(n))]
return components
def find_articulation_points():
"""使用Tarjan算法寻找割点"""
disc = [-1] * n
low = [-1] * n
visited = [False] * n
ap = set()
timer = [0]
def dfs(u, parent):
children = 0
visited[u] = True
disc[u] = low[u] = timer[0]
timer[0] += 1
for v in adj_lists[u]:
if not visited[v]:
children += 1
dfs(v, u)
low[u] = min(low[u], low[v])
# 检查u是否为割点
if parent != -1 and low[v] >= disc[u]:
ap.add(u)
elif v != parent:
low[u] = min(low[u], disc[v])
# 如果u是根节点且有多个子节点
if parent == -1 and children > 1:
ap.add(u)
for i in range(n):
if not visited[i]:
dfs(i, -1)
return ap
# 3. 核心着色算法
def dsatur_coloring(vertices=None, max_colors=None):
"""增强版DSATUR算法"""
if vertices is None:
vertices = set(range(n))
if max_colors is None:
max_colors = n
n_sub = len(vertices)
vertices_list = list(vertices)
vertex_map = {v: i for i, v in enumerate(vertices_list)}
reverse_map = {i: v for v, i in vertex_map.items()}
# 创建子图的邻接表
sub_adj_lists = [[] for _ in range(n_sub)]
for i, v in enumerate(vertices_list):
for u in adj_lists[v]:
if u in vertex_map:
sub_adj_lists[i].append(vertex_map[u])
colors = np.full(n_sub, -1)
saturation = np.zeros(n_sub, dtype=int)
adj_colors = [set() for _ in range(n_sub)]
# 计算子图中的度数
sub_degrees = np.zeros(n_sub, dtype=int)
for i in range(n_sub):
sub_degrees[i] = len(sub_adj_lists[i])
# 初始化优先队列 (-饱和度, -度数, 顶点ID)
vertex_heap = [(0, -sub_degrees[i], i) for i in range(n_sub)]
heapq.heapify(vertex_heap)
colored_count = 0
colored_vertices = np.zeros(n_sub, dtype=bool)
while colored_count < n_sub and vertex_heap:
# 获取优先级最高的未着色顶点
_, _, vertex = heapq.heappop(vertex_heap)
# 如果顶点已着色,跳过
if colored_vertices[vertex]:
continue
# 计算可用颜色
used = [False] * max_colors
for u in sub_adj_lists[vertex]:
if colors[u] != -1:
used[colors[u]] = True
# 找到最小可用颜色
color = -1
for c in range(max_colors):
if not used[c]:
color = c
break
if color == -1:
# 没有可用颜色
return None
colors[vertex] = color
colored_vertices[vertex] = True
colored_count += 1
# 更新邻居的饱和度
for u in sub_adj_lists[vertex]:
if not colored_vertices[u]:
if color not in adj_colors[u]:
adj_colors[u].add(color)
saturation[u] += 1
# 重新入堆以更新优先级
heapq.heappush(vertex_heap, (-saturation[u], -sub_degrees[u], u))
# 将子图着色映射回原图
result = np.full(n, -1)
for i in range(n_sub):
result[reverse_map[i]] = colors[i]
return result
def kempe_chain_interchange(coloring, v, c1, c2):
"""Kempe链交换"""
if coloring[v] != c1 or c1 == c2:
return False
# 保存原始着色
original = coloring.copy()
# 构建Kempe链
chain = {v}
queue = deque([v])
while queue:
current = queue.popleft()
for u in adj_lists[current]:
if u not in chain and coloring[u] in (c1, c2):
chain.add(u)
queue.append(u)
# 交换颜色
for u in chain:
if coloring[u] == c1:
coloring[u] = c2
elif coloring[u] == c2:
coloring[u] = c1
# 验证交换后的合法性
for u in range(n):
for v in adj_lists[u]:
if coloring[u] == coloring[v] and coloring[u] != -1:
# 恢复原始着色
coloring[:] = original
return False
return True
def iterated_greedy(initial_coloring=None, iterations=100):
"""迭代贪心算法"""
if initial_coloring is None:
# 使用DSATUR生成初始解
coloring = dsatur_coloring()
else:
coloring = initial_coloring.copy()
best = coloring.copy()
best_colors_used = len(set(coloring))
for _ in range(iterations):
if time.time() - start_time > max_time:
break
# 随机选择顶点顺序
vertices = list(range(n))
random.shuffle(vertices)
# 临时移除顶点着色
temp_coloring = coloring.copy()
for v in vertices:
temp_coloring[v] = -1
# 重新着色
for v in vertices:
used = [False] * n
for u in adj_lists[v]:
if temp_coloring[u] != -1:
used[temp_coloring[u]] = True
# 找到最小可用颜色
for c in range(n):
if not used[c]:
temp_coloring[v] = c
break
# 更新最佳解
colors_used = len(set(temp_coloring))
if colors_used < best_colors_used:
best = temp_coloring.copy()
best_colors_used = colors_used
coloring = temp_coloring.copy()
elif random.random() < 0.3: # 有时接受较差解以跳出局部最优
coloring = temp_coloring.copy()
return best
def tabu_search(initial_coloring, max_iterations=1000):
"""禁忌搜索优化"""
coloring = initial_coloring.copy()
best_coloring = coloring.copy()
best_colors_used = len(set(coloring))
# 初始化禁忌表
tabu_list = {}
tabu_tenure = 10
iteration = 0
# 计算冲突
def count_conflicts():
conflicts = 0
for v in range(n):
for u in adj_lists[v]:
if u > v and coloring[u] == coloring[v]:
conflicts += 1
return conflicts
# 找到最佳移动
def find_best_move():
best_delta = float('inf')
best_moves = []
for v in range(n):
current_color = coloring[v]
# 计算当前冲突
current_conflicts = sum(1 for u in adj_lists[v] if coloring[u] == current_color)
# 尝试所有可能的颜色
for c in range(best_colors_used + 1):
if c != current_color:
# 计算新冲突
new_conflicts = sum(1 for u in adj_lists[v] if coloring[u] == c)
delta = new_conflicts - current_conflicts
# 检查禁忌状态
move_key = (v, c)
is_tabu = move_key in tabu_list and iteration < tabu_list[move_key]
# 特赦准则:如果移动导致新的最佳解
if is_tabu and not (new_conflicts == 0 and c < best_colors_used):
continue
if delta <= best_delta:
if delta < best_delta:
best_moves = []
best_delta = delta
best_moves.append((v, c))
if not best_moves:
return None, None
# 随机选择一个最佳移动
v, c = random.choice(best_moves)
return v, c
# 主循环
while iteration < max_iterations and time.time() - start_time < max_time:
v, c = find_best_move()
if v is None:
break
# 执行移动
old_color = coloring[v]
coloring[v] = c
# 更新禁忌表
tabu_list[(v, old_color)] = iteration + tabu_tenure
# 更新最佳解
colors_used = len(set(coloring))
if colors_used < best_colors_used:
best_coloring = coloring.copy()
best_colors_used = colors_used
iteration += 1
return best_coloring
# 4. 多阶段混合策略
def hybrid_coloring():
"""多阶段混合着色策略"""
# 阶段1: 预处理和结构识别
max_clique = find_max_clique()
clique_size = len(max_clique)
# 阶段2: 初始解生成
# 使用DSATUR生成初始解
initial_coloring = dsatur_coloring()
if initial_coloring is None:
initial_coloring = np.zeros(n, dtype=int)
for i in range(n):
used = set()
for j in adj_lists[i]:
if j < i:
used.add(initial_coloring[j])
for c in range(n):
if c not in used:
initial_coloring[i] = c
break
# 阶段3: 迭代优化
# 应用迭代贪心
improved_coloring = iterated_greedy(initial_coloring, iterations=50)
colors_used = len(set(improved_coloring))
if colors_used < best_color_count:
best_coloring[:] = improved_coloring
best_color_count = colors_used
# 阶段4: 禁忌搜索优化
if time.time() - start_time < max_time * 0.7:
tabu_coloring = tabu_search(improved_coloring, max_iterations=500)
tabu_colors_used = len(set(tabu_coloring))
if tabu_colors_used < best_color_count:
best_coloring[:] = tabu_coloring
best_color_count = tabu_colors_used
# 阶段5: 颜色合并
if time.time() - start_time < max_time * 0.9:
merged_coloring = color_merging(best_coloring.copy())
merged_colors_used = len(set(merged_coloring))
if merged_colors_used < best_color_count:
best_coloring[:] = merged_coloring
best_color_count = merged_colors_used
def color_merging(coloring):
"""尝试合并颜色类"""
colors_used = sorted(set(coloring))
# 尝试合并每对颜色
for c1, c2 in combinations(colors_used, 2):
# 检查是否可以合并
can_merge = True
vertices_with_c1 = np.where(coloring == c1)[0]
for v in vertices_with_c1:
for u in adj_lists[v]:
if coloring[u] == c2:
can_merge = False
break
if not can_merge:
break
if can_merge:
# 合并颜色
coloring[vertices_with_c1] = c2
# 递归尝试更多合并
return color_merging(coloring)
# 重新映射颜色以确保连续
color_map = {}
new_coloring = np.zeros_like(coloring)
next_color = 0
for i, c in enumerate(coloring):
if c not in color_map:
color_map[c] = next_color
next_color += 1
new_coloring[i] = color_map[c]
return new_coloring
# 5. 图分解与重组
def solve_by_decomposition():
"""通过图分解求解"""
# 分解图
components = decompose_graph()
if len(components) > 1:
# 分别处理每个组件
component_colorings = []
max_colors = 0
for component in components:
# 为组件着色
comp_coloring = dsatur_coloring(component)
# 重新映射颜色
color_map = {}
for v in component:
if comp_coloring[v] not in color_map:
color_map[comp_coloring[v]] = len(color_map)
comp_coloring[v] = color_map[comp_coloring[v]]
component_colorings.append((component, comp_coloring))
max_colors = max(max_colors, max(comp_coloring[list(component)]) + 1)
# 重组解
combined_coloring = np.full(n, -1)
for component, comp_coloring in component_colorings:
for v in component:
combined_coloring[v] = comp_coloring[v]
return combined_coloring
else:
return None
# 6. 迭代颜色减少
def iterative_color_reduction(coloring):
"""迭代尝试减少颜色数"""
colors_used = len(set(coloring))
# 如果颜色数已经等于最大团大小,无法进一步减少
max_clique = find_max_clique()
if colors_used <= len(max_clique):
return coloring
# 尝试减少一种颜色
target_colors = colors_used - 1
# 重新映射颜色以确保连续
color_map = {}
for i, c in enumerate(coloring):
if c not in color_map:
color_map[c] = len(color_map)
coloring[i] = color_map[c]
# 尝试移除最高颜色
highest_color = target_colors
vertices_with_highest = np.where(coloring == highest_color)[0]
# 临时移除这些顶点的颜色
temp_coloring = coloring.copy()
temp_coloring[vertices_with_highest] = -1
# 尝试用更少的颜色重新着色
for v in vertices_with_highest:
used = [False] * target_colors
for u in adj_lists[v]:
if temp_coloring[u] != -1:
used[temp_coloring[u]] = True
# 寻找可用颜色
available_color = -1
for c in range(target_colors):
if not used[c]:
available_color = c
break
if available_color != -1:
temp_coloring[v] = available_color
else:
# 无法减少颜色
return coloring
# 递归尝试进一步减少
if time.time() - start_time < max_time * 0.95:
return iterative_color_reduction(temp_coloring)
else:
return temp_coloring
# 主算法流程
# 1. 尝试通过图分解求解
decomp_coloring = solve_by_decomposition()
if decomp_coloring is not None:
decomp_colors_used = len(set(decomp_coloring))
if decomp_colors_used < best_color_count:
best_coloring = decomp_coloring
best_color_count = decomp_colors_used
# 2. 应用多阶段混合策略
hybrid_coloring()
# 3. 迭代颜色减少
if time.time() - start_time < max_time * 0.95:
reduced_coloring = iterative_color_reduction(best_coloring.copy())
reduced_colors_used = len(set(reduced_coloring))
if reduced_colors_used < best_color_count:
best_coloring = reduced_coloring
best_color_count = reduced_colors_used
# 确保颜色编号连续且从0开始
color_map = {}
final_coloring = np.zeros_like(best_coloring)
next_color = 0
for i, c in enumerate(best_coloring):
if c not in color_map:
color_map[c] = next_color
next_color += 1
final_coloring[i] = color_map[c]
return final_coloring