轉載請註明出處:http://blog.csdn.net/tyhj_sf/article/details/53321527 原理
為更好地說明和理解遺傳演算法的原理及運算過程,下面結合例子類比遺傳演算法的各個主要執行步驟。
例:求下述二元函數的最大值:
(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代碼:
/** * @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代碼:
/** * @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代碼:
/** * 基因突變 */ 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)。
從上表中可以看出,群體經過一代進化之後,其適應度的最大值、平均值都得到了明顯的改進。事實上,這裡已經找到了最佳個體“111111”。
需要說明的是,表中有些欄的資料是隨機產生的。這裡為了更好地說明問題,我們特意選擇了一些較好的數值以便能夠得到較好的結果,而在實際運算過程中有可能需要一定的迴圈次數才能達到這個最優結果。如下圖所示,設定迴圈次數為500次,每次迴圈結果迅速收斂於最大值。
本步驟對應的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);