遗传算法详解及java实现

时间:2020-12-17 11:12:27

转载请注明出处:http://blog.csdn.net/tyhj_sf/article/details/53321527

原理

为更好地说明和理解遗传算法的原理及运算过程,下面结合例子模拟遗传算法的各个主要执行步骤。

例:求下述二元函数的最大值:
遗传算法详解及java实现
(1) 个体编码
遗传算法的运算对象是表示个体的符号串,所以必须把变量 x1, x2 编码为一种符号串。本题中,用无符号二进制整数来表示。 因 x1, x2 为 0 ~ 7之间的整数,所以分别用3位无符号二进制整数来表示,将它们连接在一起所组成的6位无符号二进制数就形成了个体的基因型,表示一个可行解。
例如,基因型 X=101110 所对应的表现型是:x=[ 5,6 ]。个体的表现型x和基因型X之间可通过编码和解码程序相互转换。
本步骤对应的Java代码:

    /**基因长度*/
public static final int GENE_LENGTH = 6;
/**基因对应的数值上限,由基因 的位数决定*/
public static final int NUM = 1 << GENE_LENGTH;

以及:

 /** 
* @param size
* @Description: 初始化基因长度
*/

private void initGeneSize(int size) {
if (size <= 0) {
return;
}
gene = new boolean[size];
}
/** 
* @param size
* 随机生成基因序列
*/

public Chromosome(int size) {
if (size <= 0) {
return;
}
initGeneSize(size);
for (int i = 0; i < size; i++) {
gene[i] = Math.random() >= 0.5;
}
}

(2) 初始群体的产生
遗传算法是对群体进行的进化操作,需要给其淮备一些表示起始搜索点的初始群体数据。
本例中,群体规模的大小取为4,即群体由4个个体组成,每个个体可通过随机
方法产生。如:011101,101011,011100,111001

本步骤对应的Java代码:

/** 
* @Description: 初始化种群
*/

private void init() {
System.out.println("1>生成初始种群...");
ddWindow.setVisible(true);
population = new ArrayList<Chromosome>();
for (int i = 0; i < popSize; i++) {
Chromosome chro = new Chromosome(geneSize);
population.add(chro);
}
}

(3) 适应度计算
遗传算法中以个体适应度的大小来评定各个个体的优劣程度,从而决定其遗传机会的大小。
本例中,目标函数总取非负值,并且是以求函数最大值为优化目标,故可直接利用目标函数值作为个体的适应度。

本步骤对应的Java代码:

/** 
* @Description: 计算种群适应度
*/

private void caculteScore() {
System.out.println("2>计算种群适应度...");
bestScore=(double)population.get(0).getScore();
worstScore=(double)population.get(0).getScore();
totalScore = 0;
for (Chromosome chro : population) {
setChromosomeScore(chro);
if (chro.getScore() > bestScore) { //设置最好基因值
bestScore = chro.getScore();
if (y < bestScore) {
x = changeX(chro);
y = bestScore;
geneI = generation;
}
}
if (chro.getScore() < worstScore) { //设置最坏基因值
worstScore = chro.getScore();
}
totalScore += chro.getScore();
}
averageScore = totalScore / popSize;
//因为精度问题导致的平均值大于最好值,将平均值设置成最好值
averageScore = averageScore > bestScore ? bestScore : averageScore;
}

(4) 选择运算
选择运算(或称为复制运算)把当前群体中适应度较高的个体按某种规则或模型遗传到下一代群体中。一般要求适应度较高的个体将有更多的机会遗传到下一代群体中。
本例中,我们采用与适应度成正比的概率来确定各个个体复制到下一代群体中的数量。其具体操作过程是:
• 先计算出群体中所有个体的适应度的总和 Σfi ( i=1.2,…,M );
• 其次计算出每个个体的相对适应度的大小 fi / Σfi ,它即为每个个体被遗传
到下一代群体中的概率,
• 每个概率值组成一个区域,全部概率值之和为1;
• 最后再产生一个0到1之间的随机数,依据该随机数出现在上述哪一个概率区
域内来确定各个个体被选中的次数。
遗传算法详解及java实现

本步骤对应的Java代码:

 /** 
* @return
* Email: tyhj_sf@163.com
* @Description: 轮盘赌法选择可以遗传下一代的染色体
*/

