什么是遗传算法

如果你去百度百科搜的话,他会给你讲一堆遗传算法的概念,其实说白了,遗传算法就是计算机科学家从自然界获得灵感总结的一套优化方法的总称。

遗传算法是用来解决优化问题的。很早之前,我看到过一个视频,介绍使用遗传算法来让计算机设计能走的很远的蛋白质结构。emmm,可能你会不知道我在说什么,那就去看看这个视频吧,这个视频介绍的是用遗传算法来设计小车的结构。

【boxcar2D】进化小车三个多小时的进化过程

遗传算法相比于其他优化算法的优势在于,他是一种 弱方法,所谓 弱方法就是说在各个领域几乎都能用,并且你在优化的时候不需要过多的领域知识。并且,有的时候使用遗传算法可以设计出超越人类知识边界的模式
上图就是NASA使用遗传算法设计出的天线,是不是感觉很厉害。

遗传算法的基本流程

遗传算法的常用术语

搜索空间:在处理优化问题的时候我们通常是要搜索解空间,以便寻找最优解,所有的解构成的集合就称为搜索空间,我们举一个非常非常简单的模型。

上图就是优化一个参数的例子,我们假设这个参数是连续的,那么解空间就是无限大,我们需要找到一个适应度值最大的解作为我们算法的结果。

使用遗传算法搜索解空间通常都是由于解空间太大了,逐个遍历不切实际。
种群规模:种群规模就是遗传算法中任意一个种群的个体数。在设计遗传算法的时候我们通常需要找到一个平衡,因为太多的大部分算法在个体数达到某个值的时候可以取得效果和效率上的平衡,超过这个值算法的效率就会降低,与此同时搜索的效果可能并不会变好。
交叉率:交叉率指的是在执行交叉方法的时候,两个亲代交换基因的概率。
变异率:字面意思,就是在进化过程中基因变异的概率。
基因表示:这可以说是遗传算法设计中的核心问题,不同的问题应该使用不同的编码,这样才能使算法效率最大化。
精英主义:一个种群中通常都会有适应度比较好的个体,如果在这些个体在进化的过程中也进行交叉和变异,那么好的性状可能得不到保留,换一种说法就是,算法就不收敛了。所以通常我们需要保护这些个体,让他们的基因能够安全地流传下去。

实现一个简单的遗传算法

终于到了最激动人心的实现环节了,看了刚刚的介绍,你是不是也跃跃欲试了呢?下面我给大家介绍使用java编写的简单的遗传算法,为什么用java?因为java真的很好用
算法设计的目标:我们使用0和1来编码基因组,目的是找到一个全为1的基因编码。
算法伪代码

generation=0//初始化种群
population[generation]=initializePopulation(populationSize);
//评估种群的适应度
evaluatePopulation(population[generation])
while isTerminationConditionMet()==false do//选择亲代parents=selectParent(population[generation]);//交叉population[generation+1]=crossover(parents);//变异population[generation+1]=mutate(population[generation+1]);//评估种群适应度evaluatePopulation(population[generation]);//代数加一generation++;
End Loop;

代码示例
我们的代码将包含你下面的四个类
GeneticAlgorithm:所有遗传算法用到的算法都写在这里面
Individual:个体的属性和方法
Population:群体的属性和方法,里面集成了很多个体
AllOnesGA:类似于main函数
首先是Individual.java

