遗传算法初级
遗传算法是一种基于仿生学的计算机算法,通过模拟自然进化和优胜劣汰法则来搜索问题的最优解(我会说这其实就是稍微改良了一下的暴搜?)
它是由美国的J.Holland于1975年提出来的玄学概率学混合暴力搜索方法,广泛适用于寻找算法优解、机器学习、人工生命、自适应自学习算法等等各种领域。
但是要知道,这个玄学的算法在用于寻找算法最优解时有一些限制:该算法最好是不能直接用公式套出最优解的算法。
such as luoguP1018的乘积最大,虽然理论上你可以用遗传来做,但明显这道题用动归就能算出来好不好。。。
像经济预测这类的,根本没有一种算法能够算出最优解(完全符合实际的趋势)的问题,遗传算法的极大优势才会凸显出来
OK现在来讲讲遗传算法是什么
让我们想像有一群山羊,每个山羊跑步的能力不同,有的跑的快,有的跑的慢。这其实就是一个种群。
比如说这群山羊有100只
1 struct goat{
2 int speed;
3 int dna;
4 }f[101];
至于dna是什么待会再说。
山羊每年都会繁殖,且假定每年种群中增加的新生儿都是10个。这样这个种群就会以一个一次函数的形式增长好吧我只是顺便温习前几天学的高中生物
但不幸的是,这群神奇的山羊生活的地方又有一群狼,每年狼都会吃掉羊群中的十只跑得最慢的山羊。所以很明显的,每年山羊种群中跑得最慢的个体被淘汰掉,所以留下来的跑得越来越快,也更有机会能产下后代。慢慢的,整个山羊种群中活下来的都是跑得最快的山羊,以及它们产下的优良子代。
所以整个算法就是:计算每个个体的适应环境程度,然后根据适应度越高繁殖后代概率越大的原则,从群体中选出两个个体作为父方母方产下后代,然后对该后代的基因进行变异。不断重复上述操作,直到你决定停为止。然后选出一个最优个体。
怎么实现呢?
先来说繁殖。
受到人类染色体结构的启发,我们可以设想一下,假设目前只有“0”,“1”两种碱基,我们也用一条链条把他们有序的串连在一起,因为每一个单位都能表现出 1 bit的信息量,所以一条足够长的染色体就能为我们勾勒出一个个体的所有特征。这就是二进制编码法,染色体大致如下:
010010011011011110111110
这就是一只山羊的DNA。(当然是模拟山羊)
应当注意的是,我们要学会分辨个体的特征中哪些比较重要,哪些不大重要。比如说山羊,虽然它头上的角花纹是螺旋形还是条纹形也算它的一个遗传特征,但这跟它跑得快还是跑得慢完全没有关系。对于这种特征,我们就不需要把它编程实现了。
然后来讨论一下有用的特征。DNA就类似于一个二进制集合,01分别表示该特征存在还是不存在。比如01表示一只山羊没有灵活的关节但有四条长腿,10表示山羊的关节很灵活但是腿很短,等等(至于11这种人生赢家和00这样的辣鸡都去死吧)有关于二进制集合的操作我也有发博客。
现在我们有一个父亲0100和母亲1001
对于后代的每一位dna,我们可以抽随机数,表示这一位dna是随他爸爸还是随他妈妈。让我们假设他的运气比较好,第一位随他妈妈,第二位随他爸爸,第三位随他妈妈,第四位又随他妈妈
这样后代的DNA就是1101
当然,只有遗传是不够的,没有变异怎么能算的上是一个好模拟(好你可以闭嘴了)
假设对于山羊的每一位基因,有%0.01的概率,能让该位的1变成0,0变成1。然后这个神奇的后代又足够幸运,刚好在第三位DNA变异了
第一位羊生赢家诞生!
好了现在它的DNA序列是1111,也就是最好状况。假设一个‘1’代表速度+1,那么它现在的速度就是4
然后自然而然的,我们就讲到了父母亲代的选择上。在这里,我们用一个轮盘赌的算法来模拟哪只山羊能被选中。
首先把所有的个体适应度相加作为总适应度sum,然后 随机生成一个1~sum之间的数random
然后从1开始遍历群体数组,每次tot+=f[i]的适应度。当tot>=random的时候,选择当前个体作为遗传亲代。
当然母方也是这么选咯
使用这个轮盘赌算法,你可以发现,适应度越高的个体越容易被选中,但被选中的也不全是适应度最高的个体。因为有一种可能,所有适应度高的个体普遍缺少最后两个优势特征,而一个适应度很低的个体却正好拥有这两个特征
比如说这三个
01101111000
10111111000
00000000111
很明显第一个或者第二个跟第三个个体繁殖都会取到明显的好效果。所以说给别人留台阶就是给自己留后路啊
这就是轮盘赌的意义
好了遗传算法初级部分大概讲完了。布置两道小题目
1.创造一个有1000个个体的种群,并在500次进化中使最优个体的基因尽量接近这串字符DNA码“mynameislife”。
2.试着用遗传算法做01背包。(当然如果你得到的答案是错误的真的不怪你,因为这个问题有最优解算法不适合遗传)发个luogu二维背包链接
本篇博客就讲到这里,谢谢大家