人工智能初步——利用随机重启爬山、模拟退火算法求解2n皇后问题

这两天在写AI的课程实验,趁刚刚完结实验代码,脑海中还有些思路,在此简单总结一下。

目录

问题描述

2N皇后问题:给定一个n*n的棋盘。现要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。请尽量快地给出一组可行解。

关于N皇后问题

N皇后问题是一个很经典的问题,在大家最初学习算法的时候都有讨论过。回溯法是经典的解法,但是随着N的增大,其复杂度的增加呈指数增长。哪怕N只是为100,使用回溯解法的话,运行也要相当久的时间。

简单分析

对于2N皇后问题,很多同学的第一想法可能是两次求解N皇后问题,且第二次求解时为摆放位置设限。然而,这种做法不免显得过于繁琐。

我们在这里,不妨先简单地分析了一下几种情况:

1. 当N为偶数时:其实只要求得一组可行解即可,另外一组可行解可以由当前解沿N*N矩阵的中轴线作对称变换得到。因为N为偶数,所以不会存在黑白皇后位置冲突的情况。
  例如:N=4时,求得一组可行解为:3 1 4 2,按着这个思路,变换得到另一组可行解为:2 4 1 3。可以判断,这样的两组解合并起来是符合题目要求的。

2. 当N为奇数时:沿中轴线对称的方式不再可行,因为肯定有一个皇后会落在中轴线上。此时,可以考虑通过中心点作点对称的情况。
  这里要注意,如果求得的可行解存在关于中心点对称的皇后摆放(或有皇后位于中心点),那么此解不合要求,需要重新再求;直到没有两个皇后的位置是关于中心点对称的,另一组解可以通过对当前解关于中心点作点对称变换得到。
  例如,N=5时,求得一组可行解为:2 4 1 3 5,按着这个思路,变换得到另一组可行解为:1 3 5 2 4。可以判断,这样的两组解合并起来是符合题目要求的。

从上可以看出,其实2N皇后问题并不需要二次求解N皇后,在大多数情况下只需求得一组可行解即可。

爬山算法

在介绍爬山算法之前,我觉得很有必要先弄清楚什么是局部搜索。

局部搜索

从数学层面来理解,局部搜索是一种解决最优化问题的启发式算法。

对于某些计算起来非常复杂的最优化问题,比如各种 NP 完全问题,要找到最优解所需要的时间会随问题规模的增大呈指数增长,因此诞生了各种启发式算法来退而求次地寻找局部最优解,而局部搜索算法就是其中的一种算法。

局部搜索算法从某一状态(而不是多条路径)出发,通常只移动到与当前状态相邻的状态。而在典型情况下,搜索的路径是不保留的。尽管局部搜索算法不是系统化的求解方法,但是它有几个关键的优点:

1. 占用很少的内存,通常情况下容量是常数级别的。
2. 经常能在不适合系统化算法的很大或者无限的(连续的)状态空间中快速找出合理的解。

爬山算法简介

爬山算法属于局部搜索算法的一份子,因此是一种解决最优化问题的启发式算法。

在实际运用中,爬山算法不会前瞻与当前状态不直接相邻的状态,只会选择比当前状态价值更好的相邻状态,所以简单来说,爬山算法是向价值增长方向持续移动的循环过程。

由于它的贪婪特性,使得在解决问题中容易陷入局部极大值(Local maxima,指一个比所有邻居状态价值要高但是比全局最大值要低的状态),我们能采取随机重启(Random restart)以及模拟退火(Simulated annealing)的方法来改进。本文的主要涉及的就是这两种算法。

先在这里简单地说一下它们之间的区别,主要在于如何选择下一状态以及如何有效地得到全局最优解:

1. 随机重启爬山算法:  
   求解过程中,当得到了局部极大值时,如果不是全局最优解,则随机生成初始状态,重新求解,直到得到全局最优解。  
