遗传算法是解决复杂问题的一种搜索策略,是一种智能计算。他模拟自然界中的种群进化。从这点看,它和神经网络这些算法的起源很像,都是使用计算机去模拟一些自然法则和运作规律。同样,和神经网络一样,遗传算法也是一种基于概率的算法。当然,在现代启发式算法中,很多算法都都是基于概率的,比如神经网络,粒子群算法,模拟退火等。这类方法的要解决的一个问题就是NP问题,要求得结果是满意解,而非最优解。理解这点,我觉得很重要。
本文中的GA实现的例子来自于http://www.theprojectspot.com/tutorial-post/creating-a-genetic-algorithm-for-beginners/3 一文。有兴趣的读者,也可以参考原文的实现。
遗传算法工作的时候,可以分为以下几个步骤:
1.初始化:首先需要初始化一个种群,大小可以根据需要随意,几个个体或者几千个个体都可以。种群的中个体的产生是随机的。
2.评估:每一个种群成员需要计算他们的适应度,就和自然界里的生物对自然环境的适应度一样。这里的适应度计算是一个关键,一个基本原则是,把对解决当前问题有意义的个体的适应度应该设置得比较高,否则应该给一个低的适应度。适应度计算函数很影响遗传算法性能。因为每一次进化,每一次迭代,每一个个体都要不停得被评估,如果这个方法很慢,搜索性能就会比较差。
3.选择:选择适应度比较高的个体进入下一个进化阶段。适应度低的可以抛弃。
4.交叉:将选中的个体进行基因交叉,产生下一代种群。这个过程就和自然界中生物体有性生殖一样,做DNA的交叉。
5.变异:和自然界一样,DNA是会变异的,变异操作可以帮助我们跳出局部最优。
6.重复:不停重复以上评估、选择、交叉、变异,直到找到一个满意的个体作为算法的最终解。
下面来看一个实现,首先看一下个体:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package simpleGa;
public class Individual { static int defaultGeneLength = 64; private byte[] genes = new byte[defaultGeneLength]; private int fitness = 0; // 随机的个体 public void generateIndividual() { for (int i = 0; i < size(); i++) { byte gene = (byte) Math.round(Math.random()); genes[i] = gene; } } // Use this if you want to create individuals with different gene lengths public static void setDefaultGeneLength(int length) { defaultGeneLength = length; } public byte getGene(int index) { return genes[index]; } public void setGene(int index, byte value) { genes[index] = value; fitness = 0; } public int size() { return genes.length; } public int getFitness() { if (fitness == 0) { fitness = FitnessCalc.getFitness(this); } return fitness; } @Override public String toString() { String geneString = ""; for (int i = 0; i < size(); i++) { geneString += getGene(i); } return geneString; } } |
每一个个体需要计算它的适应度:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package simpleGa;
public class FitnessCalc { static byte[] solution = new byte[64]; // 设置一个候选解 public static void setSolution(byte[] newSolution) { solution = newSolution; } // 设置一个候选解 就是参数类型不一样,这里做个转换 static void setSolution(String newSolution) { solution = new byte[newSolution.length()]; // Loop through each character of our string and save it in our byte // array for (int i = 0; i < newSolution.length(); i++) { String character = newSolution.substring(i, i + 1); if (character.contains("0") || character.contains("1")) { solution[i] = Byte.parseByte(character); } else { solution[i] = 0; } } } // 计算某一个个体的适应度 static int getFitness(Individual individual) { int fitness = 0; // 个体和候选解比较,约接近候选集,适应度越高 for (int i = 0; i < individual.size() && i < solution.length; i++) { if (individual.getGene(i) == solution[i]) { fitness++; } } return fitness; } // 适应度的最大值 static int getMaxFitness() { int maxFitness = solution.length; return maxFitness; } } |
基于个体而产生的种群:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package simpleGa;
public class Population { Individual[] individuals; // 建立一个总群 public Population(int populationSize, boolean initialise) { individuals = new Individual[populationSize]; // 初始化种群 if (initialise) { // 建立每一个个体 for (int i = 0; i < size(); i++) { Individual newIndividual = new Individual(); newIndividual.generateIndividual(); saveIndividual(i, newIndividual); } } } public Individual getIndividual(int index) { return individuals[index]; } public Individual getFittest() { Individual fittest = individuals[0]; // 取得适应度最高的个体作为种群的适应度 for (int i = 0; i < size(); i++) { if (fittest.getFitness() <= getIndividual(i).getFitness()) { fittest = getIndividual(i); } } return fittest; } // Get population size public int size() { return individuals.length; } // Save individual public void saveIndividual(int index, Individual indiv) { individuals[index] = indiv; } } |
基于以上的个体和种群,就可以进行遗传算法的主要工作,交叉,变异等等。来看一下核心的算法实现:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
package simpleGa;
public class Algorithm { private static final double uniformRate = 0.5; private static final double mutationRate = 0.015; private static final int tournamentSize = 5; private static final boolean elitism = true; // 进化一个种群 public static Population evolvePopulation(Population pop) { Population newPopulation = new Population(pop.size(), false); // 保留最优秀的个体 if (elitism) { newPopulation.saveIndividual(0, pop.getFittest()); } // 交叉种群 int elitismOffset; if (elitism) { elitismOffset = 1; } else { elitismOffset = 0; } // 交叉产生新的个体 for (int i = elitismOffset; i < pop.size(); i++) { //确保不要把最差的个体选进来 Individual indiv1 = tournamentSelection(pop); Individual indiv2 = tournamentSelection(pop); Individual newIndiv = crossover(indiv1, indiv2); newPopulation.saveIndividual(i, newIndiv); } // 变异 for (int i = elitismOffset; i < newPopulation.size(); i++) { mutate(newPopulation.getIndividual(i)); } return newPopulation; } // 交叉 private static Individual crossover(Individual indiv1, Individual indiv2) { Individual newSol = new Individual(); // Loop through genes for (int i = 0; i < indiv1.size(); i++) { // 以一定概率交叉 if (Math.random() <= uniformRate) { newSol.setGene(i, indiv1.getGene(i)); } else { newSol.setGene(i, indiv2.getGene(i)); } } return newSol; } // 变异 private static void mutate(Individual indiv) { // Loop through genes for (int i = 0; i < indiv.size(); i++) { if (Math.random() <= mutationRate) { // 以一定概率变异 byte gene = (byte) Math.round(Math.random()); indiv.setGene(i, gene); } } } // 选择用于交叉的个体,确保最差的个体不会进来 private static Individual tournamentSelection(Population pop) { Population tournament = new Population(tournamentSize, false); for (int i = 0; i < tournamentSize; i++) { int randomId = (int) (Math.random() * pop.size()); tournament.saveIndividual(i, pop.getIndividual(randomId)); } Individual fittest = tournament.getFittest(); return fittest; } } |
以上就是全部的实现,最后,测试这些代码