遗传算法是一个颇有意思的算法。git
咱们都知道达尔文提出了伟大的“生物进化论”,他证实了全部的生物都不是上帝创造的,而是在遗传、变异、生存斗争和天然选择中,由简单到复杂,由低等到高等,不断发展变化的。也就是:物竞天择、适者生存、优胜劣汰。github
达尔文的进化论给美国的科学家霍兰德留下了深入的印象,但他从计算机科学的角度思考了这个问题,他想到,是否能够像繁育良种马和良种玉米同样繁育程序。这也就是遗传算法的由来。遗传算法借鉴了进化生物学中的一些现象,包括遗传、突变、天然选择以及杂交等。算法
在《复杂》这本书中有个例子,罗比是一个扫地机器人,他生活在一个 10 * 10 格子的世界里,这个世界随机散落着易拉罐,且每一个格子最多只有一个易拉罐。罗比的任务是清理这些易拉罐。世界的周围都是墙壁。函数
罗比的视野是有限的,他只能看到四周格子以及本身所在位置的状况,也就是上下左右和当前位置五个格子的状况。spa
每次清理工做罗比能够执行 200 个动做,动做能够是:向上走,向下走,向左走,向右走,随机一个方向走、捡罐头、不动。每个动做执行完之后都会有一个评分:若是捡罐头时格子中有罐头,那么罐头被捡起,加 10 分。若是捡罐头的时候格子中没有罐头,扣 1 分。若是罗比撞到了墙,扣 5 分。设计
显然,罗比尽量多捡到罐头、别在没罐头的时候去捡、别撞墙,分数就越高。那么,咱们须要设计一套策略,让罗比根据当前所在位置的状态去执行相应的动做,可以拿到最高的分。code
那么,总共有多少种状态?blog
每次罗比看到的格子为:上下左右中,一共 5 个格子,而每一个格子有三种可能性:没罐头、有罐头、墙壁。咱们把没罐头记为 0,有罐头记为 1,是墙壁记为 2。那么状态能够列为:rem
上下左右中 0 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 0 0 1 0 0 0 0 1 1 ... 2 2 2 2 2
显然,一共有 3 * 3 * 3 * 3 * 3 = 243
种状态。字符串
而对于每一种状态,罗比有 7 种动做能够执行:
0: 向上走 1: 向下走 2: 向左走 3: 向右走 4: 随机选取方向走 5: 捡罐头 6: 什么都不干
因此,243 个状态,每一个状态 7 种动做,那么策略的总数是 $7^243$
个。要想遍历全部的策略去求最优解显然是不现实的,在这里可使用遗传算法,来 “进化” 出一个近似最优解。
以前已经提到过了,遗传算法借鉴了天然选择中的思想,在遗传算法中,也有“种群”、“染色体”、“基因”、“杂交”、“变异”等概念。
试想咱们须要获得的策略是什么样子的。罗比须要根据每一种格子的状态去寻求一种动做,在上面已经提到过,格子的状态分别是 00000
、00001
、00002
以此类推。咱们能够发现,这实际上就是全部的 5 位 3 进制数。
那么,罗比的策略就能够用一个字符串表示,字符串的长度是格子状况的总数,每一位表明对应格子状况所须要做出的策略,好比 152455021256444063355141362453351200451455123151604162324266004040256060052004316401456203443334225141156451050235106256354245063143011340422626044356444400300555631325215155436144345164455440161256251412661563442063025020602255536510141514365
。这个策略的第一位是 1,这表明着罗比所处的状况是 00000
时,应该做出动做 1,也就是向下走。相似的,第二位是 5,表明罗比在 00001
时应该捡罐头。
咱们须要经过遗传算法找到最优的策略,这每个策略实际上就表明着个体,咱们经过一个字符串来表明它,这个字符串能够称为染色体(基因组),染色体是基因的集合,种群是染色体的集合。
遗传算法首先须要随机生成一个种群,而后经过“天然选择”来筛选他们,越优秀的个体越容易留下来,并不断繁衍出优秀的子代,这样通过一代又一代的选择,最后留下的个体就是近似最优的。
对应到罗比问题中来,遗传算法过程以下:
初始化种群:随机生成 200 个个体
计算适应度:对 200 个个体进行评分,评分的规则是让罗比打扫 1000 次世界,每次打扫世界都是随机生成的,且每次打扫罗比移动 200 步,最后算出这 1000 次打扫的平均分。
杂交:根据全部个体的得分,随机选取两个个体进行杂交,得分越高的个体越容易被选中。杂交会生成两个子代,将子代放入新种群。重复这个过程,直到新种群的个数达到 200 个。
突变:子代的一个或多个基因会有必定的几率发生突变,发生突变的基因会随机变为其余的动做,好比从 1 变为 6。
迭代:重复以上的过程,直到达到 1000 代。
固然上面的 200、1000 这些数字都是人为设定的,你也能够设定其余的,只要能获得比较好的解就行。
是否是颇有意思,由于咱们根本不用动脑去设计策略,而是所有交给了计算机。固然这里面还有适应度函数的设计、杂交的设计、参数的选定等使人伤脑筋的事情,但遗传算法的过程提及来就是这样。
在步骤 2 里面,咱们对种群中全部个体进行了评分,评分的实现也比较简单,生成一个世界,根据策略在世界内进行 200 个动做,并把这个过程重复 1000 次便可,最后能拿到一个平均分。
举个栗子,假设种群内只有 3 个个体,他们得分分别是 1, 2, 7
,咱们须要进行两次选择,且得分越高的个体越容易被选中。这里使用 轮盘赌 方法来选择个体。
实际上也就是个求几率的问题,三个个体总分为 1 + 2 + 7 = 10
,那么每一个个体占比就能够算出来:0.1, 0.2, 0.7
。而后让每一个个体的几率加上前一个个体的几率,也就是:0.1, 0.3, 1
。
这样随机生成一个 0 到 1 的数,而后从左往右去比较,小于哪一个个体就选择哪一个个体便可。
当出现负数怎么办,好比 -5, 3, 9
,这里能够给每一个元素都加上第一个元素的绝对值再加 1,也就是加 6,转为:1, 9, 15
,而后再像上面那样按轮盘赌便可。
事实上,这里能够有不少种方法能够选择,好比所有调完正数后让每一个元素平方,或者对全部元素加上一个总步数 * 撞墙扣分,表明一次打扫中最多可能扣掉的分。这里选择方法的选取对整个遗传算法影响也是很大的,须要设计一个比较合理的选择方式。
选择出父母以后,须要让父母进行杂交,好比选取某一个位置,取父亲的前半段加上母亲的后半段,以及父亲的后半段加母亲的前半段。
203651 100344 取后三位交叉 203344 100651
交叉算子的方式还有其余更复杂的。
子代有很小的几率会发生基因突变,好比
203344 -> 206344
第三个位置的基因发生变化。
实际上这个参数仍是很麻烦。。我调了很长时间,最后进化了 2000 次,最高得分大概维持在 380 附近,这是种群平均分和种群中最高分随子代数的趋势图,能够看到种群确实在一点一点进化,若是参数更合理一点相信必定能达到几乎满分的得分的~
贴一个 380 分左右的策略:450351253450256256354356265152354252154454454351351464051353450055356644335603132054351253256352104350004552116414256156116103346453105056355052053324226604006321154130114255453253352312102112111412414140255154432364654062360530606611264324300
完整代码在 这里