private Chromosome getParentChromosome (){
System.out.println("4.1>筛选父代种群一次...");
while (true) {
double slice = Math.random() * totalScore;
double sum = 0;
for (Chromosome chro : population) {
sum += chro.getScore();
System.out.println("测试:sum="+sum+" chro.getScore()="+chro.getScore());
if (sum > slice && chro.getScore() >= averageScore) {
return chro;
}
}
}
}

(5) 交叉运算
交叉运算是遗传算法中产生新个体的主要操作过程,它以某一概率相互交换某两个个体之间的部分染色体。
本例采用单点交叉的方法,其具体操作过程是:
• 先对群体进行随机配对;
• 其次随机设置交叉点位置;
• 最后再相互交换配对染色体之间的部分基因。
遗传算法详解及java实现

本步骤对应的Java代码:

 /** 
* @Description:种群进行遗传
*/

private void evolve() {
List<Chromosome> childPopulation = new ArrayList<Chromosome>();
//生成下一代种群
while (childPopulation.size() < popSize) {
Chromosome parent1 = getParentChromosome();
Chromosome parent2 = getParentChromosome();
List<Chromosome> children = Chromosome.genetic(parent1, parent2);
if (children != null) {
for (Chromosome chro : children) {
childPopulation.add(chro);
}
}
}
System.out.println("4.2>产生子代种群...");
//新种群替换旧种群
population.clear();
population = childPopulation;
}

(6) 变异运算
变异运算是对个体的某一个或某一些基因座上的基因值按某一较小的概率进行改变,它也是产生新个体的一种操作方法。
本例中,我们采用基本位变异的方法来进行变异运算,其具体操作过程是:
• 首先确定出各个个体的基因变异位置,下表所示为随机产生的变异点位置,
其中的数字表示变异点设置在该基因座处;
• 然后依照某一概率将变异点的原有基因值取反。
遗传算法详解及java实现

本步骤对应的Java代码:

/** 
* 基因突变
*/

private void mutation() {
System.out.println("5>基因突变...");
for (Chromosome chro : population) {
if (Math.random() < mutationRate) { //发生基因突变
int mutationNum = (int) (Math.random() * maxMutationNum);
chro.mutation(mutationNum);
}
}
}

(7) 迭代
对群体P(t)进行一轮选择、交叉、变异运算之后可得到新一代的群体p(t+1)。
遗传算法详解及java实现
从上表中可以看出,群体经过一代进化之后,其适应度的最大值、平均值都得到了明显的改进。事实上,这里已经找到了最佳个体“111111”。

需要说明的是,表中有些栏的数据是随机产生的。这里为了更好地说明问题,我们特意选择了一些较好的数值以便能够得到较好的结果,而在实际运算过程中有可能需要一定的循环次数才能达到这个最优结果。如下图所示,设置循环次数为500次,每次循环结果迅速收敛于最大值。遗传算法详解及java实现

本步骤对应的Java代码:

/**
* 迭代运算
* */

public void caculte() {

//1.初始化种群
init();
for(generation = 1; generation < maxIterNum; generation++) {
//2.计算种群适应度
caculteScore();
System.out.println("3>验证阈值...");
//4.种群遗传
evolve();
//5.基因突变
mutation();
print();
}
}

特别注意

基本遗传算法使用3种遗传算子
1)选择运算使用比例选择算子;
2)交叉运算使用单点交叉算子;
3)变异运算使用基本位变异算子或均匀变异算子。
运行参数设置
1)群体大小,一般设置为20-100个染色体;
2)进化代数,一般设置为100-500代;
3)染色体交叉概率,一般设置为0.4-0.99;
4)变异概率,一般设置为0.0001-0.1;

完整源码

需要eclipse下完整的工程源文件的同学请在评论区留言写下邮箱。

由于代码较长,仅贴出遗传算法部分代码便于对照前面讲解的原理进行深入理解,图形界面动态展示部分不再贴出:
代码中已经添加了大量的注释,不再对代码进行解释,请对照前面的原理自行分析。

染色体类Chromosome源码:

