JAVA實現遺傳演算法
關於遺傳演算法的詳細原理以及具體的定義這裡就不多介紹,想瞭解的可以自行百度,下面就簡單介紹下自己對遺傳演算法的理解,本文對基因的編碼採用二進位規則。
演算法思想:
遺傳演算法參照達爾文的進化論,認為物種都是向好的方向去發展(適者生存),因此可以認為到足夠的代數之後,得到的最值可實際的最值很接近。
演算法步驟:
1)隨機產生一個種群;
2)計算種群的適應度、最好適應度、最差適應度、平均適應度等指標;
3)驗證種群代數是否達到自己設定的閾值,如果達到結束計算,否則繼續下一步計算;
4)採用轉盤賭法選擇可以產生下一代的父代,產生下一代種群(種群中個體數量不變);
5)種群發生基因突變;
6)重複2、3、4、5步。
演算法實現-基因部分
1、種群個體(這裡認為是染色體),在個體中,我們為這個個體添加兩個屬性,個體的基因和基因對應的適應度(函數值)。
public class Chromosome {private boolean[] gene;//基因序列private double score;//對應的函數得分}
2、隨機產生基因序列,基因的每一個位置是0還是1,這裡採用完全隨機的方式實現。
public Chromosome(int size) {if (size <= 0) {return;}initGeneSize(size);for (int i = 0; i < size; i++) {gene[i] = Math.random() >= 0.5;}}private void initGeneSize(int size) {if (size <= 0) {return;}gene = new boolean[size];}
3、把基因轉化為對應的值,比如101對應的數字是5,這裡採用位元運算來實現。
public int getNum() {if (gene == null) {return 0;}int num = 0;for (boolean bool : gene) {num <<= 1;if (bool) {num += 1;}}return num;}
4、基因發生變異,對於變異的位置這裡完全採取隨機的方式實現,變異原則是由1變為0,0變為1。
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;}}
5、複製基因,用於產生下一代,這一步就是將已存在的基因copy一份。
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;}
6、父母雙方產生下一代,這裡兩個個體產生兩個個體子代,具體哪段基因差生交叉,完全隨機。
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;//對位置上的基因進行交叉互換for (int i = min; i <= max; i++) {boolean 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;}
演算法實現-遺傳演算法
1、對於遺傳演算法,我們需要有對應的種群以及我們需要設定的一些常量:種群數量、基因長度、基因突變個數、基因突變率等,具體參照如下代碼:
public abstract class GeneticAlgorithm {private List<chromosome> population = new ArrayList<chromosome>();//種群private int popSize = 100;//種群數量private int geneSize;//基因最大長度private int maxIterNum = 500;//最大迭代次數private double mutationRate = 0.01;//基因變異的機率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所在代數}
2、初始化種群,在遺傳演算法開始時,我們需要初始化一個原始種群,這就是原始的第一代。
private void init() {for (int i = 0; i < popSize; i++) {population = new ArrayList<chromosome>();Chromosome chro = new Chromosome(geneSize);population.add(chro);}caculteScore();}
3、在初始種群存在後,我們需要計算
種群的適應度以及最好適應度、最壞適應度和平均適應度等。
private void caculteScore() {setChromosomeScore(population.get(0));bestScore = population.get(0).getScore();worstScore = 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、在計算個體適應度的時候,我們需要根據基因
計算對應的Y值,這裡我們設定兩個抽象方法,具體實現由類的實現去實現。
private void setChromosomeScore(Chromosome chro) {if (chro == null) {return;}double x = changeX(chro);double y = caculateY(x);chro.setScore(y);}/** * @param chro * @return * @Author:lulei * @Description: 將二進位轉化為對應的X */public abstract double changeX(Chromosome chro);/** * @param x * @return * @Author:lulei * @Description: 根據X計算Y值 Y=F(X) */public abstract double caculateY(double x);
5、在計算完種群適應度之後,我們需要使用
轉盤賭法選取可以
產生下一代的個體,這裡有個條件就是只有個人的適應度不小於平均適應度才會長生下一代(適者生存)。
private Chromosome getParentChromosome (){double slice = Math.random() * totalScore;double sum = 0;for (Chromosome chro : population) {sum += chro.getScore();//轉到對應的位置並且適應度不小於平均適應度if (sum > slice && chro.getScore() >= averageScore) {return chro;}}return null;}
6、選擇可以產生下一代的個體之後,就要交配
產生下一代。
private void evolve() {List<chromosome> childPopulation = new ArrayList<chromosome>();//產生下一代種群while (childPopulation.size() < popSize) {Chromosome p1 = getParentChromosome();Chromosome p2 = getParentChromosome();List<chromosome> children = Chromosome.genetic(p1, p2);if (children != null) {for (Chromosome chro : children) {childPopulation.add(chro);}} }//新種群替換舊種群List<chromosome> t = population;population = childPopulation;t.clear();t = null;//基因突變mutation();//計算新種群的適應度caculteScore();}
7、在產生下一代的過程中,可能會發生
基因變異。
private void mutation() {for (Chromosome chro : population) {if (Math.random() < mutationRate) { //發生基因突變int mutationNum = (int) (Math.random() * maxMutationNum);chro.mutation(mutationNum);}}}
8、將上述步驟
一代一代的重複執行。
public void caculte() {//初始化種群generation = 1;init();while (generation < maxIterNum) {//種群遺傳evolve();print();generation++;}}
編寫實作類別
由於上述遺傳演算法的類是一個抽象類別,因此我們需要針對特定的案例編寫實作類別,假設我們計算 Y=100-log(X)在[6,106]上的最值。
1、我們假設基因的長度為24(基因的長度由要求結果的有效長度確定),因此對應的二進位最大值為 1<< 24,我們做如下設定
public class GeneticAlgorithmTest extends GeneticAlgorithm{public static final int NUM = 1 << 24;public GeneticAlgorithmTest() {super(24); }}
2、對X值的抽象方法進行實現
@Overridepublic double changeX(Chromosome chro) {// TODO Auto-generated method stub return ((1.0 * chro.getNum() / NUM) * 100) + 6;}
3、對Y的抽象方法進行實現
@Overridepublic double caculateY(double x) {// TODO Auto-generated method stub return 100 - Math.log(x);}
運行結果
遺傳演算法思考
自己看了很多遺傳演算法的介紹,上面提到的最優解都是最後一代的最值,自己就有一個疑問了,為什麼我知道前面所有帶中的最值,也就是程式中的X Y值,為什麼不能用X Y值做遺傳演算法最後的結果值呢?
完整代碼
1、Chromosome類
/** *@Description: 基因遺傳染色體 */ package com.lulei.genetic.algorithm; import java.util.ArrayList;import java.util.List;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 * @Author:lulei * @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 * @Author:lulei * @Description: 初始化基因長度 */private void initGeneSize(int size) {if (size <= 0) {return;}gene = new boolean[size];}/** * @param c1 * @param c2 * @Author:lulei * @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;//對位置上的基因進行交叉互換for (int i = min; i <= max; i++) {boolean 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 * @Author:lulei * @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 * @Author:lulei * @Description: 將基因轉化為對應的數字 */public int getNum() {if (gene == null) {return 0;}int num = 0;for (boolean bool : gene) {num <<= 1;if (bool) {num += 1;}}return num;}}
2、GeneticAlgorithm類
/** *@Description: */ package com.lulei.genetic.algorithm; import java.util.ArrayList;import java.util.List; public abstract class GeneticAlgorithm {private List<chromosome> population = new ArrayList<chromosome>();private int popSize = 100;//種群數量private int geneSize;//基因最大長度private int maxIterNum = 500;//最大迭代次數private double mutationRate = 0.01;//基因變異的機率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所在代數public GeneticAlgorithm(int geneSize) {this.geneSize = geneSize;}public void caculte() {//初始化種群generation = 1;init();while (generation < maxIterNum) {//種群遺傳evolve();print();generation++;}}/** * @Author:lulei * @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);}/** * @Author:lulei * @Description: 初始化種群 */private void init() {for (int i = 0; i < popSize; i++) {population = new ArrayList<chromosome>();Chromosome chro = new Chromosome(geneSize);population.add(chro);}caculteScore();}/** * @Author:lulei * @Description:種群進行遺傳 */private void evolve() {List<chromosome> childPopulation = new ArrayList<chromosome>();//產生下一代種群while (childPopulation.size() < popSize) {Chromosome p1 = getParentChromosome();Chromosome p2 = getParentChromosome();List<chromosome> children = Chromosome.genetic(p1, p2);if (children != null) {for (Chromosome chro : children) {childPopulation.add(chro);}} }//新種群替換舊種群List<chromosome> t = population;population = childPopulation;t.clear();t = null;//基因突變mutation();//計算新種群的適應度caculteScore();}/** * @return * @Author:lulei * @Description: 輪盤賭法選擇可以遺傳下一代的染色體 */private Chromosome getParentChromosome (){double slice = Math.random() * totalScore;double sum = 0;for (Chromosome chro : population) {sum += chro.getScore();if (sum > slice && chro.getScore() >= averageScore) {return chro;}}return null;}/** * @Author:lulei * @Description: 計算種群適應度 */private void caculteScore() {setChromosomeScore(population.get(0));bestScore = population.get(0).getScore();worstScore = 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() {for (Chromosome chro : population) {if (Math.random() < mutationRate) { //發生基因突變int mutationNum = (int) (Math.random() * maxMutationNum);chro.mutation(mutationNum);}}}/** * @param chro * @Author:lulei * @Description: 設定染色體得分 */private void setChromosomeScore(Chromosome chro) {if (chro == null) {return;}double x = changeX(chro);double y = caculateY(x);chro.setScore(y);}/** * @param chro * @return * @Author:lulei * @Description: 將二進位轉化為對應的X */public abstract double changeX(Chromosome chro);/** * @param x * @return * @Author:lulei * @Description: 根據X計算Y值 Y=F(X) */public abstract double caculateY(double x);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;}}
3、GeneticAlgorithmTest類
/** *@Description: */ package com.lulei.genetic.algorithm; public class GeneticAlgorithmTest extends GeneticAlgorithm{public static final int NUM = 1 << 24;public GeneticAlgorithmTest() {super(24); }@Overridepublic double changeX(Chromosome chro) {// TODO Auto-generated method stub return ((1.0 * chro.getNum() / NUM) * 100) + 6;}@Overridepublic double caculateY(double x) {// TODO Auto-generated method stub return 100 - Math.log(x);}public static void main(String[] args) {GeneticAlgorithmTest test = new GeneticAlgorithmTest();test.caculte();}}