package SimpleGA;//这是个体类
public class Individual {private int[] chromosome;       //染色体数组private double fitness = -1;    //适应度//含有染色体数组的构造函数public Individual(int[] chromosome){this.chromosome = chromosome;}//含有染色体长度的构造函数,随机初始化染色体public Individual(int chromosomeLength){this.chromosome = new int[chromosomeLength];for(int gene = 0; gene <  chromosomeLength; gene++){if(Math.random() > 0.5){this.setGene(gene, 1);} else {this.setGene(gene, 0);}}}public int[] getChromosome(){return this.chromosome;}public int getChromosomeLength(){return this.chromosome.length;}public void setGene(int offset, int gene){this.chromosome[offset] = gene;}public int getGene(int offset){return this.chromosome[offset];}public void setFitness(double fitness){this.fitness = fitness;}public double getFitness(){return this.fitness;}public String toString(){String output = "";for(int gene = 0; gene < this.chromosome.length; gene++){output += this.chromosome[gene];}return output;}}

接着是Population.java

package SimpleGA;import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;//群体类
public class Population {private Individual population[];private double populationFitness = -1;  //群体的适应度public Population(int populationSize){this.population = new Individual[populationSize];}public Population(int populationSize, int chromosomeLength){this.population = new Individual[populationSize];for(int individualCount = 0; individualCount < populationSize; individualCount++){Individual individual = new Individual(chromosomeLength);   //这通过长度创建个体this.population[individualCount] = individual;  //加入到群体中}}public Individual[] getIndividuals(){return this.population;}//将个体按照适应度排序,适应度越高索引值越低//排序后根据索引值获得个体public Individual getFittest(int offset){Arrays.sort(this.population, new Comparator<Individual>() {@Overridepublic int compare(Individual o1, Individual o2) {if(o1.getFitness() > o2.getFitness()){return -1;  //前面比后面高就返回-1} else if(o1.getFitness() < o2.getFitness()){return 1;   //前天比后面第就返会1,此时交换两个元素} else {return 0;   //相等返回0}}});return this.population[offset];}public void setPopulationFitness(double fitness){this.populationFitness = fitness;}public double getPopulationFitness(){return this.populationFitness;}//得到群体中的个体数public int size(){return this.population.length;}//将个体加入到群体中的某个位置public Individual setIndividual(int offset, Individual individual){return population[offset] = individual;}public Individual getIndividual(int offset){return this.population[offset];}//对于每一个个体,都将它与其他任意个体随机交换public void shuffle(){Random rnd = new Random();for(int i=population.length-1; i>0; i--){int index = rnd.nextInt(i+1);   //随机获得[0, i+1)的一个数Individual a = population[index];       //进行交换population[index] = population[i];population[i] = a;}}}

然后是GeneticAlgorithm.java

package SimpleGA;//这个类写遗传算法的通用算法
public class GeneticAlgorithm {private int populationSize;     //搜索范围private double mutationRate;    //变异率private double crossoverRate;   //交叉率private int elitismCount;       //精英数量//构造函数public GeneticAlgorithm(int populationSize, double mutationRate, double crossoverRate, int elitismCount) {this.populationSize = populationSize;this.mutationRate = mutationRate;this.crossoverRate = crossoverRate;this.elitismCount = elitismCount;}//初始化群体public Population initPopulation(int chromosomeLength){Population population = new Population(this.populationSize, chromosomeLength);return population;}//评估适应度public double calcFitness(Individual individual){//记录为1的染色体数量int correctGenes = 0;//扫描所有染色体for(int geneIndex =0; geneIndex<individual.getChromosomeLength(); geneIndex++){if(individual.getGene(geneIndex)==1){correctGenes++;}}//适应度为二进制编码中为1的编码占编码总数的比例double fitness = (double)correctGenes / individual.getChromosomeLength();//存储适应度individual.setFitness(fitness);return fitness;}//评估群体适应度,其值就是每个个体适应度加和public void evalPopulation(Population population){double populationFitness = 0;   //群体的适应度for(Individual individual : population.getIndividuals()){populationFitness += calcFitness(individual);}population.setPopulationFitness(populationFitness);}//终止检查public boolean isTerminationConditionMet(Population population){//只要群体中有一个个体的适应度为1就终止检查for(Individual individual : population.getIndividuals()){if(individual.getFitness()==1){return true;}}return false;}//选择方法//这个方法比较难理解,有点像概率里面的分布函数//我们使用轮盘赌的方式实现交叉,就是古典概型,适应度高的个体被选中染色体的概率就高public Individual selectParent(Population population){//获得个体Individual individuals[] = population.getIndividuals();double populationFitness = population.getPopulationFitness();double rouletteWheelPosition = Math.random()*populationFitness;//Math.random() 返回[0, 1)的随机数//找到亲代double spinWheel = 0;for(Individual individual: individuals){spinWheel += individual.getFitness();if(spinWheel >= rouletteWheelPosition){return individual;}}return individuals[population.size()-1];    //返回群体中的最后一个个体}//交叉方法public Population crossoverPopulation(Population population){//创建新种群Population newPopulation = new Population(population.size());//根据种群的适应度遍历种群for(int populationIndex=0; populationIndex<population.size(); populationIndex++){Individual parent1 = population.getFittest(populationIndex);//如果概率上达到了交叉的条件,并且不是精英就交叉(精英的性状直接保留至下一代)if(this.crossoverRate>Math.random() && populationIndex>this.elitismCount){//初始化后代Individual offspring = new Individual(parent1.getChromosomeLength());//找到第二个亲代//形状越好找被选中的概率越大Individual parent2 = selectParent(population);//开始交叉操作for(int geneIndex=0; geneIndex<parent1.getChromosomeLength(); geneIndex++){//交叉的时候有一半的概率使用p1的基因,一半的概率使用p2的基因if(Math.random() < 0.5){    //使用p1的基因offspring.setGene(geneIndex, parent1.getGene(geneIndex));} else {                    //使用p2的基因offspring.setGene(geneIndex, parent2.getGene(geneIndex));}}//将子代加入到种群中,实际上就是将原来的parent1替换成offspring了newPopulation.setIndividual(populationIndex, offspring);} else {//直接将个体添加到种群中newPopulation.setIndividual(populationIndex, parent1);}}return newPopulation;}public Population mutatePopulation(Population population){//初始化新种群Population newPopulation = new Population(this.populationSize);//根据适应度遍历种群for(int populationIndex=0; populationIndex<population.size(); populationIndex++){Individual individual = population.getFittest(populationIndex);//遍历个体的基因for(int geneIndex=0; geneIndex<individual.getChromosomeLength(); geneIndex++){//精英就不用变异了(富人靠科技,穷人靠变异)if(populationIndex >= this.elitismCount){if(this.mutationRate > Math.random()){  //达到了变异的条件int newGene = 1;if(individual.getGene(geneIndex) == 1){newGene = 0;    //既然是变异,那么就应该在相应位置将基因取反}//将变异后的基因插入individual.setGene(geneIndex, newGene);}}}//添加个体到群体中newPopulation.setIndividual(populationIndex, individual);}//返回变异后的群体return newPopulation;}}

最后是AllOnesGA.java

package SimpleGA;//引导类,用于初始化遗传算法
public class AllOnesGA {public static void main(String[] args){//实例化一个遗传算法对象GeneticAlgorithm ga = new GeneticAlgorithm(100, 0.01, 0.95, 3);//初始化群体,染色体长度为50Population population = ga.initPopulation(50);//下面进行进化计算ga.evalPopulation(population);int generation = 1; //进化的代数while(ga.isTerminationConditionMet(population) == false){//打印群体中适应度最高的个体System.out.println("Best solution: "+population.getFittest(0).toString() +" generation: "+generation);//交叉//更新种群population = ga.crossoverPopulation(population);//变异population = ga.mutatePopulation(population);//评估群体ga.evalPopulation(population);//代数加一generation++;}//打印最终结果System.out.println("Found solution in " + generation + "generations");System.out.println("Best solution: "+population.getFittest(0).toString());}
}

你可以在设置不同的初始值来计算,你会发现,几乎每一次计算的结果都不一样,并且如果精英数设置为0的话,算法可能就不收敛了,我使用上面代码的参数跑的结果如下

总结

这只是遗传算法一个很简单的示例,遗传算法还可以解决很多很复杂的问题,总的来说还是挺有意思的哈哈。

【Java编程】遗传算法简介与简单二进制编码计算相关推荐

