一、實驗目的
1.掌握遺傳算法的基本原理和步驟。
2. 複習VB、VC的基本概念、基本語法和編程方法,並熟練使用VB或VC編寫遺傳算法程序。
二、實驗內容
1. 上機編寫程序,解決以下函數優化問題:
2. 調試程序。
3. 根據實驗結果,撰寫實驗報告。
三、實驗原理
遺傳算法是一類隨機優化算法,但它不是簡單的隨機比較搜索,而是通過對染色體的評價和對染色體中基因的作用,有效地利用已有信息來指導搜索有希望改善優化質量的狀態。
標準遺傳算法流程圖如下圖所示,主要步驟可描述如下:
① 隨機產生一組初始個體構成初始種羣。
② 計算每一個體的適配值(fitness value,也稱爲適應度)。適應度值是對染色體(個體)進行評價的一種指標,是GA進行優化所用的主要信息,它與個體的目標值存在一種對應關係。
③ 判斷算法收斂準則是否滿足,若滿足,則輸出搜索結果;否則執行以下步驟。
④ 根據適應度值大小以一定方式執行復制操作(也稱爲選擇操作)。
⑤ 按交叉概率pc執行交叉操作。
⑥ 按變異概率pm執行變異操作。
⑦ 返回步驟②。
圖1.1 標準遺傳算法流程圖
代碼實現::::::
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include<time.h>
#define byte unsigned char
#define step 200 //步長
#define MAX 50
#define N 10 //隨機數個數
#define Pc 0.74 //被選擇到下一代的概率,個數=Pc*N,小於N 下一代數=上一代,不用處理
#define Pt 0.25 //交叉的概率,個數=Pt*N 舍,小於N 0~(n2+1)隨機數,之後部分開始交叉
#define Pm 0.01 //變異的概率,個數=Pm*N*n2 入,小於N 0~(N*(n2+1))隨機數/(n2+1)=個體,0~(N*(n2+1))隨機數%(n2+1)=該個體基因位置
#define n2 15//2的15次方,共16位
#define next_t (int)(Pt*N)//交叉個數
#define next_m (int)(Pm*N+1)//變異個數 向後約等於
#define e 0.001//次數限制閾值
/*
int N=10; //隨機數個數
float Pc=0.74; //被選擇到下一代的概率,個數=Pc*N,小於N 下一代數=上一代,不用處理
float Pt=0.25; //交叉的概率,個數=Pt*N 舍,小於N 0~(n2+1)隨機數,之後部分開始交叉
float Pm=0.01; //變異的概率,個數=Pm*N*n2 入,小於N 0~(N*(n2+1))隨機數/(n2+1)=個體,0~(N*(n2+1))隨機數%(n2+1)=該個體基因位置
*/
byte bitary[N][n2+1],bitary0[N][n2+1];//二進制
int src1[N];
float ShowType(int a);//表現型
void BinNum(int a);//二進制位數n2
float fit_func(float a);//適應度
void DecToBin (int src,int num);//十進制轉二進制
void BinToDec (void);//十進制轉二進制
int selectT(float a,float b[10]);//選擇交叉個體
int selectM(float a,float b[10]);//選擇變異個體
void main(void)
{
//範圍是[-100,100]***************************
int src[N],i=0,j=0,k=0,count=0;//十進制
float show[N];//表現型
float fit[N],sumfit=0;//適應度
float pcopy[N];//優勝劣汰,遺傳到下一代的概率fit[i]/總和(fit[i])
float pacc[N];//pcopy[i]累加概率值
float prand[N];//隨機產生N個0~1的下一代概率
int iselect;//根據概率選擇到的個體序號
int new_select[N];//根據概率選擇到的個體
int new_T[next_t],new_M[next_m];
float min,min1;
printf("隨機數(原始母體),表現型, 適配值\n");
srand( (unsigned)time(NULL) );
for(i=0;i<N;i++)
{
src[i]=rand()%32768; //rand()%201-100===>-100~100的十進制隨機數 隨時間遞增
show[i]=ShowType(src[i]);//轉化成表現型
fit[i]=fit_func(show[i]);//計算各個適配值(適應度)
sumfit=sumfit+fit[i]; //種羣的適應度總和
printf("%5d, %f, %f\n",src[i],show[i],fit[i]);
}
printf("\n第%d代適配總值\n%f\n",count,sumfit);//第0代
count++;
min=sumfit;
printf("\n遺傳到下一代的概率\n");
for(i=0;i<N;i++)
{
pcopy[i]=fit[i]/sumfit;
printf("%f, ",pcopy[i]);
}
// 求選擇(被複制)的累加概率,用於輪盤賭產生隨機數區域,選擇下一代個體
printf("\n遺傳到下一代的累加概率\n");
pacc[0]=pcopy[0];
for(i=1;i<N;i++)
{
pacc[i]=pacc[i-1]+pcopy[i];
printf("%f, ",pacc[i]);
}
//每個src[N]都隨機取其中一個pcopy,取得的值pcopy[i]跟pcopy概率大小有關
//模擬輪盤賭方式選擇新一代
printf("\n\n新產生的第%d代,表現型, 適配值\n",count);
srand( (unsigned)time(NULL) );
for(i=0;i<N;i++)
{
prand[i]=(float)( (rand()%101)*0.01 );//0~1的十進制小數 ,精確到0.01
iselect=selectT(prand[i],pacc);
new_select[i]=src[iselect];//產生的新一代,十進制
show[i]=ShowType(new_select[i]);//轉化成表現型
fit[i]=fit_func(show[i]);
DecToBin (new_select[i],i);
sumfit=sumfit+fit[i]; //種羣的適應度總和
printf(" %d %f %f\n",new_select[i],show[i],fit[i]);
}
printf("\n第%d代適配總值\n%f\n",count,sumfit);//第1代
min1=sumfit;
if (min>sumfit)
{
min1=min;
min=sumfit;
}
while(fabs(min-min1)>e&&count<MAX)
{
//從新一代選擇個體交叉
printf("\n隨機產生交叉個體號 ");
srand( (unsigned)time(NULL) );
for(i=0;i<2;i++) //簡單起見交叉數設爲2
{
new_T[i]=rand()%N;//0~10的十進制數 產生的交叉個體
if (i>0)//兩個不同個體交叉
while(new_T[i]==new_T[i-1])
new_T[i]=rand()%N;
printf("%d, ",new_T[i]);
}
srand( (unsigned)time(NULL) );//隨機產生交叉位置
k=rand()%n2;//0~14的十進制數
printf("\n隨機產生交叉位置 %d\n",k);
printf("\n原編碼\n");
for(j=n2;j>=0;j--)
printf("%c",bitary[new_T[0]][j]);
printf("\n");
for(j=n2;j>=0;j--)
printf("%c",bitary[new_T[1]][j]);
printf("\n位置%d後交叉編碼\n",k);
char temp;
for(i=k+1;i<n2+1;i++)//交叉
{
temp=bitary[new_T[0]][i];
bitary[new_T[0]][i]=bitary[new_T[1]][i];
bitary[new_T[1]][i]=temp;
}
for(j=n2;j>=0;j--)
printf("%c",bitary[new_T[0]][j]);
printf("\n");
for(j=n2;j>=0;j--)
printf("%c",bitary[new_T[1]][j]);
//從新一代選擇個體變異
printf("\n隨機產生變異個體號 ");
srand( (unsigned)time(NULL) );
for(i=0;i<1;i++) //簡單起見變異數設爲1個
{
new_M[i]=rand()%N;//0~9的十進制數 產生的變異個體
k=rand()%(n2+1);//0~15的十進制數
printf("%d\n編碼位置 %d\n原編碼\n",new_M[i],k);
for(j=n2;j>=0;j--)
printf("%c",bitary[new_M[i]][j]);
if (bitary[new_M[i]][k]=='0')//變異取反
bitary[new_M[i]][k]='1';
else bitary[new_M[i]][k]='0';
printf("\n位置%d變異後編碼\n",k);
for(j=n2;j>=0;j--)
printf("%c",bitary[new_M[i]][j]);
}
printf("\n");
count++;
//新的bitary即產生第二代
printf("\n新產生的第%d代\n",count);
for(i=0;i<N;i++)
{
for(j=n2;j>=0;j--)
printf("%c",bitary[i][j]);
printf("\n");
}
BinToDec ();//二進制轉十進制
for(i=0;i<N;i++)
{
new_select[i]=src1[i];
show[i]=ShowType(src[i]);//轉化成表現型
fit[i]=fit_func(show[i]);//計算各個適配值(適應度)
sumfit=sumfit+fit[i]; //種羣的適應度總和
printf("%5d, %f, %f\n",src1[i],show[i],fit[i]);
}
printf("\n第%d代適配總值\n%f\n",count,sumfit);
if (sumfit<min)
{
min1=min;
min=sumfit;
}
}
printf("\n\n\n*****************\n over \n*****************\n",sumfit);
}
//////////////////////////子函數////////////////
float ShowType(int a)
{
float temp;
temp=(float)(a*200.0/32767-100);//(2的15次方減1)=32767
return temp;
}
float fit_func(float a)
{
float temp;
temp=a*a;
return temp;
}
void DecToBin (int src,int num)
{
int i;
//注意負數的補碼
if (src<0)
{
src=(int)pow(2,16)-abs(src);
}
for (i=0;i<=n2;i++)
{
bitary[num][i]='0';
bitary0[num][i]='0';
if(src)
{
bitary[num][i]=(src%2)+48;
bitary0[num][i]=(src%2)+48;
src=(int)(src/2);
}
}
}
void BinToDec (void)
{
int i,j;
for(i=0;i<N;i++)
{
src1[i]=0;
for(j=0;j<n2+1;j++)
{
src1[i]=src1[i]+(bitary[i][j]-48)*(int)pow(2,j);
}
}
}
int selectT(float a,float b[10])
{
int i;
for(i=0;i<N;i++)
{
if (a<b[i])
return i;
}
return -1;
}