k图着色 局部搜索算法与模拟退火算法的python实现
文章目录
- 前言
- 如何评价一个解?
- 通过计算出的冲突来找它的邻居解
- 数据的格式
- 局部搜索
- 概述
- 补充
- 代码
- 模拟退火
- 概述
- 补充
- 代码
- 总结以及不足之处
前言
这两种算法就不详细介绍了,流程思路比较简单,而且两者对代码逻辑稍作修改便可互换,但都需要面对的两个主要问题是:
如何评价一个解?
这里使用了颜色集合来先对节点进行归类,对所有的节点随机的赋以颜色(这里用数字做代替),然后再按照颜色对其归类,集合的数量对应为颜色的数量。然后在一个颜色子集合中取所有任意两个点来判断他们是否是连通的即可,若是连通这两个便是冲突的,可将其存储并计数,找到所有的冲突节点和数量,这便是评价解是否优秀的一种方法。
通过计算出的冲突来找它的邻居解
在找到冲突的节点之后,便可以由此来生成邻域解。大致做法是:以先前按照颜色划分的集合为基础,将这个冲突的节点从这个颜色集合中取出,再依次放入其他颜色的节点,这样就生成了k-1个邻域解(设k为颜色个数)。这样遍历所有颜色集合中的所有冲突节点便可生成全部的邻域解。评价出最好的一个邻域解进行下一次迭代即可。
数据的格式
python读取处理数据很方便啦~
20
0: 1 3 4 9 18 7 2 5
1: 16 5 7 2 17
2: 17 5 8
3: 7 18 5 2 11 17 10 13 9 16 4 12
4: 17 19 11 13 16 14 18 8
5: 17 9 15 8 2 10 13 6 3 12 4 14 11 7 0
6: 9 4 12 7
7: 11 16 15 17 6 18 4 8 10 14 1 13 0 19 9
8: 5 4 3 1 7 12 19 0 6 13
9: 12 1 10 17 6 13 0 5 7 11 3 19 2 15 4 18
10: 6 2 18 8 16 11 0 1 17 4 15 3 13
11: 7 17 8 13 12 2 14 9 18 15 19 1 4 0
12: 5 6 15 18 14 9 0 8 19 10
13: 0 19 9 10 2 11 18 15 12 16 1 4 5 7 6 17 14
14: 2 3 11 19 10 18 1
15: 18 8 9 16 2 3 14
16: 1 12 6 0 7 19 17 9 8 15
17: 16 2 1 12 5 7
18: 3 13 6 11 4 19 8 17 16 10 0 7 2 1 12 15 5 9 14
19: 11 1 7
500节点数据:data.txt
局部搜索
概述
这里将先前读取到的数据存储在字典之中,在将它转化为numpy矩阵进行存储,为的是后面方便直接使用numba来加速计算,性能提升的十分明显,这里在get_one_color_set_conflict(graph, color_set)
函数中使用了,有兴趣的可以关闭对比试下效果。
补充
在使用局部搜索算法时可以先假设一个解得序列,以一种方式来评价这个解得优秀性,这里使用冲突节点的多少来进行评判,再根据冲突节点来构造其领域解,如将这个冲突的节点放到其他颜色的集合之中,生成邻域解之后再进行评估,选择邻域解中的最优解,之后进行下次的迭代。
例如在本题目中的图可使用邻接矩阵或者邻接表的形式进行存储,如下:
1:2,3,4
2:1,5,9
3:1,8
4:1,7,8
5:2,6,8
6:5
7:4,8,9
8:3,4,5
9:2,7
可以将颜色用数字来代替:红,绿,蓝->0,1,2
可以使用随机的初始值{0: [1, 3, 5], 2: [2, 4, 8], 1: [6, 7, 9]},计算出冲突的节点,这里使用冲突对来表示各个颜色集合中的冲突{0: [[1, 3]], 1: [[7, 9]], 2: [[4, 8]]},共计有三对6个冲突节点,那么可以构造它的邻域解共计12个:
{
0: [3, 5],
2: [2, 4, 8],
1: [6, 7, 9, 1]
}, {
0: [1, 5],
2: [2, 4, 8],
1: [6, 7, 9, 3]
}, {
0: [3, 5],
2: [2, 4, 8, 1],
1: [6, 7, 9]
}, {
0: [1, 5],
2: [2, 4, 8, 3],
1: [6, 7, 9]
}, {
0: [1, 3, 5, 7],
2: [2, 4, 8],
1: [6, 9]
}, {
0: [1, 3, 5, 9],
2: [2, 4, 8],
1: [6, 7]
}, {
0: [1, 3, 5],
2: [2, 4, 8, 7],
1: [6, 9]
}, {
0: [1, 3, 5],
2: [2, 4, 8, 9],
1: [6, 7]
}, {
0: [1, 3, 5, 4],
2: [2, 8],
1: [6, 7, 9]
}, {
0: [1, 3, 5, 8],
2: [2, 4],
1: [6, 7, 9]
}, {
0: [1, 3, 5],
2: [2, 8],
1: [6, 7, 9, 4]
}, {
0: [1, 3, 5],
2: [2, 4],
1: [6, 7, 9, 8]
}
依次对这些邻域解进行评估,选择出当前邻域中最优的那一个解,也就是冲突数量最少的那个,再使用该解进行下一次迭代 ,直至求出0冲突的全局最优解。
不算伪代码的伪代码–>
使用局部搜索算法来求解3色最优解要花费大量的时间,其命中3色最优解的概率很小,非常容易陷入局部最优当中,在几百轮的(断断续续)重启计算后只得出了一次最优三色解。
代码
import random
import timeimport numba as nb
import numpy as npdef get_graph(path):g = {}f = open(path, 'r')f.readline()data = f.readlines()for i in data:i = i.split()node = int(i.pop(0)[:-1])i = list(map(int, i))# print(node[:-1],i)g[node] = i# print(g)return gdef get_random_solution(color_num, city_list):c = {}city_num = len(city_list)random_color_list = [random.randrange(0, color_num) for i in range(city_list[0], city_list[-1] + 1)]for i in range(city_num):c.setdefault(random_color_list[i], [])c[random_color_list[i]].append(city_list[i])# print(c)return cdef get_one_color_set_conflict1(graph, color_set): # 检查当前颜色集合中各个城市之间的冲突color_size = len(color_set)conflict_pairs = []count = 0for i in range(color_size - 1):g_list = graph[color_set[i]]# print(g_list)# g_list=np.array(g_list)# print(g_list)for j in range(i + 1, color_size):if color_set[j] in g_list:conflict_pairs.append([color_set[i], color_set[j]])count += 1# print(color_set[i],color_set[j])return count, conflict_pairs@nb.njit()
def get_one_color_set_conflict(graph, color_set): # 检查当前颜色集合中各个城市之间的冲突color_size = len(color_set)conflict_pairs = []count = 0for i in range(color_size - 1):for j in range(i + 1, color_size):if graph[color_set[i], color_set[j]] == 1:conflict_pairs.append([color_set[i], color_set[j]])count += 1# print(color_set[i],color_set[j])return count, conflict_pairsdef get_conflict_count(graph, color_solution): # 对每个颜色解决方案判断冲突conflict_pairs = {}count = 0# print("conflict pairs: ") #!!!!!for i in range(len(color_solution)):try:# color_solution_i_t = get_np_array_color_solution()t, conflict_pairs[i] = get_one_color_set_conflict(graph,np.array(color_solution[i]) )count += texcept KeyError:print("随机生成的解没有覆盖所有颜色,重新运行生成随机解!")exit(-1)# print(count,"对冲突:",conflict_pairs) #!!!!!return count, conflict_pairsdef get_neighbors(color_solution, conflict_pairs):color_num = len(color_solution)neighbors_set = []count = 0for k, v in conflict_pairs.items():# print(k,v)for i in v:other_color_list = [m for m in range(color_num)]other_color_list.remove(k)for m in other_color_list:for j in range(2):# neighbor = copy.deepcopy(color_solution)neighbor = {}for x, y in color_solution.items():neighbor[x] = y[:]neighbor[k].remove(i[j])neighbor[m].append(i[j])# print(neighbor)count += 1neighbors_set.append(neighbor)# print(count,neighbors_set) #!!!!!return neighbors_setdef iteration(graph, color_solution):current_conflict_num, current_conflict_pairs = get_conflict_count(graph, color_solution)best_num = current_conflict_numbest_solution = color_solutionprint("冲突数:", current_conflict_num, "\n当前解:", color_solution, "\n冲突对:", current_conflict_pairs) # *****neighbors = get_neighbors(color_solution, current_conflict_pairs)# print("邻居:",len(neighbors))for i in neighbors:# print("当前解:",i) #!!!!!num, conflict_pairs = get_conflict_count(graph, i)if num < best_num or num == 0:best_num = numbest_solution = i# print("best: ",best_num,best_solution) #!!!!!return best_num, best_solutiondef graph_to_mat(graph):city_num = len(graph) + 1m = np.zeros((city_num, city_num))for k, v in graph.items():for i in v:m[k, i] = 1# print(m[498,:])return mdef run(graph, color_num):min = 999not_change_count = 0city_list = list(map(int, list(graph.keys())))color_solution = get_random_solution(color_num, city_list)while len(color_solution) < color_num:print("随机生成的解没有覆盖所有颜色,重新运行生成随机解!")color_solution = get_random_solution(color_num, city_list)print("random color solution: ", color_solution)graph = graph_to_mat(graph)for i in range(iter_num):print("当前第", i + 1, "轮,迭代开始:") # ******conflict_num, color_solution = iteration(graph, color_solution)if conflict_num == 0:print("!!!最佳解:", color_solution)return Trueif conflict_num < min:min = conflict_numelse:not_change_count += 1if not_change_count == conflict_num * color_num * 2 + 1:print("最小冲突数:", min, " 目前无解,提前结束。")return Falsereturn Falseif __name__ == '__main__':g = get_graph(r'data.txt')iter_num = 200color_num = 4flag = Falsel1 = []# while flag==False:for run_count in range(1, 888):t1 = time.time()flag = run(g, color_num)t2 = time.time()l1.append(t2 - t1)print("第", run_count, "次用时:", l1[run_count - 1])print("当前已使用时间:", l1, "总用时:", sum(l1))if flag:breakelse:print("重启,第", run_count + 1, "次启动,重新选择初始解。")
模拟退火
概述
将上面局部搜索的代码按照模拟退火算法的过程稍加修改下就能凑活用了,有些地方没有太细致的修改,但是也跑出很不错的效果。2秒就得出了500节点3着色最优解,确实比局部搜索给力。这里我用的是T=10,alpha=0.1,瞎选的但是出结果速度很快,较低的T和alpha可使结果快速进行收敛。
补充
通过上面使用局部搜索算法对k图着色的分析,计算冲突和计算邻域解的方法在模拟退火算法中是一致的,所以这里不再做赘述。
对于模拟退火算法需要设置一个退火时的温度和每次温度下降率,T和α,在该温度下进行迭代求解。这里的迭代函数便和局部搜索算法中有所不同,这里取优解的条件变成了两个:一个是只要该解冲突数高于某个邻域解,则将其输出为优解。第二个条件是以某个概率接受比原来差的解,温度高的时候接受的概率越大,这样使得可以跳出局部最优,找到全局最优解。
代码
import random
import timeimport numba as nb
import numpy as npdef get_graph(path):g = {}f = open(path, 'r')f.readline()data = f.readlines()for i in data:i = i.split()node = int(i.pop(0)[:-1])i = list(map(int, i))# print(node[:-1],i)g[node] = i# print(g)return gdef get_random_solution(color_num, city_list):c = {}city_num = len(city_list)random_color_list = [random.randrange(0, color_num) for i in range(city_list[0], city_list[-1] + 1)]for i in range(city_num):c.setdefault(random_color_list[i], [])c[random_color_list[i]].append(city_list[i])# print(c)return cdef get_one_color_set_conflict1(graph, color_set): # 检查当前颜色集合中各个城市之间的冲突color_size = len(color_set)conflict_pairs = []count = 0for i in range(color_size - 1):g_list = graph[color_set[i]]# print(g_list)# g_list=np.array(g_list)# print(g_list)for j in range(i + 1, color_size):if color_set[j] in g_list:conflict_pairs.append([color_set[i], color_set[j]])count += 1# print(color_set[i],color_set[j])return count, conflict_pairs@nb.njit()
def get_one_color_set_conflict(graph, color_set): # 检查当前颜色集合中各个城市之间的冲突color_size = len(color_set)conflict_pairs = []count = 0for i in range(color_size - 1):for j in range(i + 1, color_size):if graph[color_set[i], color_set[j]] == 1:conflict_pairs.append([color_set[i], color_set[j]])count += 1# print(color_set[i],color_set[j])return count, conflict_pairsdef get_conflict_count(graph, color_solution): # 对每个颜色解决方案判断冲突conflict_pairs = {}count = 0# print("conflict pairs: ") #!!!!!for i in range(len(color_solution)):try:# color_solution_i_t = get_np_array_color_solution()t, conflict_pairs[i] = get_one_color_set_conflict(graph, np.array(color_solution[i]))count += texcept KeyError:print("随机生成的解没有覆盖所有颜色,重新运行生成随机解!")exit(-1)# print(count,"对冲突:",conflict_pairs) #!!!!!return count, conflict_pairsdef get_neighbors(color_solution, conflict_pairs):color_num = len(color_solution)neighbors_set = []count = 0for k, v in conflict_pairs.items():# print(k,v)for i in v:other_color_list = [m for m in range(color_num)]other_color_list.remove(k)for m in other_color_list:for j in range(2):# neighbor = copy.deepcopy(color_solution)neighbor = {}for x, y in color_solution.items():neighbor[x] = y[:]neighbor[k].remove(i[j])neighbor[m].append(i[j])# print(neighbor)count += 1neighbors_set.append(neighbor)# print(count,neighbors_set) #!!!!!return neighbors_setdef iteration(graph, color_solution, T):current_conflict_num, current_conflict_pairs = get_conflict_count(graph, color_solution)best_num = current_conflict_numbest_solution = color_solution# print("冲突数:", current_conflict_num, "\n当前解:", color_solution, "\n冲突对:", current_conflict_pairs) # *****neighbors = get_neighbors(color_solution, current_conflict_pairs)# print("邻居:",len(neighbors))for i in neighbors:# print("当前解:",i) #!!!!!num, conflict_pairs = get_conflict_count(graph, i)delta = num - best_numif delta < 0 or np.exp(-delta / T) > np.random.rand(1):best_num = numbest_solution = i# print("best: ",best_num,best_solution) #!!!!!return best_num, best_solutiondef graph_to_mat(graph):city_num = len(graph) + 1m = np.zeros((city_num, city_num))for k, v in graph.items():for i in v:m[k, i] = 1# print(m[498,:])return mdef run(graph, color_num):# min = 999# not_change_count = 0T = 100count = 1alpha = 0.98city_list = list(map(int, list(graph.keys())))color_solution = get_random_solution(color_num, city_list)while len(color_solution) < color_num:print("随机生成的解没有覆盖所有颜色,重新运行生成随机解!")color_solution = get_random_solution(color_num, city_list)print("random color solution: ", color_solution)graph = graph_to_mat(graph)while T > 0.01:for i in range(iter_num):# print("当前第", i + 1, "轮,迭代开始:") # ******conflict_num, color_solution = iteration(graph, color_solution, T)if conflict_num == 0:print("!!!最佳解:", color_solution)return True# if conflict_num < min:# min = conflict_num# else:# not_change_count += 1# if not_change_count == conflict_num * color_num * 2 + 1:# print("最小冲突数:", min, " 目前无解,提前结束。")# return FalseT = T * alphacount += 1print("------------第", count, "次降温:", T)return Falseif __name__ == '__main__':g = get_graph(r'data.txt')iter_num = 200color_num = 3flag = Falsel1 = []# while flag==False:for run_count in range(1, 2):t1 = time.time()flag = run(g, color_num)t2 = time.time()l1.append(t2 - t1)print("第", run_count, "次用时:", l1[run_count - 1])print("当前已使用时间:", l1, "总用时:", sum(l1))if flag:breakelse:print("重启,第", run_count + 1, "次启动,重新选择初始解。")
总结以及不足之处
- 里面有些许小bug未进行修复。例如说在模拟退火中若遍历完所有的邻居都没有找到一个结果,这时函数没有进行返回(None?),后面的代码就会无法继续向下运行。
- 对非常规的随机解处理方式不恰当,例如说某个解中没有该颜色的节点,这会导致后面字典索引的异常。
- 计算冲突节点的时候使用的是冲突对,如[1,2],[2,3],这样在计算邻域解的时候会产生额外重复的邻域解,进行不必要的计算,浪费了时间。
- numba,yyds!
k图着色 局部搜索算法与模拟退火算法的python实现相关推荐
- k图着色 遗传算法的简单python伪代码
文章目录 概述 python伪代码 概述 该问题中所使用到的部分函数与知识与局部搜索.模拟退火中的相同,参照k图着色 局部搜索算法与模拟退火算法的python实现 遗传算法的整体思路比较简单,在解决图 ...
- 模拟退火算法理论+Python解决函数极值+C++实现解决TSP问题
简述 算法设计课这周的作业: 赶紧写了先,不然搞不完了. 文章目录 简述 算法理论部分 变量简单分析 从状态转移概率到状态概率 推导 理解当温度收敛到接近0的时候,收敛到结果 理论部分的后记 pyth ...
- 局部搜索法求解K图染色问题(java版)
局部搜索法:K图着色问题 问题描述 问题分析: 伪代码: 简单实例 局限性 图节点文本文件 具体实现 问题描述 对于文件给出的一个有 500 个节点的图形(n500),利用局部搜索算法,求最 少可用几 ...
- matlab最短路径问题(旅行商模型)—模拟退火算法、禁忌搜索算法解决中国省会间最短路径问题
matlab最短路径问题(模拟退火算法.禁忌搜索算法) 模拟退火算法 禁忌搜索算法 模拟退火算法 %%% 模拟退火算法源程序 % 此题以中国31省会城市的最短旅行路径为例 % clear;clc; ...
- 模拟退火算法学习笔记
0 基本术语介绍 (1)组合优化(Combinatorial Optimization) 组合优化问题的目标是从组合问题的可行解集中求出最优解,通常可描述为:令Ω={S1,S2,-,Sn}为所有状态构 ...
- JAVA用爬山法解决八皇后问题_局部搜索算法.ppt
局部搜索算法 人工智能原理第2章 搜索技术(下) 本章内容2.1 搜索与问题求解2.2 无信息搜索策略2.3 启发式搜索策略2.4 局部搜索算法2.5 约束满足问题2.6 博弈搜索参考书目附录 A*算 ...
- TSP_旅行商问题 - 模拟退火算法(三)
一.前言 [旅行商问题]旅行商问题(TravelingSalesmanProblem,TSP)是一个经典的组合优化问题.经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市 ...
- 模拟退火算法(Simulated Annealing,SA)的全面讲解及python实现
https://blog.csdn.net/Trisyp/article/details/104953406 抽象来源:美国物理学家Metropolis等人在1953年发表研究复杂系统,计算其中能量分 ...
- 基于Python实现的模拟退火算法
模拟退火算法 摘要 该项目主要是利用局部搜索算法(LS)和模拟退火算法(SA)解决 TSP 问题.先是使用 LS 求解 TSP 问题,再尝试 SA 问题,比较两者,在效率上 SA 更占有.最后再在 L ...
最新文章
- 如何用c语言打出 * * * * * * * * * * * * *?
- [转]oracle中查询指定行数的记录
- springboot 详解 (四)redis filter
- 【数据结构与算法】之深入解析“自由之路”的求解思路与算法示例
- SAP Cloud for Customer 技术概述
- python 安装 Pyside 出现 “Failed to find the MSVC compiler version 10.0 on your system”
- 详解机器视觉照明重点内容
- CentOS下ELK收集Nginx日志
- vc ado连接mysql_VC用Ado接口连接和使用数据库及注意事项
- SQL SERVER2008 打开脚本总是报“未能完成操作,存储空间不足”
- java时间段的查询_JAVA实现按时间段查询数据操作的方法
- 地摊经济:78岁高龄老人摆地摊的背后,蕴藏着9000万老年再就业市场的巨大商机
- flashFXP V4.0 烈火汉化绿色版
- 小米4 miui专用 Xposed安装器86版
- 机器人项目:智能寻迹小车
- android insert方法,史上最精炼android四大组件基础总结(忘记了的可以过一遍)
- windows服务器双网卡修改默认路由,windows下双网卡双网关的设置
- Hyperf 热更新Watcher
- 在html代码中加广告
- 使用深度优先搜索算法解决迷宫问题