  1. PTA 基础编程题目集 6-8 简单阶乘计算 C语言

    PTA 基础编程题目集 6-8 简单阶乘计算 C语言 本题要求实现一个计算非负整数阶乘的简单函数. 函数接口定义: int Factorial( const int N ); 其中N是用户传入的参数, ...

  2. Java 缓冲流简介及简单用法

    在java编程中, 我们有时会听到缓冲流和原始流等字眼. 其实在之前的博文中, 提到过流可以分为原始流和处理流. http://blog.csdn.net/nvd11/article/details/ ...

  3. 【Java编程】建立一个简单的JDBC连接-Drivers, Connection, Statement and PreparedStatement

    本blog提供了一个简单的通过JDBC驱动建立JDBC连接例程.并分别通过Statement和PreparedStatement实现对数据库的查询. 在下一篇blog中将重点比較Statement与P ...

  4. 关于《Java编程思想》的简单纠正

    今天在看<Java编程思想>(我买的第四版,中文版)这本书的时候,在书第93页部分开头,有这么一段描述: "5.6.1    指定初始化 如果想为某个变量赋初值,该怎么做呢?有一 ...

  5. Java Iterator 接口简介和简单用法.

    Iterator 的中文意思是迭代器. 单单从中文翻译也不易理解迭代器的意思啊,  其实Iterator是1个接口,  它的作用就是遍历容器的所有元素. 一, Iterator 接口简介 Iterat ...

