遗传算法(Genetic Algorithm) 是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。
一、一些通用的基本概念:
1、染色体:遗传算法不能直接处理问题空间的参数,必须把它们转换成遗传空间的由基因按一定结构组成的染色体或个体。这一转换操作就叫做编码。一般用二进制编码转换,这个二进制串就是染色体,也就是个体。
2、种群:是由一群由染色体代表的个体组成的群体。初代种群产生之后,逐代演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度大小选择个体,根据随机概率进行组合交叉和变异,产生出新种群。末代种群中的最优个体可以作为问题近似最优解。
二、遗传算法的步骤:
Step1:用固定长度的染色体表示问题变量域,选择种群数量为N,交叉概率Pc(0.4–0.9 ),变异概率Pm(0.01–0.1 )(交叉概率和编译概率都视具体问题而定)
Step2:定义适应性函数,用于描述染色体的性能或适应性
Step3:随机产生大小为N的染色体种群
Step4:计算每个染色体的适应性
Step5:从当前种群选择一对染色体,双亲染色体被选择的概率与其适应性相关,适应性高的容易被选中(轮盘赌法、精英选择法、竞争法)
Step6:按照交叉概率和变异概率进行交叉变异,产生一对后代染色体
Step7:将后代染色体放入新种群中
Step8:重复步骤5-7,直到新种群中染色体数目为N
Step9:用新种群代替旧种群
Step10:跳至步骤4,继续执行,直到满足终止条件
三、代码(注释已经很详细了)
import java.util.Random;
import java.util.Scanner;
public class inheritance {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n=10; //函数 f=x*20-x*x,x[0,15],求解这个函数在定义域上的极大值,0-15正好四位二进制数,做染色体
double pc=0.7,pm=0.001;
int[] su=new int[17];
Chrom[] chroms=new Chrom[n];
Chrom[] tchroms=new Chrom[n];
for(int i=0;i<n;i++) {
int t=(int)(Math.random()*16);//0-15
if(su[t]==0) {
chroms[i]=new Chrom(t,String.format("%04d",Integer.parseInt(Integer.toBinaryString(t))),f(t));//值,二进制字符串,适应性
tchroms[i]=new Chrom(0,"", 0);
su[t]=1;
}else {
i--;
}
}
for(int k=0;k<100;k++){
for(int i=0;i<n;i++) {
System.out.print(chroms[i].n+" ");//查看每一代的个体
}
System.out.println();
setrate(chroms); //计算适应率
select(chroms, tchroms);//选择
overlap(chroms, pc); //交叉
vary(chroms, pm); //变异
}
System.out.println(chroms[0].n);
}
public static int f(int x) {//这是需要求解的函数
return x*20-x*x;
}
public static void get(Chrom chrom1,Chrom chrom2) {//用于复制个体chrom类
chrom1.n=chrom2.n;
chrom1.gene=chrom2.gene;
chrom1.adapt=chrom2.adapt;
chrom1.adaptrate=chrom2.adaptrate;
chrom1.sumrate=chrom2.sumrate;
}
public static void setrate(Chrom[] chroms) {
int sumt=0;
for(int i=0;i<chroms.length;i++) {
sumt+=chroms[i].adapt;//计算适应值
}
double t=0;
for(int i=0;i<chroms.length;i++) {
chroms[i].setrate(sumt);//计算适应性比率
chroms[i].setsumrate(t);//计算累计适应性比率
t+=chroms[i].adaptrate;
}
}
public static void select(Chrom[] chroms,Chrom[] tchroms) {//选择交叉的个体
int i=0;
while(i<chroms.length){
double r=Math.random();
for(int j=0;j<chroms.length;j++) {
if(r<=chroms[j].sumrate) {
get(tchroms[i], chroms[j]);
i++;
break;
}
}
}
for(i=0;i<chroms.length;i++) {
get(chroms[i],tchroms[i]);
}
}
public static void overlap(Chrom[] chroms,double pc) {//交叉操作
int i=0;
while (i<chroms.length-1) {
if (Math.random()<pc) {//小于pc就交叉
int point=(int)(1+Math.random()*3);//1-3
String ts=chroms[i+1].gene.substring(point);
chroms[i+1].gene=chroms[i+1].gene.substring(0,point)+chroms[i].gene.substring(point);
chroms[i+1].reset2(f(chroms[i+1].reset1()));
chroms[i].gene=chroms[i].gene.substring(0,point)+ts;
chroms[i].reset2(f(chroms[i].reset1()));
}
i+=2;
}
}
public static void vary(Chrom[] chroms,double pm) {//变异操作
for(int i=0;i<chroms.length;i++) {
if (Math.random()<pm) {//小于pm才变异
int point=(int)(Math.random()*4);//0-3
if(chroms[i].gene.charAt(2)=='0') {
chroms[i].gene=chroms[i].gene.substring(0,point)+"1"+chroms[i].gene.substring(point+1);
}else {
chroms[i].gene=chroms[i].gene.substring(0,point)+"0"+chroms[i].gene.substring(point+1);
}
chroms[i].reset2(f(chroms[i].reset1()));
}
}
}
}
class Chrom{//个体类
int n;
String gene;
double adapt,adaptrate,sumrate;//适应性 适应性比率 累计适应性比率
public Chrom(int n,String gene,int adapt) {
this.n=n;
this.gene=gene;
this.adapt=adapt;
}
public void setrate(double sumt) {//计算适应性比率
adaptrate=adapt/sumt;
}
public void setsumrate(double adaptrate) {//计算累计适应性比率
sumrate=this.adaptrate+adaptrate;
}
public int reset1() {//用于变异后重置
n=Integer.valueOf(gene,2);
return n;
}
public void reset2(int adapt) {//重新计算适应性
this.adapt=adapt;
}
}
可以看到,运行结果到最后,整个种群基因趋向于10。
四、有话说
本人也是刚学习人工智能,这个代码是自己理解打出来的。可能是因为这个过程本来就具有随机性,不是每次都能得到最优解,这个问题和老师交流过,只有在无数代才能得到最优解,所以运行结果上下浮动应该也是正常的。觉得对您的学习有帮助的话,请点个赞吧!欢迎在评论区指正交流