public class Chromosome {  
private boolean[] gene;//基因序列
private double score;//对应的函数得分

public double getScore() {
return score;
}

public void setScore(double score) {
this.score = score;
}

/**
* @param size
* 随机生成基因序列
*/

public Chromosome(int size) {
if (size <= 0) {
return;
}
initGeneSize(size);
for (int i = 0; i < size; i++) {
gene[i] = Math.random() >= 0.5;
}
}

/**
* 生成一个新基因
*/

public Chromosome() {

}

/**
* @param c
* @return
* @Description: 克隆基因
*/

public static Chromosome clone(final Chromosome c) {
if (c == null || c.gene == null) {
return null;
}
Chromosome copy = new Chromosome();
copy.initGeneSize(c.gene.length);
for (int i = 0; i < c.gene.length; i++) {
copy.gene[i] = c.gene[i];
}
return copy;
}

/**
* @param size
* @Description: 初始化基因长度
*/

private void initGeneSize(int size) {
if (size <= 0) {
return;
}
gene = new boolean[size];
}


/**
* @param c1
* @param c2
* @Description: 遗传产生下一代
*/

public static List<Chromosome> genetic(Chromosome p1, Chromosome p2) {
if (p1 == null || p2 == null) { //染色体有一个为空,不产生下一代
return null;
}
if (p1.gene == null || p2.gene == null) { //染色体有一个没有基因序列,不产生下一代
return null;
}
if (p1.gene.length != p2.gene.length) { //染色体基因序列长度不同,不产生下一代
return null;
}
Chromosome c1 = clone(p1);
Chromosome c2 = clone(p2);
//随机产生交叉互换位置
int size = c1.gene.length;
int a = ((int) (Math.random() * size)) % size;
int b = ((int) (Math.random() * size)) % size;
int min = a > b ? b : a;
int max = a > b ? a : b;
//对位置上的基因进行交叉互换
boolean t;
for (int i = min; i <= max; i++) {
t = c1.gene[i];
c1.gene[i] = c2.gene[i];
c2.gene[i] = t;
}
List<Chromosome> list = new ArrayList<Chromosome>();
list.add(c1);
list.add(c2);
return list;
}

/**
* @param num
* @Description: 基因num个位置发生变异
*/

public void mutation(int num) {
//允许变异
int size = gene.length;
for (int i = 0; i < num; i++) {
//寻找变异位置
int at = ((int) (Math.random() * size)) % size;
//变异后的值
boolean bool = !gene[at];
gene[at] = bool;
}
}

/**
* @return
* @Description: 将基因转化为对应的数字
*/

public int getNum() {
if (gene == null) {
return 0;
}
int num = 0;
for (boolean bool : gene) {
num <<= 1;
if (bool) {
num += 1;
}
}
return num;
}
}

主算法类GeneticAlgorithm源码 :