2. 模拟退火爬山算法:
   基于随机爬山算法,允许在随机选择相邻状态的时候有概率地选择价值更小的状态。在初期,向低价值状态移动的概率高,随着时间流逝该概率会越来越低。(温度逐渐降低,即"退火"。)

算法实现与关键优化

初始化

不同于一般的随机初始化。我实现时采用的初始化方式为:先依次为每一行的对应列摆上皇后,如第i 行,那么皇后就摆在第i 列,之后再随机选择交换皇后所在列。

这样做的优点是可以保证在任一时刻,每一行每一列都只有一个皇后,大大缩小了搜索范围,节省了程序运行时间。(从我的程序运行耗时可以明显看出这一策略的优越性。)

具体的实验代码如下:

/*初始化函数:在每行每列放置一个皇后*/
void generate_status(int* status) {
    for (int i = 0; i < N; i++) {
        status[i] = i;
    }
    /*随机交换*/
    srand((unsigned)time(NULL));
    for (int i = 0; i < N; i++) {
        int r = rand();
        r = r % N;
        swap(status[r], status[N - r - 1]);
    }
}

评价函数

对于每一个状态,需要有一个评价函数对其进行估值评价。在此,我选用冲突数作为评价指标,存在冲突数越多的状态,其评价就越差。显然,最优解的评价结果为0,即不存在冲突。

为了进一步优化实验运行效果,此函数我设置为内联函数。

具体的实验代码如下:

/*评价函数:返回摆放状态的冲突数*/
inline int evaluate(int* status, CollisionList& collision_list) {
    collision_list.clear();
    int num = 0;
    for (int i = 0; i < N; i++) {
        for (int j = i+1; j < N; j++) {
            int offset = j - i;
            if (abs(status[i] - status[j]) == offset) {
                collision_list.push_back(j);
                num += 1;
            }
        }
    }
    return num;
}

尝试交换函数

对传入的状态进行交换尝试,如果交换后的状态评价结果小于当前传入状态,就进行交换,将新状态返回;否则,不交换,直接返回原先的状态。
  对于模拟退火算法,这里就需要加上一个temperature变量,当邻居状态不是更优,但是温度够高,达到了振荡指标时,也可以进行状态转换。同时,temperature值是在不断减小的。

寻找下一个更优状态的函数

对于传入的状态,不断调用尝试交换函数,直到获得了冲突数更小的新状态,即为一个更优状态,将此状态的冲突数作为返回值返回。

N皇后解法的主函数

这个函数的主要实现的就是调用初始化函数进行初始化,然后持续迭代调用寻找下一个更优状态函数,直到返回的冲突数指标为0时,可以将此状态作为求得的一组可行解再交还给main函数。

算法效率比较

下面就是见证奇迹的时刻啦~~~
  笔者特意写了一份回溯法求解N皇后问题的程序,作为对比参照。

N=10时:


回溯法求解10皇后问题
爬山算法求解10皇后问题

额,好像N设的有点小了。。。。来个大点的。。。。

N=20吧:


爬山算法求解20皇后问题

  什么?回溯法?这。尴尬了。。等了好久都没跑出来结果。。。
  退而求其次,我跑了个N=16的回溯法给大家瞧瞧,不过也是让我足足等了5分多钟:


回溯法求解16皇后问题

可见当N>10以后,爬山算法的优越性尽显无疑,我于是又给它上了几个更大的数,具体的运行效果如下:


爬山算法求解100皇后问题
爬山算法求解300皇后问题
爬山算法求解1000皇后问题

爬山算法 N=1000的耗时也不过是回溯法 N=16情况运行耗时的一半!
  AI的强大之处可以略窥一二了吧~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,082评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,231评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,047评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,977评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,893评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,976评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,605评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,888评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,906评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,732评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,513评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,980评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,132评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,447评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,027评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,232评论 2 339

推荐阅读更多精彩内容