遗传算法
原文:https://blog.csdn.net/why_still_confused/article/details/52198767
遗传算法的步骤
(1) 用固定长度的染色体表示问题变量域,选择染色体种群数量为N,交叉概率为C,突变概率为M
(2) 定义适应性函数来衡量问题域上单个染色体的性能或适应性。适应性函数是在繁殖过程中选择配对染色体的基础。
(3) 随机产生一个大小为N的染色体的种群。
(4) 计算每个染色体的适应性。
(5) 在当前种群中选择一对染色体。双亲染色体被选择的概率和其适应性有关。适应性高的染色体被选中的概率高于适应性低的染色体。
(6) 通过执行遗传操作——交叉和突变产生一对后代染色体。
(7) 将后代染色体放入新种群中。
(8) 重复步骤5,直到新染色体种群的大小等于初始种群的大小N为止。
(9) 用新(后代)染色体种群取代初始(双亲)染色体种群。
(10) 回到步骤4,重复这个过程直到满足终止条件为止。
1.编码和解码
编码:将单个染色体设为0或1的字符串,前半段为x1,后半段为x2。
Init.java:
//初始化群体
public class Init {
//初始化一条染色体
public String Single(int GeneLength){
String res = "";
for(int i = 0; i < GeneLength; i++){
if(Math.random()<0.5){
res += 0;
}else {
res += 1;
}
}
return res;
}
//初始化一组染色体
public String[] Group(int GeneLength, int GroupSize){
String[] group = new String[GroupSize];
for(int i = 0; i < GroupSize; i++){
group[i] = Single(GeneLength);
}
return group;
}
}
解码:将x1和x2分开,并转换为10进制。
Decode.java:
//解码
public class Decode {
//单个染色体解码
public double[] decode(String single,int GeneLength){
//二进制数前GENE/2位为x的二进制字符串,后GENE/2位为y的二进制字符串
int a=Integer.parseInt(single.substring(0,GeneLength/2),2);
int b=Integer.parseInt(single.substring(GeneLength/2,GeneLength),2);
double[] x = {-1,-1};//new double[2];
//这里设置x1,x2的区间
x[0] = a * (10.0 - 0) / (Math.pow(2, GeneLength/2) - 1); //x的基因
x[1] = b * (10.0 - 0) / (Math.pow(2, GeneLength/2) - 1); //y的基因
return x;
}
}
2.适应度
Fitness.java:
//适应度
public class Fitness {
//计算单个染色体的适应度
public double fitSingle(String str, int GeneLength){
Decode decode = new Decode();
double[] x = decode.decode(str,GeneLength);
//适应度计算公式 这里我们计算最简单的 f= x+y
double fitness = x[0] + x[1];
return fitness;
}
//计算染色体组的适应度
public double[] fitGroup(String str[], int GeneLength){
double[] fit = new double[str.length];
for(int i = 0; i < str.length; i++){
fit[i] = fitSingle(str[i], GeneLength);
}
return fit;
}
//返回染色体组中适应度最值的序号
public int fitGroupMaxNum(double fit[]){
double max = fit[0];
int index = 0;
for(int i = 0; i < fit.length; i++){
if(fit[i] > max){
max = fit[i];
index = i;
}
}
return index;
}
//返回染色体组中适应度最值
public double fitGroupMax(double fit[]){
double max = fit[0];
for(int i = 0; i < fit.length; i++){
if(fit[i] > max){
max = fit[i];
}
}
return max;
}
}
3.选择
每一代适应度高的染色体被选中繁殖的概率高,用轮盘赌算法解决。
Select.java:
//轮盘赌选择
public class Select {
Init init = new Init();
Fitness fitness = new Fitness();
//轮盘赌算法
public String[] Roulette(String group[], int GeneLength){
//染色体概率数组
double p[] = new double[group.length];
String[] newgroup = new String[group.length];
//适应度的和
double sum = 0;
//计算适应度数组
double[] fit = fitness.fitGroup(group,GeneLength);
//求适应度的和sum
for(int i =0; i < fit.length; i++){
sum += fit[i];
}
//初始化p[]
for(int i = 0; i < fit.length; i++){
p[i] = fit[i] / sum;
}
//求出适应度最大的个体maxStr,它的序号是maxNum
int maxNum = fitness.fitGroupMaxNum(fit);
String maxStr = group[maxNum];
//转动轮盘,适应度大的被选中的概率大
for(int i = 0; i < fit.length; i++){
double r = Math.random();
//累计概率
double q = 0;
//适应度最大的个体直接继承
if(i == maxNum){
newgroup[i] = maxStr;
//将其概率置空
p[i] = 0;
continue;
}
//遍历轮盘,寻找轮盘指针指在哪个区域
for(int j = 0; j < fit.length; j++){
q += p[j];
if(r <= q){
//如果被选中,保留进入下一代
newgroup[i] = group[j];
//将其概率置空
p[j] = 0;
break;
}
newgroup[i] = init.Single(GeneLength);
}
}
return newgroup;
}
}
4.交叉(繁殖)
染色体组依次两两配对,随机选取这两段上的基因的某一点分成两段,给他们的子代。
Cross.java:
//交叉繁殖
public class Cross {
Fitness fitness = new Fitness();
public String[] cross(String[] group, int Genelength, double crossRate){
String temp1, temp2;
int pos = 0;
double[] fit = fitness.fitGroup(group,Genelength);
//计算适应度最大的染色体序号
int maxFitNum = fitness.fitGroupMaxNum(fit);
String max = group[maxFitNum];
for(int i = 0; i < group.length; i++){
if(Math.random() < crossRate){
//交叉点
pos = (int)(Math.random() * Genelength) + 1;
//防止数组越界
//取group[i]的前pos个字段和group[i+1]的从pos处开始的字段作为为temp1的字段
temp1 = group[i].substring(0, pos) + group[(i+1) % group.length].substring(pos);
//同理
temp2 = group[(i+1) % group.length].substring(0, pos) + group[i].substring(pos);
group[i] = temp1;
group[(i+1) % group.length] = temp2;
}
}
group[0] = max;
return group;
}
}
5.变异
随机将单个染色体上的某一位的基因改变(即0变1,或1变0)。
Mutation.java:
//变异
public class Mutation {
//替换String中的指定位、str是要改动的字符串、num是要改动的位、pos是要改动成什么
public String replacePos(String str, int num, String pos){
String temp;
if(num == 0){
temp = pos + str.substring(1);
}else if(num == str.length() - 1){
temp = str.substring(0,str.length() - 1) + pos;
}else{
String temp1 = str.substring(0, num);
String temp2 = str.substring(num + 1);
temp = temp1 + pos + temp2;
}
return temp;
}
//MP = Mutation probability 变异概率
public String[] mutation(String[] group, int GeneLength, double MP){
Fitness fitness = new Fitness();
double[] fit = fitness.fitGroup(group, GeneLength);
int maxFitNum = fitness.fitGroupMaxNum(fit);
String max = group[maxFitNum];
for(int i = 0; i < group.length; i++){
//从[0, GeneLength * group.length]区间随机取数
int n = (int) (Math.random() * GeneLength * group.length);
//取得的染色体数组下标
int chrNum = (int) (n / GeneLength);
//取得的基因下标
int geneNum = (int) (n % GeneLength);
String temp = "";
if(group[chrNum].charAt(geneNum) == '0'){
temp = replacePos(group[chrNum], geneNum , "1");
}else {
temp = replacePos(group[chrNum], geneNum, "0");
}
group[chrNum] = temp;
}
group[0] = max;
return group;
}
}
- Main方法
Main.java:
public class Main {
public static final int GroupSize = 10; //染色体数(群体中个体数)
public static final double MP = 0.15; //变异概率
public static final double crossRate = 0.6; //交叉概率
public static final int iteration = 1000; //迭代次数
public static final int accuracy = 8; //精确度,选择精确到小数点后几位
//求出精度对应的所需基因数
int temp = (int) ((int)Math.log(6)+ accuracy*Math.log(10) );
int GeneLength = temp * 2; //基因数
//输出原群体和适应度,测试用
public void outAll(String[] group){
Fitness fitness = new Fitness();
System.out.println("原群体");
for(String str:group){
System.out.println(str);
}
double fit[] = fitness.fitGroup(group,GeneLength);
System.out.println("原群体适应度");
for(double str:fit){
System.out.println(str);
}
}
//输出适应度最大值,以及返回最优的个体,测试用
public int outMax(String str,String[] group){
Fitness fitness = new Fitness();
double[] fit = fitness.fitGroup(group,GeneLength);
double max = fitness.fitGroupMax(fit);
System.out.println(str+"后适应度最大值为"+max);
return fitness.fitGroupMaxNum(fit);
}
public static void main(String[] args) {
Init init = new Init();
Fitness fitness = new Fitness();
Select rws = new Select();
Cross cross = new Cross();
Mutation mutation = new Mutation();
Decode decode = new Decode();
Main ga = new Main();
String group[] = init.Group(ga.GeneLength,GroupSize); //初始化
ga.outAll(group);
for(int i = 0; i < iteration; i++){
group = rws.Roulette(group, ga.GeneLength); //选择
ga.outMax("选择",group);
group = cross.cross(group,ga.GeneLength,crossRate); //交叉
ga.outMax("交叉",group);
group = mutation.mutation(group, ga.GeneLength, MP); //变异
ga.outMax("变异",group);
System.out.println("");
}
int max = ga.outMax("完成", group);
double best[] = decode.decode(group[max], ga.GeneLength); //解码
double result = fitness.fitSingle(group[max], ga.GeneLength);
System.out.println("x1 = "+best[0]+"\n"+"x2 = "+best[1]);
System.out.println("最小值为"+result);
}
}
- 结果
为了方便,计算的是个很简单的函数 f = x1 + x2 ,区间是(0,10)。最大值肯定是接近20。
运行结果如下:
完成后适应度最大值为19.994716634209887
x1 = 9.994812001823428
x2 = 9.99990463238646
最小值为19.994716634209887