本篇记录的是算法课的一次实验报告,这次实验很有意思,笔者断断续续花了两周时间做实验、与大佬交流经验和写报告。最终实验报告99分 ,得到了老师的肯定,所以请放心阅读。本篇只讲思路,不含代码,也没有图(懒得画),想直接copy的可以离开啦
文章共分为4个部分,分别是回溯法介绍,地图填色问题介绍,剪枝策略的设计,算法效率分析。
做实验前首先要搞懂什么是回溯法,多看看多学学怎么设计剪枝策略之类的知识对做实验有很大帮助
回溯算法是一种遍历问题的整个解空间树,以找到所有可行解或一个最优解的算法。随着问题规模增大,朴素回溯法的效率越来越低。而剪枝策略可以剪掉找不到可行解的分支,减少搜索,提高搜索效率。因此设计好的剪枝策略时提高回溯算法效率的关键。
搞懂回溯法肯定也要搞懂什么是地图填色问题吧,关键不在于理解填色规则的字面意思,而在于要把问题转化为数学问题,便于算法的设计(这点挺重要的,少了报告就不够完整。很多科目的老师们都强调把问题转化为数学问题再编程实现,有点数模的味道)
地图填色问题是指使用不同的颜色为地图不同区域着色,使得具有共同边界的区域颜色不同。由于地图填色与地图区域内部无关,因此每个区域可以看成一个顶点,顶点间用边连接表示这两个区域相邻。于是地图填色问题就转化成了图(数据结构意义的图)填色问题。
设计剪枝策略前,得先整个朴素回溯法的代码,然后设计了新的剪枝策略就往这个代码里加(朴素回溯法比较简单,就不贴代码啦)
然后就是设计剪枝策略,每设计完一个都要利用小规模问题测试正确性,然后尝试三个地图le450_5a、le450_15b、le450_25a的填色,列出填色情况和算法运行效率。这三个图的问题分别为使用5种颜色填涂450个区域,使用15种颜色填涂450个区域,使用25种颜色填涂450个区域,这三个图都不是平面图,平面图只用4种颜色就可以填涂了,这3个图至少得用题目要求的颜色数才能填涂。 有的策略可能跑不出地图le450_15b,因为这个图很怪,非常怪,笔者不清楚它的具体区域分布,但听大佬说,这个图有一个较复杂的完全子图,一般的剪枝策略都要在里面回溯非常多次。(设计完策略然后直接用小规模问题测试正确性是必要的,不保证算法正确性那这个算法再快都是虚的。)
当然你也会问,随便整个小规模问题测的是正确的,但算法其实是有问题的,那是正常的。因为你只测试了若干个小规模问题该算法的正确性,要严格说明算法是正确的,得写个严格的数学证明。笔者菜,就不写证明糊弄人了。
下面列出了较多的策略,有成功的也有失败的,希望能对你有所启发
(1)策略提出原因
搜索顺序对算法效率的影响肯定是有的啦,这个策略是很经典的剪枝策略
(2)策略思路
优先搜索未着色的可选颜色最少的顶点,如果可选颜色数最少的点不止一个,则在这些顶点中优先搜索相邻未着色的点数最多的顶点。就是优先满足约束条件较多的顶点,记为顶点A。先满足A的需求,当A无法着色时就可以剪枝了。若不先满足A,那么等到给其他一些无关紧要的点着色后再来对A着色,然后发现A无法着色就要回溯很多步了,这样算法就慢了。
(3)正确性检验
小规模地图填色测试——过
三个地图着色测试(c++)
地图 | le450_5a | le450_15b | le450_25a |
---|---|---|---|
着色情况 | 成功 | 失败 | 成功 |
运行时间(ms) | 137.463 | - | 19.0438 |
第2个图跑不出来,所以是没法偷懒了,得多整几个策略
(1)策略提出原因
就是尽早剪枝,减少搜索时间,也是很经典的策略
(2)策略思路
在策略1的基础上,记当前着色的点为A,着色后,探测A的邻接点是否有无法着色的情况,有的话A就得换种颜色了
(3)正确性检验
小规模地图填色测试——过
三个地图着色测试(c++)
地图 | le450_5a | le450_15b | le450_25a |
---|---|---|---|
着色情况 | 成功 | 成功 | 成功 |
运行时间(ms) | 135.964 | 34564.7 | 19.1501 |
好耶!加了策略2第2个图就可以跑出来了,虽然用了34秒多,但总算跑出来了。
其实做到这里就可以了,策略1和2都是优秀的策略,但笔者还是觉得不够尽兴,所以又想了些策略,看能不能更快跑出第2个图。但这些策略都是些外门邪道,不能保证算法的正确性,属于投机取巧,当然我也会把它们列出来,兴许会对你有所启发,说不定笔者没整成功的策略你能整的更好
如题是失败合集,所以不测试算法正确性不跑图le450_5a和图le450_25a,只给出图le450_15b的测试,只是想看看能不能更快跑出图2,所以在这一部分笔者就彻底放飞自我了,不考虑策略正确性,只追求快!
(1)策略提出原因
最大完全子图是图中一个很特殊的存在,它的阶数决定了这个图最少需要用多少种颜色填涂。这个策略其实是笔者乱试试出来的,先随便找一个点开始着色,然后使用策略1和2,结果发现找不同的初始点开始着色,运行时间有很大差异,有的点跑的贼快,然后才猜想是不是最大完全子图的原因。
(2)策略思路
在策略1和2的基础上,初始着色点不按照策略1的规则,而是从图的一个最大完全子图里的点里挑,然后才对剩余点使用策略1和2
(3)图le450_15b测试
最大完全子图顶点序号 | 1 | 32 | 43 | 97 | 100 | 153 | 164 | 204 |
---|---|---|---|---|---|---|---|---|
着色情况 | 成功 | 失败 | 成功 | 失败 | 失败 | 失败 | 失败 | 失败 |
运行时间(ms) | 376.736 | - | 503.15 | - | - | - | - | - |
最大完全子图顶点序号 | 215 | 293 | 326 | 334 | 345 | 426 | 432 | |
着色情况 | 失败 | 失败 | 失败 | 失败 | 失败 | 失败 | 成功 | |
运行时间(ms) | - | - | - | - | - | - | 26.6564 |
随便找了一个最大完全子图,把它的所有点都作为初始着色点试几次,然后发现有的点跑的很快很快,爽到了
遗憾的是笔者菜,分析不出哪些点作为初始点会跑的更快,策略3.1也就做不下去了
(1)策略提出原因
回溯法其实有点像深搜,有这么一种情况,当A无法着色时,回溯到上一个着色点B,然而B与A是不相邻的,也就是说无论怎么修改B的颜色,对A的着色都是没有影响的,这时就得考虑回溯到与A相邻的时间最近着色的点
(2)策略思路
思路就如提出原因里所说,当然也是在策略1和2的基础上,但不使用策略3.1
(3)图le450_15b测试
跑图le450_15b用了661.441ms,也是很不错的速度。然后笔者整出这个策略后非常高兴,拿去跑随机生成的图,却发现是有一定概率着色失败的。仔细分析策略提出原因才发现,对于无法着色的点A,回溯到上一个点B时,假设B与A不相邻,B无法直接影响A的着色,但B是有可能影响A的邻接点的着色的,再不行就影响A的邻接点的邻接点的着色……一直套娃,这样B就能间接地影响A的着色 ,所以回溯到相邻的时间最近着色的点就有一定概率剪掉可行解,全部剪完了就着色失败了
使用随机生成的地图分析策略1和策略1、2组合效率,只分析正确的算法,策略3是不稳定的分析效率没有意义
随机地图生成法
以生成能使用4种颜色填涂的图为例
(1)先将图的顶点分成4块,每块的顶点数相同
(2)不同块间随机生成边
(3)这样就得到了能用4种颜色填涂的图
算法效率分析
算法效率与图的顶点数和顶点平均度数有关。毕竟规模越大求解就越慢,度数越大边就越多,可行解就会变少,搜索解空间的效率也会有所改变
(1)对顶点数分析
固定顶点平均度数为30,改变顶点数
顶点数 | 400 | 600 | 800 | 1000 | 1200 | 1400 | 1600 | 1800 |
---|---|---|---|---|---|---|---|---|
策略1着色情况 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 |
运行时间(ms) | 13.8 | 21.1 | 40.9 | 81.3 | 147.7 | 2033.5 | 9902.1 | 61038.2 |
策略1和2着色情况 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 |
运行时间(ms) | 13.8 | 21.5 | 42.1 | 80.4 | 148.2 | 2023.0 | 9802.7 | 60017.3 |
看表可知,顶点数越多,运行时间越慢,顶点数到1600以后运行时间增长极快。然后策略1、2组合在顶点数较多时效率略高于策略1
(2)对顶点平均度数分析
固定顶点数为800,改变顶点平均度数
平均度数 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 |
---|---|---|---|---|---|---|---|---|
策略1着色情况 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 |
运行时间(ms) | 37404.8 | 7622.4 | 303.8 | 70.7 | 59.4 | 39.9 | 30.8 | 30.8 |
策略1和2着色情况 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 |
运行时间(ms) | 37548.4 | 7551.6 | 303.0 | 71.7 | 58.9 | 40.0 | 30.1 | 30.7 |
平均度数 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 |
策略1着色情况 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 |
运行时间(ms) | 31.7 | 31.8 | 33.4 | 35.1 | 36.7 | 38.1 | 39.5 | 42.1 |
策略1和2着色情况 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 | 成功 |
运行时间(ms) | 31.6 | 31.7 | 34.6 | 35.6 | 37.2 | 39.7 | 39.2 | 41.3 |
看表可知,随着顶点数增加,运行时间先减少后增加。与想象的不太一样,笔者还以为运行时间会一直递减,因为度数越大可行解就越少了,就能更快剪枝了,搜索解空间的效率应该会越来越高才对。结果表明不能太依赖惯性思维,事实和想象的是有区别的!
本篇的重心一直放在设计好的策略提高求解出地图填色问题的一个可行解的效率,但其实还可以分析分析求解所有可行解的效率,利用颜色对称和完全子图的性质可以较快求出所有可行解,笔者懒啦,就没动手研究求解所有可行解的问题了,溜啦