生物學中的進化論的核心爲「物競天擇,適者生存」,暗含了的規則是生物可否生存是不定的,可是適應環境的生物更容易生存。生物的多樣性可以保持來源於繁殖和變異。
沒錯,你沒有點錯,這的確是一篇有關人工智能入門的博客。開篇先提到一些生物學的觀點是由於,人工智能中遺傳算法的靈感來源於生物學,它是一種仿生的概念。java
一.遺傳算法
那麼,什麼是遺傳算法呢?咱們來舉個栗子吧。
在爬蟲動物園裏有着各類各樣的螞蟻,我想選出一種螞蟻來表明動物園去參加全城動物園的螞蟻戰鬥比賽。那我該怎麼選呢?這裏有一個聰明人提出了一個方法:首先,咱們從各個螞蟻的種羣中選擇出一部分來(選擇初代種羣),讓他們相互打架,而後把勝者留下來互相繁殖(交叉產生子代,選擇了比較好的基因進行遺傳)。把小螞蟻養大,而後再讓它們打架,勝者繁殖(不斷生成子代)。經過屢次的繁殖和選擇,動物園有極大的可能選出的螞蟻是全部螞蟻中打架最厲害的。
好吧,這個例子不是特別的好。我理解的遺傳算法,就是一個多點的不定向搜索。它經過屢次的實驗,來找到符合適應度函數的個體。算法
二.基本步驟
基本的步驟以下:
1.編碼:將咱們能夠用來搜索的部分編碼,經常使用二進制串
2.產生初代:
3.計算每一個個體的適應度及適應度佔整體的比例(這裏我使用了輪盤賭方案):
4.以交叉機率選擇父代母代進行交叉
5.以變異機率進行變異
6.生成知足種羣容量的子代
7.進化多代segmentfault
三.代碼及說明
Java代碼:dom
import java.math.BigDecimal; import java.util.Random; public class Genetic { int geneSize = 20; int populationSize = 50; int iterationNum =100; double crossoverPro =0.8; double mutationPro = 0.05; int [][] individual = new int[populationSize][geneSize]; double [] realValue =new double[populationSize]; double [] fitness =new double [50]; double [] fitnessPro =new double [populationSize]; double currentOpt =0; int currentX =0; Random random =new Random(); double [] max =new double[iterationNum]; public void init(){ for(int i =0;i<populationSize;i++){ for(int j =0;j<geneSize;j++){ if(random.nextBoolean()) individual[i][j] =1; else individual[i][j] =0; } } } private void CalRealValue(){ for(int i =0;i<populationSize;i++){ realValue[i] =0; double decimal = 0; for(int j =0;j<3;j++){ realValue[i] = realValue[i]*2+individual[i][j]; } for(int j =geneSize-1;j>=3;j--){ decimal = decimal/2+individual[i][j]; } realValue[i] += decimal/2; } } private void CalFitnessAndFitnessPro(){ double sum =0; currentOpt =0; currentX =0; for(int i =0;i<populationSize;i++){ fitness[i] = realValue[i]+10*Math.sin(5*realValue[i])+7*Math.cos(4*realValue[i])+17; if(currentOpt<fitness[i]){ currentOpt =fitness[i]; currentX =i; } sum+=fitness[i]; } double section =0; for(int i =0;i<populationSize;i++){ section +=fitness[i]; fitnessPro[i] = section/sum; } } private void Reproduction(){ int [][] temp =new int [populationSize][geneSize]; //交叉 for(int i =0;i<populationSize-1;i=i+2){ int [] father =new int [geneSize]; double pro = random.nextDouble(); for(int j=0;j<populationSize;j++){ if(fitnessPro[j]>=pro){ for(int k =0;k<geneSize;k++) father[k] = individual[j][k]; break; } } pro =random.nextDouble(); int [] mother =new int [geneSize]; for(int j=0;j<populationSize;j++){ if(fitnessPro[j]>=pro){ for(int k =0;k<geneSize;k++) mother[k] = individual[j][k]; break; } } double pm =random.nextDouble(); if(pm<crossoverPro){ int changePosition =random.nextInt(geneSize); for(int j=0;j<geneSize;j++){ if(j<changePosition){ temp[i][j] =father[j]; temp[i+1][j] = mother[j]; } else { temp[i][j] = mother[j]; temp[i+1][j] = father[j]; } } }else{ for(int j=0;j<geneSize;j++){ temp[i][j] =father[j]; temp[i+1][j] = mother[j]; } } } for(int i =0;i<geneSize;i++){ individual[populationSize-1][i] = individual[currentX][i]; } //變異 for(int i =0;i<populationSize-1;i++){ for(int j =0;j<geneSize;j++){ if(random.nextDouble()<mutationPro) individual[i][j] = 1- temp[i][j]; else individual[i][j] = temp[i][j]; } } } public Genetic(){ init(); for(int i =0;i<iterationNum;i++){ CalRealValue(); CalFitnessAndFitnessPro(); BigDecimal optTemp =new BigDecimal(currentOpt-17); BigDecimal valueTemp =new BigDecimal(realValue[currentX]); optTemp =optTemp.setScale(5, BigDecimal.ROUND_HALF_UP); valueTemp =valueTemp.setScale(5, BigDecimal.ROUND_HALF_UP); System.out.println("第"+i+"代,最大值爲:"+optTemp+" 對應X是:"+valueTemp); max[i] = currentOpt-17; Reproduction(); } } public double[] getMax(){ return max; } public static void main(String[] args) { Genetic genetic =new Genetic(); } }
這裏我使用了二進制編碼,單點交叉,輪盤賭加精英選擇(沒代向下完整保留最優個體)。我目標是計算f(x) = x+ 10sin5x + 7cos4x 在[0,9]區間上的極大值,之因此在適應度函數里加入還加了17是由於f(x)在某些位置的時候是取到負數(最小-17),而參與輪盤賭計算的函數必須是正數,因此我加了17.函數
四.小嚐試
二進制編碼在0.001這樣的數的時候是不精確的,因而我想能不能用十進制來解決這個問題呢?下面我嘗試作了十進制編碼(精確度是0.00001)
Java代碼:性能
import java.math.BigDecimal; import java.util.Random; public class DecimalGenetic { int geneSize = 6; int populationSize = 50; int iterationNum =100; double crossoverPro =0.8; double mutationPro = 0.01; int [][] individual = new int[populationSize][geneSize]; double [] realValue =new double[populationSize]; double [] fitness =new double [50]; double [] fitnessPro =new double [populationSize]; double currentOpt =0; int currentX =0; Random random =new Random(); double []max =new double[iterationNum]; public void init(){ for(int i =0;i<populationSize;i++){ for(int j =0;j<geneSize;j++){ if(j == 0) individual[i][j] =random.nextInt(9); else individual[i][j] =random.nextInt(10); } } } private void CalRealValue(){ for(int i =0;i<populationSize;i++){ realValue[i] =0; realValue[i]+=individual[i][0]; double decimal = 0; for(int j =geneSize-1;j>0;j--){ decimal = decimal/10+individual[i][j]; } realValue[i] += decimal/10; // System.out.println(realValue[i]+" "+i); } } private void CalFitnessAndFitnessPro(){ double sum =0; currentOpt =0; currentX =0; for(int i =0;i<populationSize;i++){ fitness[i] = realValue[i]+10*Math.sin(5*realValue[i])+7*Math.cos(4*realValue[i])+17; if(currentOpt<fitness[i]){ currentOpt =fitness[i]; currentX =i; } sum+=fitness[i]; } double section =0; for(int i =0;i<populationSize;i++){ section +=fitness[i]; fitnessPro[i] = section/sum; } } private void Reproduction(){ int [][] temp =new int [populationSize][geneSize]; //產生交叉 for(int i =0;i<populationSize-1;i=i+2){ int [] father =new int [geneSize]; double pro = random.nextDouble(); for(int j=0;j<populationSize;j++){ if(fitnessPro[j]>=pro){ for(int k =0;k<geneSize;k++) father[k] = individual[j][k]; break; } } pro =random.nextDouble(); int [] mother =new int [geneSize]; for(int j=0;j<populationSize;j++){ if(fitnessPro[j]>=pro){ for(int k =0;k<geneSize;k++) mother[k] = individual[j][k]; break; } } double pm = random.nextDouble(); if(pm<crossoverPro){ int changePosition =random.nextInt(geneSize); for(int j=0;j<geneSize;j++){ if(j<changePosition){ temp[i][j] =father[j]; temp[i+1][j] =mother[j]; } else { temp[i][j] = mother[j]; temp[i+1][j] = father[j]; } } } else{ for(int j=0;j<geneSize;j++){ temp[i][j] =father[j]; temp[i+1][j] =mother[j]; } } } for(int i =0;i<geneSize;i++){ individual[populationSize-1][i] = individual[currentX][i]; } //變異 for(int i =0;i<populationSize-1;i++){ for(int j =0;j<geneSize;j++){ if(random.nextDouble()<mutationPro) individual[i][j] = random.nextInt(10); else individual[i][j] = temp[i][j]; } } } public DecimalGenetic(){ init(); for(int i =0;i<iterationNum;i++){ CalRealValue(); CalFitnessAndFitnessPro(); BigDecimal optTemp =new BigDecimal(currentOpt-17); optTemp =optTemp.setScale(5, BigDecimal.ROUND_HALF_UP); System.out.println("第"+i+"代,最大值爲:"+optTemp+" 對應X是:"+realValue[currentX]); max[i] = currentOpt-17; Reproduction(); } } public double[] getMax(){ return max; } public static void main(String[] args) { DecimalGenetic decimalGenetic =new DecimalGenetic(); } }
實現和二進制大部分一致,可是在變異部分我用了取隨機數這個方法。編碼
五.思考:
關於編碼,我在作完以後有一些思考,想和你們分享.咱們編碼一個數據,用0-1串表示,每一位是沒有差異的,可是轉換爲數字的時候,每一位的權值是不一樣的。對於真正的天然界,每一個性狀對於生物的存活概率的影響也是不一樣的。好比果蠅的翅膀大小和眼色對於它的生存概率的影響是不一樣的,那反應成咱們這裏的編碼就是位置不一樣,權重不一樣.這裏算是遺傳算法的奇妙之處吧.人工智能
下面推薦一個講述比較完整的博客:http://www.javashuo.com/article/p-hcdqmajn-hb.htmlcode