目录
-
实验目的
- 掌握回溯法算法设计思想。
- 掌握地图填色问题的回溯法解法。
-
实验内容与结果
背景知识:
为地图或其他由不同区域组成的图形着色时,相邻国家/地区不能使用相同的颜色。 我们可能还想使用尽可能少的不同颜色进行填涂。一些简单的“地图”(例如棋盘)仅需要两种颜色(黑白),但是大多数复杂的地图需要更多颜色。
每张地图包含四个相互连接的国家时,它们至少需要四种颜色。1852年,植物学专业的学生弗朗西斯·古思里(Francis Guthrie)于1852年首次提出“四色问题”。他观察到四种颜色似乎足以满足他尝试的任何地图填色问题,但他无法找到适用于所有地图的证明。这个问题被称为四色问题。长期以来,数学家无法证明四种颜色就够了,或者无法找到需要四种以上颜色的地图。直到1976年德国数学家沃尔夫冈·哈肯(Wolfgang Haken)(生于1928年)和肯尼斯·阿佩尔(Kenneth Appel,1932年-2013年)使用计算机证明了四色定理,他们将无数种可能的地图缩减为1936种特殊情况,每种情况都由一台计算机进行了总计超过1000个小时的检查。
他们因此工作获得了美国数学学会富尔克森奖。在1990年,哈肯(Haken)成为伊利诺伊大学(University of Illinois)高级研究中心的成员,他现在是该大学的名誉教授。
四色定理是第一个使用计算机证明的著名数学定理,此后变得越来越普遍,争议也越来越小 更快的计算机和更高效的算法意味着今天您可以在几个小时内在笔记本电脑上证明四种颜色定理。
问题描述:
我们可以将地图转换为平面图,每个地区变成一个节点,相邻地区用边连接,我们要为这个图形的顶点着色,并且两个顶点通过边连接时必须具有不同的颜色。附件是给出的地图数据,请针对三个地图数据尝试分别使用5个(le450_5a),15个(le450_15b),25个(le450_25a)颜色为地图着色。
回溯法算法思想:
基本思想就是按照一定的约束条件沿着某条路径走,如果遇到不符合约束条件的节点就回退,直到找到一条满足条件的路径。解空间可以理解为树状结构,树的每一层填写一个解,如果该解可行时遍历该节点的子节点,如果该解不可行时则不遍历子节点。例如,下图是三个节点三色问题的树形示意图,第i(1 <= i <= 3)层中的每个节点有三个子节点,代表着第i个节点的可能取值。
地图填色中的回溯法:
在地图填色问题中,每一个节点就是一个需要填色的区域,约束条件为相邻的区域所填颜色不能一致,以下示意图可以大概地表示回溯法在地图填色问题中的应用。
由于约束条件需要考虑相邻区域之间颜色是否相等的问题,故可以使用无向图的形式区域之间的关系进行存储。例如以下情况:
伪代码:
对当前算法进行优化:
最少剩余量选择(MRV)
最少剩余量选择就是优先选择剩余未填色节点中可填颜色最少的节点,可以增大该路径是可行解的几率。该方法可以通过对每个节点设置一个剩余可填颜色数量的成员来进行判断。
度最大选择(DH)
度最大选择就是优先选择度最大的节点进行填色,因为度数越大的节点受到其他节点的约束就越大,先填涂其他节点容易导致度数大的节点无可填颜色,延长运行时间。在下图情况中,区域3的度为4,区域4的度为6,故先对区域4进行涂色。
该方法需要在MRV算法之后进行,也就是说先考虑填涂可填颜色最少的节点,当可填颜色相同时,选择其中度数最大的节点进行填色。
最小约束值(LCV)
最小约束值就是在可填颜色中,选择约束最小的值,即对相邻区域的可选颜色影响最小的值。用此方法可以尽快得到可行解从而提高算法效率。如下图所示,往上的分支为选择最小约束值的方法。
向前检验:
向前检验就是当某节点选择了一个颜色后,在相邻节点的可选颜色中将该颜色删去。如果在删除的过程中发现删除后没有颜色可以填涂,就放弃该节点的这个颜色。如下所示,在WA中涂上红色,那么删除NT和SA可选颜色中的红色,在Q中涂上绿色,此时NT和SA中都只剩下蓝色可填,如果在NT中涂上蓝色,那么SA中无可选颜色。所以放弃此路径进行回溯。
约束传递:
约束传递就是在一对相邻区域X -> Y中,对于X的可填颜色中的任一颜色,如果能在Y中找到与该颜色不产生冲突的颜色,则检查X的下一个相邻区域;如果不能,则在X中删去该颜色。
注意:如果在X的可填颜色中删去了某种颜色,则需要对X的相邻区域重新进行检查,并且此时的检查方向为相邻区域K -> X。
以下图为例,当检索到NSW -> SA区域对时,NSW中填涂蓝色时,SA中只能填涂蓝色,此时产生冲突,则在NSW中删去蓝色,并将V -> NSW加入检查队列。此时NSW中的可填颜色只剩下红色,则对于V中填涂红色的情况,NSW中只能填涂红色,此时产生冲突,将V中的红色删去。
约束传递可能会出现无解的情况,如下图所示。因此在使用约束传递提高效率的同时还需要加以判断保证得到可行的解。
颜色轮寻:
颜色轮寻是得到一组可行解后,可以将其余的颜色轮换,得到另外的一组解。假如只有红蓝两种颜色进行填涂,当得到一组可行解后将红色和蓝色调换,得到另一组可行解。如下所示:
小规模数据测试:
对下面这个小规模数据,利用四色填色测试算法的正确性
简化后如下图所示:
其中邻接关系可表示为:
0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 |
1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 |
0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 1 |
0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 |
0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 |
0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
运行程序得到480种填色方案。
假设数字与颜色的对应关系为:1 -> 红色,2 -> 绿色,3 -> 蓝色,4 -> 黄色,抽取方案480进行涂色,结果如下:
该填色方案正确。
再抽取方案288进行试涂,结果如下:
该填色方案正确。算法正确性得证。
对附件中给定的地图数据填涂:
首先去除数据集中的冗余信息,只留下节点之间的邻接关系。
使用文件流对数据集中的信息进行读取,并对节点间的邻接关系进行存储,如下所示:
void getData() {
ifstream ifstream1("D:\\Algorithm\\HW3\\le450_5a.col");
string s;
while (getline(ifstream1, s)) {
// cout << s << endl;
int a = stoi(s);
int b = stoi(s.substr(s.find(" ") + 1));
areas[a].adj[b] = 1;
areas[b].adj[a] = 1;
areas[a].degree++;
areas[b].degree++;
}
}
存储好数据后分别对三个数据集进行测试,数据集le450_5a和le450_15b在较长的时间内程序无响应,对其进行检查发现数据集le450_5a在区域103,184之间反复回溯,le450_15b在316,226之间反复回溯,考虑可能出现无解的情况。
故采用数据集le450_25a进行测试
使用常规回溯法求解,较长时间内程序无法得出可行解,对算法进行优化。使用优化后的算法发现求得可行解 > 100000组,选取10000组可行解计算求出一个解的平均时间,并进行比较。
MRV + DH | MRV + DH + 向前检查 | MRV + DH + 约束传递 |
0.0664005s | 0.0595122s | 0.0869898s |
由结果可以看出,采用MRV + DH + 向前检查的优化算法最快。
随机产生不同规模的图,分析算法效率与图规模的关系(四色)
在随机产生不同图的过程中,固定地图的区域数量,随机产生区域间的邻接关系。产生规模分别为n = 5,8,10的图,统一使用MRV + DH优化算法,统计平均产生一组解的时间,结果如下:
5 | 8 | 10 |
0.00125s | 0.00136706s | 0.00169167s |
由结果可知,图规模越大时,产生一组解所需时间越长。
-
实验总结
通过本实验更深入地理解了四色定理,使用回溯法解决地图填色问题,使用MRV + DH和约束传递,向前检查的优化方式,加快程序的运行时间。