一、爬山法
爬山法就是完全的贪心算法,每一步都选最优位置,可能只能得到局部最优解。本实验对普通爬山法进行了简单的优化,采用了传统爬山法的变种——随机重启爬山法,当爬山步数超过一定值时,会重新打乱棋盘,重新“爬山”。
- 适应度函数:冲突皇后的总对数
- “爬山”:每一步就是将棋盘的某一行的皇后移动到最优的位置,即该位置让冲突的皇后对数最少,即适应度函数值最小。
- “不断地爬山”: 循环对棋盘的每一行进行爬山直到该棋盘状态下适应度函数值为0。
爬山法基本算法框图如下:
核心代码实现如下:
以上代码搭配注释可看出基本算法思想,实际函数实现比较简单,源码见本文最后。
二、模拟退火算法
模拟退火算法本质也是一种贪心算法,只不过以一定的概率来接受更差的状态。这种概率会随着移动变得越来越小,好处是可能会让算法跳出局部最优解,从而得到全局最优解。
- 适应度函数:冲突的皇后对数之和。
- “模拟退火的过程”就是对棋盘的某一行进行最佳位置的选择。具体降温过程是:设置一个最高温度Max,最低温度Min,以及降温速率r。某一温度T从温度Max以速率r降温到Min后则返回最终选择的皇后位置。
- 具体的选择是,在每一次降温过程中,先比较相邻两个皇后位置对应的状态的适应度函数值h(x),设能量差为dE = h_new(x) – h_old(x)。如果dE <= 0,则接受该新的状态,继续比较相邻状态;如果dE > 0, 则以概率P(dE)= exp(-dE/rT)接受该移动。当该行比较出最优位置后,重新降温,达到最小温度后得出该行移动的位置。
核心代码如下:
三、遗传算法
遗传算法就是模拟自然选择的过程,将许多棋盘视为一个种群,每一个种群对应每一个个体。每两个个体进行结合产生一个新的个体,多次结合产生的新个体形成新的种群,然后再次繁殖,直到出现有适应度最高的个体。
本算法中适应度函数为每个棋盘个体中不冲突的皇后对数。
遗传算法有三个基本操作:选择,交叉,变异。
- 选择:选择一些个体来产生下一代。一种常用的选择策略是 “比例选择”,也就是个体被选中的概率与其适应度函数值成正比。假设群体的个体总数是M,那么那么一个体Xi被选中的概率为f(Xi)/( f(X1) + f(X2) + …….. + f(Xn) ) 。比例选择实现算法就是所谓的“轮盘赌算法”。
- 交叉: 两个棋盘交换部分的行得到一个新的物种,本次交叉采用两个变异点,交换两点之间的办法。
- 变异:随机挑选一个行的位置,随机改变该行皇后的位置。
- 优化:主要可以有两个优化,
- 每次产生的后代个体的适应度总是要不比父母个体差,由更优的种群产生的后代。就是说产生子代适应度一定要优于或者等于父代,不然就继续随机选择;
- 当种群适应度处于稳定的时候,会适当提升变异率,以制造更优的个体出现。这样求解的效率会更好。
伪代码如下:
/*
* Pc: 交叉发生的概率
* Pm: 变异发生的概率
* M: 种群规模
* G: 终止进化的代数
* Tf: 进化产生的每一个个体的适应度函数超过Tf,则可终止进化
*/
初始化Pm,Pc,M,G,Tf等参数。
随机产生第一代种群 Pop。
do{
计算种群Pop中每一个个体的适应度函数值F(i)。
初始化空种群newPop
do{
根据适应度大小,按比例选择的方法从种群Pop中选出2个个体作为Parents
if(random(0, 1) < Pc){
对个体parents按交叉概率Pc执行交叉操作得到个体son
// son个体 一个或者两个都行
}
if(random(0, 1) < Pm){
对son个体按变异概率Pm执行变异操作
}
if(F(son) > F(parents)){
将son个体加入种群newPop中
}
else{
适当提高Pm变异率。 // 根据实际情况而定。
}
}while(M个子代被创建)
Pop 被 newPop取代
}while(种群中存在一个适应度达到最优的个体)
三个算法具体实现代码在我的github上 https://github.com/JoshuaQYH/AI-Course-code/tree/master,就不贴了。
看懂伪代码,理解算法思想后实现基本没什么难度了吧。
最后有什么说的不对,做的不好的欢迎指教和讨论哈~