  6. PTA基础编程题目集-6-8 简单阶乘计算

    int Factorial( const int N ){int fact=1;if(N<0)return 0;for(int i=1;i<=N;i++){fact*=i;}return ...

  7. 基础编程题目集 6-8 简单阶乘计算 (10 分)

    问题描述: 解题思路: 由于数据很小,所以直接递归即可 解题代码: int Factorial( const int N ){if(N < 0) return 0;if(N == 0 || N ...

  8. Java编程内存分析简要

    Java编程内存分析简要 需求1:计算五名学生,一门课程的平均分. 分析数据存储: 数组存储数据(1) 数组存储数据(2) 数组存储数据(3) 关于需求1使用面向对象程序设计思想的思考. 需求2:计算 ...

  9. 【连载】Scala程序设计:Java虚拟机多核编程实战——简介

    可以在JVM上编程的语言有很多.通过这本书,我希望让你相信花时间学习Scala是值得的. Scala语言为并发.表达性和可扩展性而设计.这门语言及其程序库可以让你专注于问题领域,而无需深陷于诸如线程和 ...

最新文章

  1. 实战Jenkins+SVN+tomcat持续集成发布
  2. HttpServlet中的service方法
  3. 13.multi-term-vector请求
  4. 3-2numpy数组的合并的拆分
  5. python使用复合语句def创建函数对象_Python 纯函数
  6. 数据库操作语句类型(DQL、DML、DDL、DCL)简介
  7. firebug尺寸标尺消失不显示问题
  8. 我的计算机 不显示桌面存储路劲,win10系统保存文件路径在桌面无法显示的操作步骤...
  9. 为什么大多公司不要培训班培训出来的Java程序员?
  10. 好太太亮相综艺《阳光姐妹淘》,种草青年生活新方式
  11. 新一轮勒索病毒变种全球肆虐 中国已遭攻击
  12. RTX的“远程登录”原理是什么?
  13. 编译原理———词法分析器
  14. 使用Bootstrap框架写的一个小实例
  15. 如何将图片压缩到15k以下?教你一键压缩图片的大小
  16. SRS:流媒体服务器如何实现负载均衡
  17. Vscode国内直接下载地址!!!,看过来
  18. xbox控制台小帮手怎么卸载?
  19. 5.2.6 三划分快速排序算法
  20. Spring相关文章汇总篇【Spring,SpringBoot,SpringCloud等】

热门文章

  1. 东莞理工学院计算机硕士点,东莞理工学院2021研究生招生计划
  2. Monitor:单节点监控之Cadvisor
  3. 如何让手机计算机出现错误二字,手机日历出错了?看到很多人都在讨论“二九”两字,你也去看一下...
  4. 如何解决:“Avoid hardcoding the debug mode
  5. el-date-picker报错[Vue warn]: Avoid mutating a prop directly since the value will--解决方法
  6. Notability for Mac(备注笔记软件)
  7. 一款牛逼的浏览器,功能强大到上天!
  8. Spring5从零单排学习笔记【非常详细】
  9. 在Arduino中使用DS18B20温度传感器(基于OneWire和DallasTemperature库)
  10. 学习周记2022/9/16