public abstract class GeneticAlgorithm {  
private List<Chromosome> population = new ArrayList<Chromosome>();
/**种群数量*/
private int popSize = 40;
/**染色体最大长度*/
private int geneSize;
/**最大迭代次数*/
private int maxIterNum = 500;
/**基因变异的概率*/
private double mutationRate = 0.001;
/**最大变异步长*/
private int maxMutationNum = 3;
/**当前遗传到第几代*/
private int generation = 1;

private double bestScore;//最好得分
private double worstScore;//最坏得分
private double totalScore;//总得分
private double averageScore;//平均得分

private double x; //记录历史种群中最好的X值
private double y; //记录历史种群中最好的Y值
private int geneI;//x y所在代数

private DynamicDataWindow ddWindow;
private long tp;

public GeneticAlgorithm(int geneSize) {
this.geneSize = geneSize;
}

public void caculte() {

//1.初始化种群
init();
for(generation = 1; generation < maxIterNum; generation++) {
//2.计算种群适应度
caculteScore();
System.out.println("3>验证阈值...");
//4.种群遗传
evolve();
//5.基因突变
mutation();
print();
}
}

/**
* @Description: 输出结果
*/

private void print() {
System.out.println("--------------------------------");
System.out.println("the generation is:" + generation);
System.out.println("the best y is:" + bestScore);
System.out.println("the worst fitness is:" + worstScore);
System.out.println("the average fitness is:" + averageScore);
System.out.println("the total fitness is:" + totalScore);
System.out.println("geneI:" + geneI + "\tx:" + x + "\ty:" + (y));

long millis=System.currentTimeMillis();
if (millis-tp>300) {
tp=millis;
ddWindow.addData(millis, y);
}

try {
Thread.sleep(10L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}


/**
* @Description: 初始化种群
*/

private void init() {
System.out.println("1>生成初始种群...");
ddWindow.setVisible(true);
population = new ArrayList<Chromosome>();
for (int i = 0; i < popSize; i++) {
Chromosome chro = new Chromosome(geneSize);
population.add(chro);
}
}

/**
* @Description:种群进行遗传
*/

private void evolve() {
List<Chromosome> childPopulation = new ArrayList<Chromosome>();
//生成下一代种群
while (childPopulation.size() < popSize) {
Chromosome parent1 = getParentChromosome();
Chromosome parent2 = getParentChromosome();
List<Chromosome> children = Chromosome.genetic(parent1, parent2);
if (children != null) {
for (Chromosome chro : children) {
childPopulation.add(chro);
}
}
}
System.out.println("4.2>产生子代种群...");
//新种群替换旧种群
population.clear();
population = childPopulation;
}

/**
* @return
* Email: tyhj_sf@163.com
* @Description: 轮盘赌法选择可以遗传下一代的染色体
*/

private Chromosome getParentChromosome (){
System.out.println("4.1>筛选父代种群一次...");
while (true) {
double slice = Math.random() * totalScore;
double sum = 0;
for (Chromosome chro : population) {
sum += chro.getScore();
System.out.println("测试:sum="+sum+" chro.getScore()="+chro.getScore());
if (sum > slice && chro.getScore() >= averageScore) {
return chro;
}
}
}
}

/**
* @Description: 计算种群适应度
*/

private void caculteScore() {
System.out.println("2>计算种群适应度...");
bestScore=(double)population.get(0).getScore();
worstScore=(double)population.get(0).getScore();
totalScore = 0;
for (Chromosome chro : population) {
setChromosomeScore(chro);
if (chro.getScore() > bestScore) { //设置最好基因值
bestScore = chro.getScore();
if (y < bestScore) {
x = changeX(chro);
y = bestScore;
geneI = generation;
}
}
if (chro.getScore() < worstScore) { //设置最坏基因值
worstScore = chro.getScore();
}
totalScore += chro.getScore();
}
averageScore = totalScore / popSize;
//因为精度问题导致的平均值大于最好值,将平均值设置成最好值
averageScore = averageScore > bestScore ? bestScore : averageScore;
}

/**
* 基因突变
*/

private void mutation() {
System.out.println("5>基因突变...");
for (Chromosome chro : population) {
if (Math.random() < mutationRate) { //发生基因突变
int mutationNum = (int) (Math.random() * maxMutationNum);
chro.mutation(mutationNum);
}
}
}

/**
* @param chro
* @Description: 计算并设置染色体适应度
*/

private void setChromosomeScore(Chromosome chro) {
if (chro == null) {
return;
}
int x = changeX(chro);
double y = caculateY((x&56)>>3, x&7);//注意根据具体情况对值补偿确保不会出现负值。此处不为负,所以不需要补偿
chro.setScore(y);

}

/**
* @param chro
* @return
* @Description: 将二进制转化为对应的X
*/

public abstract int changeX(Chromosome chro);


/**
* @param x
* @return
* @Description: 根据X计算Y值 Y=F(X)
*/

public abstract double caculateY(int x1, int x2);

public void setPopulation(List<Chromosome> population) {
this.population = population;
}

public void setPopSize(int popSize) {
this.popSize = popSize;
}

public void setGeneSize(int geneSize) {
this.geneSize = geneSize;
}

public void setMaxIterNum(int maxIterNum) {
this.maxIterNum = maxIterNum;
}

public void setMutationRate(double mutationRate) {
this.mutationRate = mutationRate;
}

public void setMaxMutationNum(int maxMutationNum) {
this.maxMutationNum = maxMutationNum;
}

public double getBestScore() {
return bestScore;
}

public double getWorstScore() {
return worstScore;
}

public double getTotalScore() {
return totalScore;
}

public double getAverageScore() {
return averageScore;
}

public double getX() {
return x;
}

public double getY() {
return y;
}

public DynamicDataWindow getDdWindow() {
return ddWindow;
}

public void setDdWindow(DynamicDataWindow ddWindow) {
this.ddWindow = ddWindow;
}
}

参考文献

1.遗传算法
2.非常好的理解遗传算法的例子
3.http://www.llwjy.com/blogdetail/8d8f9fa295e57c774c2b8223166aee1b.html