學人工智能這門課時老師要求咱們完成一個遺傳算法的實驗。去機房前天晚上我忙於寫做業,而後代碼寫到2點才寫好。結果次日睡過頭。。。。白寫了一夜。java
實驗內容:算法
用遺傳算法求函數f(x)=x2的最大值,其中x爲[0,31]間的整數。app
在網上查了下資料後,發現遺傳算法的步驟主要以下:dom
(1) 個體編碼函數
遺傳算法的運算對象是表示個體的符號串,因此必須把變量 x 編碼爲一種
符號串。本題中,用無符號二進制整數來表示。
因 x 爲 0 ~ 31之間的整數,因此分別用5位無符號二進制整數來表示,表示一個可
行解。測試
例如,基因型 X=11111 所對應的表現型是:x=[ 31]。
個體的表現型x和基因型X之間可經過編碼和解碼程序相互轉換
(2) 初始羣體的產生遺傳算法是對羣體進行的進化操做,須要給其淮備一些表示起始搜索點的初始羣體數據。優化
本例中,羣體規模的大小隨便取爲4,即羣體由4個個體組成,每一個個體可經過隨機方法產生。ui
如:10000,10011,10010,10100this
(3) 適應度汁算
遺傳算法中以個體適應度的大小來評定各個個體的優劣程度,從而決定其遺傳
機會的大小。
本例中,目標函數總取非負值,而且是以求函數最大值爲優化目標,故可直接
利用目標函數值做爲個體的適應度編碼
//初始羣體的產生 int N=4;//羣體大小 body bs[]=init(N);//初始化種羣 int sum=0;//總適應度 for(int i=0;i<N;i++){ sum+=bs[i].v; } for(int i=0;i<N;i++){ bs[i].rat=bs[i].v/(sum*1.0); }
初始化後的數據
個體編碼:10000 (基因型)
個體數:16 (表現型)
適度:256 (x^2)
機率:0.19090231170768082(適度/總適度)
個體編碼:10011
個體數:19
適度:361
機率:0.2692020879940343
個體編碼:10010
個體數:18
適度:324
機率:0.24161073825503357
個體編碼:10100
個體數:20
適度:400
機率:0.29828486204325133
(4) 選擇運算
選擇運算(或稱爲複製運算)把當前羣體中適應度較高的個體按某種規則或模型遺傳到下一代羣體中。通常要求適應度較高的個體將有更多的機會遺傳到下一代
羣體中。
本例中,咱們採用與適應度成正比的機率來肯定各個個體複製到下一代羣體中
的數量。其具體操做過程是:
• 先計算出羣體中全部個體的適應度的總和 fi ( i=1.2,…,M );
• 其次計算出每一個個體的相對適應度的大小 fi / fi ,它即爲每一個個體被遺傳
到下一代羣體中的機率,
• 每一個機率值組成一個區域,所有機率值之和爲1;(好比上面0-0.19爲一個區域,0.19到0.19+0.26爲一個區域,一次後推)
• 最後再產生4個0到1之間的隨機數,依據該隨機數出如今上述哪個機率區
域內來肯定下一輪被選中的個體。
//選擇運算 body oldbs[]=bs;//先複製一份 double a=oldbs[0].rat; double b=oldbs[1].rat+a; double c=oldbs[2].rat+b; for(int i=0;i<N;i++){ double rand=Math.random(); if(rand<=a)bs[i]=oldbs[0]; if(rand>a&&rand<=b)bs[i]=oldbs[1]; if(rand>b&&rand<=c)bs[i]=oldbs[2]; if(rand>c&&rand<=1)bs[i]=oldbs[3]; } printbody(bs);
選擇計算後的數據
個體編碼:10000
個體數:16
適度:256
機率:0.19090231170768082
個體編碼:10011
個體數:19
適度:361
機率:0.2692020879940343
個體編碼:10100
個體數:20
適度:400
機率:0.29828486204325133
個體編碼:10000
個體數:16
適度:256
機率:0.19090231170768082
(5) 交叉運算
交叉運算是遺傳算法中產生新個體的主要操做過程,它以某一律率相互交換某
兩個個體之間的部分染色體。
本例採用單點交叉的方法,其具體操做過程是:
• 先對羣體進行隨機配對;
• 其次隨機設置交叉點位置;
• 最後再相互交換配對染色體之間的部分基因。
//交叉運算 (單點交叉) body oldbs1[] =bs; for (int i = 0; i < 4; i+=2) { int r1 = rand(1, 5); //System.out.println("交叉點:" + r1); String s1 = oldbs1[i].binary.substring(0, r1); String s2 = oldbs1[i].binary.substring(r1); String s3 = oldbs1[i + 1].binary.substring(0, r1); String s4 = oldbs1[i + 1].binary.substring(r1); //System.out.println(s1 + " " + s2 + " " + s3 + " " + s4); bs[i] = new body(s1 + s4); //System.out.println("b[i]" + bs[i].binary); bs[i + 1] = new body(s3 + s2); //System.out.println("b[i+1]" + bs[i + 1].binary); } printbody(bs);
交叉運算後的數據
個體編碼:10011
個體數:19
適度:361
機率:0.0
個體編碼:10000
個體數:16
適度:256
機率:0.0
個體編碼:10000
個體數:16
適度:256
機率:0.0
個體編碼:10100
個體數:20
適度:400
機率:0.0
(6) 變異運算
變異運算是對個體的某一個或某一些基因座上的基因值按某一較小的機率進
行改變,它也是產生新個體的一種操做方法。
本例中,咱們採用基本位變異的方法來進行變異運算,其具體操做過程是:
• 首先肯定出各個個體的基因變異位置,下表所示爲隨機產生的變異點位置,
其中的數字表示變異點設置在該基因座處;
• 而後依照某一律率將變異點的原有基因值取反。
//變異運算 double db=0.2;//變異機率 for (int i = 0; i < N; i++) { int is = rand(0, 6);// 變異位置 //System.out.println("變異位置"+is); if (Math.random() <= db) {//若是小於就變異 char[] cs = bs[i].binary.toCharArray(); if (cs[is] == '1') cs[is] = '0'; else cs[is] = '1'; bs[i] = new body(new String(cs)); } } printbody(bs);
變異運算後的數據
個體編碼:10011
個體數:19
適度:361
機率:0.0
個體編碼:10000
個體數:16
適度:256
機率:0.0
個體編碼:10000
個體數:16
適度:256
機率:0.0
個體編碼:10100
個體數:20
適度:400
機率:0.0
到這裏一輪進化就完成了,這裏發現一輪後的數據並無比剛初始化的數據更接近解,因而能夠弄個循環把這一輪的4個數據再來一次選擇,交叉,變異,試試。直到出現最優解。
完整代碼
package gh; import java.math.BigInteger; /** * 遺傳算法求x^2最大值,x(0...31) * @author ganhang * */ public class Genetic { public static void main(String[] args) { Genetic g=new Genetic(); g.test(); } //測試 public void test(){ //初始羣體的產生 int N=4;//羣體大小 body bs[]=init(N);//初始化種羣 int sum=0;//總適應度 for(int i=0;i<N;i++){ sum+=bs[i].v; } for(int i=0;i<N;i++){ bs[i].rat=bs[i].v/(sum*1.0); } printbody(bs); //選擇運算 body oldbs[]=bs; double a=oldbs[0].rat; double b=oldbs[1].rat+a; double c=oldbs[2].rat+b; for(int i=0;i<N;i++){ double rand=Math.random(); if(rand<=a)bs[i]=oldbs[0]; if(rand>a&&rand<=b)bs[i]=oldbs[1]; if(rand>b&&rand<=c)bs[i]=oldbs[2]; if(rand>c&&rand<=1)bs[i]=oldbs[3]; } printbody(bs); //交叉運算 (單點交叉) body oldbs1[] =bs; for (int i = 0; i < 4; i+=2) { int r1 = rand(1, 5); System.out.println("交叉點:" + r1); String s1 = oldbs1[i].binary.substring(0, r1); String s2 = oldbs1[i].binary.substring(r1); String s3 = oldbs1[i + 1].binary.substring(0, r1); String s4 = oldbs1[i + 1].binary.substring(r1); System.out.println(s1 + " " + s2 + " " + s3 + " " + s4); bs[i] = new body(s1 + s4); //System.out.println("b[i]" + bs[i].binary); bs[i + 1] = new body(s3 + s2); //System.out.println("b[i+1]" + bs[i + 1].binary); } printbody(bs); //變異運算 double db=0.2;//變異機率 for (int i = 0; i < N; i++) { int is = rand(0, 6);// 變異位置 System.out.println("變異位置"+is); if (Math.random() <= db) {//若是小於就變異 char[] cs = bs[i].binary.toCharArray(); if (cs[is] == '1') cs[is] = '0'; else cs[is] = '1'; bs[i] = new body(new String(cs)); } } printbody(bs); } //輸出 public void printbody(body[] bs){ for(body b:bs){ System.out.println("個體編碼:"+b.binary); System.out.println("個體數:"+twoToten(b.binary)); System.out.println("適度:"+b.v); System.out.println("機率:"+b.rat); System.out.println(); } System.out.println("-------------------------"); } //初始羣體的產生 n個數 public body[] init(int n){ body []bs = new body[n]; for(int i=0;i<n;i++){ bs[i]=new body(randBinary()); } return bs; } //適應度計算 y=x^2 public int count(int n){ return n*n; } //二進制轉十進制 public int twoToten(String s){ BigInteger bi=new BigInteger(s, 2); return Integer.parseInt(bi.toString()); } //隨機產生一個五位二進制 public String randBinary(){ StringBuilder sb=new StringBuilder(); for(int i=0;i<5;i++){ if(Math.random()>=0.5)sb.append(1); else sb.append(0); } return sb.toString(); } //返回a到b之間的數 public int rand(int a,int b){ return a+(int)(Math.random()*(b-a)); } //個體類 private class body{ String binary ; //二進制 int v;//適度 double rat;//個體的相對適應度的大小,即爲被遺傳到下一代羣體中的機率 public body(String s){ this.binary=s; this.v=count(twoToten(s)); } } }