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