首先,我们定义一下染色体的结构。可以利用JS中的对象,使其有两个属性,一个属性是基因位,一个是该基因对应的适应度函数值。
//定义染色体的结构
var gene = {
"geneBit": undefined, //基因
"fitValue": undefined //该基因对应的适应度函数值
}
接着,初始化染色体。根据题目要求,[0,30]之间的整数,则用5位二进制编码即可表示。
var bit = 5; //基因的位数
// 初始化染色体
function createSingle() {
var str = "";
for (var i = 0; i < bit - 1; i++) {
if (Math.random() < 0.5) {
str += "1";
} else {
str += "0";
}
}
//保证产生的数在[0,30]内
if (str == "1111") {
str += 0;
} else {
if (Math.random() < 0.5) {
str += "1";
} else {
str += "0";
}
}
return str;
}
接着,初始化种群。
var Popnum = 100; //染色体个数
var Pop = new Array(); //种群
// 初始化种群
function createPop() {
for (var j = 0; j < Popnum; j++) {
Pop[j] = new Object();
Pop[j].geneBit = createSingle();
}
}
计算适应度之前,要选择合适的适应度函数。适应度函数具有最大性和非负性两个特点。即适应度函数值越大,其被选择的可能性越大。
我利用了R语言中curve(x*x*x-60*x*x+900*x+100,0,30)画出了该函数图像。
发现其满足以上条件,所以我们选择适应度函数为:x * x * x - 60 * x * x + 900 * x + 100
当然,在计算适应度函数值之前还有进行译码工作,即二进制转化为十进制。
// 计算适应度函数值
function calFitValue(x) {
return x * x * x - 60 * x * x + 900 * x + 100;
}
// 二进制转化成十进制(译码)
function toTen(x) {
return x[0] * 16 + x[1] * 8 + x[2] * 4 + x[3] * 2 + x[4] * 1;
}
//二进制转换为十进制并且计算适应度函数值
function toTenAndCalFitValue() {
for (var j = 0; j < Popnum; j++) {
var tenValue = toTen(Pop[j].geneBit);
Pop[j].fitValue = calFitValue(tenValue);
}
}
体现自然中的优胜劣汰情况。
采用轮盘赌方法进行选择。
//轮盘赌选择法
function selection() {
var totallFitValue = 0; //总的适应度
var choicePro = new Array(Popnum); //每个染色体的适应度值对应的概率
var sumChoicePro = new Array(Popnum); //累计概率
for (var j = 0; j < Popnum; j++) {
totallFitValue += Pop[j].fitValue;
}
for (var j = 0; j < Popnum; j++) {
choicePro[j] = Pop[j].fitValue / totallFitValue;
if (j != 0) {
sumChoicePro[j] = sumChoicePro[j - 1] + choicePro[j];
} else {
sumChoicePro[j] = choicePro[j];
}
}
for (var j = 0; j < Popnum - 1; j++) {
var rand = Math.random();
for (var k = 0; k < Popnum - 1; k++) {
if (rand <= sumChoicePro[k]) {
crossover(k, k + 1); //选择出来的染色体与他下一个进行交叉
break;
}
}
}
}
// 交叉(单点)
function crossover(x, y) {
if (Math.random() < 0.6) { //变异概率为0.6
var str1, str2, tem1 = 0,
tem2 = 0;
var pos = Math.floor(Math.random() * bit);
str1 = Pop[x].geneBit.substring(pos);
str2 = Pop[y].geneBit.substring(pos);
Pop[x].geneBit = Pop[x].geneBit.substring(0, pos) + str2;
Pop[y].geneBit = Pop[y].geneBit.substring(0, pos) + str1;
// 为交叉后得到的个体计算适应度值
tem1 = toTen(Pop[x].geneBit);
Pop[x].fitValue = calFitValue(tem1);
tem2 = toTen(Pop[y].geneBit);
Pop[y].fitValue = calFitValue(tem2);
}
}
模拟遗传中突变的情况。
// 变异
function mutation() {
for (var j = 0; j < Popnum; j++) {
if (Math.random() < 0.01) { //变异概率为0.01
var pos = Math.floor(Math.random() * bit);
var tem = 0;
if (Pop[j].geneBit[pos] == "1") {
Pop[j].geneBit[pos] = "0";
} else {
Pop[j].geneBit[pos] = "1";
}
// 为变异后得到的个体计算适应度值
tem = toTen(Pop[j].geneBit);
Pop[j].fitValue = calFitValue(tem);
}
}
}
当然,还需要记录每次迭代后的最大值。
// 记录最大值
function bestGene() {
bestValue = 0, bestGeneTenValue = 0;
for (var j = 0; j < Popnum; j++) {
if (Pop[j].fitValue > bestValue) {
bestValue = Pop[j].fitValue;
bestGeneTenValue = toTen(Pop[j].geneBit);
}
}
}
Demo可以戳这里看啦
以上也是个人学习中的一些理解与实践,有任何错误,望大家批评指正。