久远的记忆,发上来和有缘人分享一下,格式有些乱掉了。。。
内部交流系列二
遗传算法简单入门
对遗传算法感兴趣,一方面源自中学生物,另一方面,进化计算,个人认为也是一种趋势,多少需要了解一点。
神经网络让人流连忘返,乐此不疲的构造各种训练样本,然后看看网络的反应是如何的;如果说用神经网络可以构建一个脑,那么遗传算法帮助下,我们则可以创建一个全新的世界。
1、算法步骤
初始化第一代
适合度分等
选择(赌轮选择法)
交叉变异(杂交,杂交变异率)
随机突变(突变率)
不断演化
2、算法步骤简单说明
说的很玄乎,实际上这个算法的思路很好理解:
(注:如果有疑问,多看看括号里面的话,对比思考一下,如果还有疑问,说明解释的不够清晰,自己再查查资料啦^_^)
1、针对问题,构造问题的解为二进制数据串,算法中看做一个染色体串
(人的基因序列)
2、初始化第一代的个体染色体群
(new 100个亚当、100个夏娃,上帝只造了一对,很冒险的说,还是仿造咱们老古人,一百对童男童女渡船到某个岛上。。。 。。。扯远了,come back)
3、不断循环演化出新一代染色体群,直到找出一个解(最重要的部分,一个时代epoch)
(怎么说的来着,子子孙孙无穷溃也,可能直到地球毁灭吧)
(注:也有可能算法不收敛,即找不到解,要设定一个最大演化次数)
(1)检查每一个个体,看看解决问题的能力怎么样,对其打分,即适应度分等
(对于控制人类演化的大神来说,要清楚每个亚当和夏娃的综合能力怎么样,以便使下一代得到好的遗传基因的概率最大化)
(2)从当前个体中选出若干个体,作为下一代的母染色体
这里常用的选择方式是“赌轮选择法 roulette wheel selection”,也就是说,适应度越高,被选中的概率越大,注意不是适应度最高的一定被选中,而是说概率最大。
(夏娃竞争超女,同时亚当进行高考相亲,实力最强的晋级几率最大,也难保有运气好的黑马让它阴沟里翻船)
(3)按照预定的杂交率(crossover rate),从每个选中染色体的一个随机确定的点上进行杂交。
(计算机世界貌似不太好处理这一步,实际上,就是生个娃下个崽的事情)
(衍生一下,其实用代码处理这个事情相当容易且强大,如果思维够开阔的话,就会慢慢发现这个算法的魅力:下一代不一定只能由一个母体一个父体杂交创建,综合多个母体父体的优秀基因也许会更好;染色体不一定要对位交换基因序列,某一小段优秀基因,在其它位置也许也是表现很优秀的;变异率不一定是比较低才好,整代表现差的时候,变异率设为很高也许能让这一代人脱胎换骨)
(4)随机突变按照预定的变异率(mutation rate),把个体染色体相应的位进行翻转(flip)
(比较好理解,就不类比了)
(5)重复步骤(2)、(3)、(4),直到新一代的数量达到上一代的数量
(下一代的数量不能太少,算法中保持相同比较好处理,也不知道要演化多少代)
3、算法源代码
模拟一个花朵的演化过程:
1、这个世界的环境参数:
private int flowerNumber; // 种群数量(一代中的花朵数量)
private Random r; // 决定种群初始状态,随机突变等随机过程以及随机结果
private int[] temperature; // 每朵花的最佳生长温度
private int[] water; // 最佳水分
private int[] sunlight; // 最佳光照
private int[] nutrient; // 最佳养分
private int[] beneficialInsect; // 对它有益的虫子(编号?数量?)
private int[] harmfulInsect; // 对它有害的虫子(编号?数量?),注意作者这里是举例多个参数,明白意思即可,别抠字眼哦
// 当前的环境条件
private int currentTemperature;
private int currentWater;
private int currentSunlight;
private int currentNutrient;
private int currentBeneficialInsect;
private int currentHarmfulInsect;
2、第一代花朵的当前生存环境采用随机值:(多数情况下,根据实际问题设定一个初始值)
// [0,75)之间
currentTemperature = r.nextInt(75);
currentWater = r.nextInt(75);
currentSunlight = r.nextInt(75);
currentNutrient = r.nextInt(75);
currentBeneficialInsect = r.nextInt(75);
currentHarmfulInsect = r.nextInt(75);
3、第一代花朵中,每朵花的最佳环境参数,也采用随机值:(多数情况下,根据实际问题设定一组经验值)
for(int i = 0; i < flowerNumber; i++){
temperature[i] = r.nextInt(75);
water[i] = r.nextInt(75);
sunlight[i] = r.nextInt(75);
nutrient[i] = r.nextInt(75);
beneficialInsect[i] = r.nextInt(75);
harmfulInsect[i] = r.nextInt(75);
}
4、找出最适应当前环境的花朵:(具体问题不同,这里的策略可能不同,每个条件的权重也可能不一样等等)
// 花朵适应度函数,找出长的最高的
public int fitness(int flower){
int theFitness = 0;
theFitness = Math.abs(temperature[flower] - currentTemperature);
theFitness += Math.abs(water[flower] - currentWater);
theFitness += Math.abs(sunlight[flower] - currentSunlight);
theFitness += Math.abs(nutrient[flower] - currentNutrient);
theFitness += Math.abs(beneficialInsect[flower] - currentBeneficialInsect);
theFitness += Math.abs(harmfulInsect[flower] - currentHarmfulInsect);
return theFitness;
}
5、最重要的时代函数:
// 花朵演化函数( 没有贴源代码,这里主要是几个步骤比较重要,实现起来不尽相同 )
public int evolve(){
// 首先找出适合度最小的成员,即误差最大的
// 把适合度最小的成员的特征,重新赋值,即丢弃最不合适的基因
// 交叉变异,随机混合花群特征
// 以百分之一的概率随机突变,这个突变概率太大,主要是演示思想
// 返回新一代中,适应度最高的花朵
// 每一代都会淘汰部分适应度最低的花朵,以使得总体基因的水平逐步提高
}
6、演示Main()方法
while(i < 100000){ // 设定最多演化的次数,超过这么多代还不满足适应度需求,视为不收敛,说明第一代的基因和环境条件差距过大
Thread.sleep(1000);
System.out.print("\n第 " + i++ + "代花朵\t");
best = flowerWorld.evolve(); // 演化一代,得到最适应环境的花朵
if(flowerWorld.fitness(best) < 50) // 如果已经满足适应度需求
break;
}
附注:
这是一个思想简单功效强大的算法,简洁而博大,极好的探索素材^_^
4、Java类库:
JGAP,sourceforge上面开源项目,已经发展了比较久
5、推荐书籍
<游戏开发中的人工智能><AI for Game Developers>(David M. Borug & Glenn Seemann)
<游戏编程中的人工智能技术><AI Techniques for Game Programming>(Mat Buckland)