寫在前面:本博客為本人原創,嚴禁任何形式的轉載!本博客只允許放在博客園(.cnblogs.com),如果您在其他網站看到這篇博文,請通過下面這個唯一的合法鏈接轉到原文!
本博客全網唯一合法URL:http://www.cnblogs.com/acm-icpcer/p/8932874.html
(本篇博客的數學推導參考了:https://blog.csdn.net/qq_27755195/article/details/56597467,源碼為我自己所寫)
對於發散函數:f(x)=x∗sin(10∗π∗x)+2,x屬於[1,2],求其在定義域上的最大值及坐標點。
先看函數圖像:

f
很明顯的震盪函數,對計算機來說,當給定區間很大的時候,即使求一階導數找到駐點,並求函數在駐點處的值,由於駐點會很多,所以本質上來說這還是暴力求解法。
當然了,對於計算機來說,使用泰勒展開式求解也是可以的,但是如果對精度有要求的話,選擇佩亞諾型余項的等價無窮小的階數也是個很頭疼的問題。
那么如果使用遺傳算法(Genetic Algorithm,GA)來求解,先在給定區間均勻鋪開一堆點,觀察點集的收斂狀況,我們可以看出:

點集的收斂速度極快,並且時間花費很少,故而表現出一定的智能性。
雖然很容易陷入局部最優,但是我改進了算法,使用了將點集按fitness值遞減排序+提高點集基因型的變異概率,對於解決此問題的效果反映良好。
現就遺傳算法的各個概念闡述我對運用其求解問題的思想:
1、種群(Population):種群是指用遺傳算法求解問題時,初始給定的多個解的集合。遺傳算法的求解過程是從這個子集開始的。在此問題中,種群就是我在前面說的均勻鋪開的點集。
2、個體(Individual):個體是指種群中的單個元素,它通常由一個用於描述其基本遺傳結構的數據結構來表示。對於單個點,抽象成一個點類,其包含一系列的操作及數據成員。核心的數據成員是:點的x、y值(x為取值為1~2的浮點數。其中,y值還充當fitness度量),以及x值的二進制編碼字符串;核心的函數成員是:根據x的二進制編碼字符串更新x的值的函數,根據x的值更新x的二進制編碼字符串的函數,以及根據x的值求對應的y值的函數。
3、染色體(Chromos):染色體是指對個體進行編碼后所得到的編碼串。其中的每1位稱為基因,若干個基因構成的一個有效信息段稱為基因組。在此問題中,染色體就是2中提到的二進制編碼字符串。
4、適應度(Fitness)函數:適應度函數是一種用來對種群中各個個體的環境適應性進行度量的函數。在此問題中,適應度函數直接用點的y值來充當。
5、遺傳操作(Genetic Operator):遺傳操作是指作用於種群而產生新的種群的操作。標准的遺傳操作包括以下3種基本形式:
(1)選擇(Selection):選擇表現型優秀的個體取代表現型差的個體,即實現淘汰。因為在求解時我使用的點集的點格式上限為100,所以我制定的策略是每次選擇表現最優的前40個個體兩兩交叉,產生20個新的個體去替代表現最差的后20個個體。
(2)交叉(Crosssover):即選擇兩個個體的染色體的基因進行互換。在此,因為我使用的基因長度為20個字符,所以我通過調用5次隨機函數生成5個位置,令這5個位置的字符進行交換。即二進制多隨機點交叉。
(3)變異(Mutation):變異即隨機選擇個體令其染色體的基因進行改變。變異的存在是為了保留產生新的更優解的可能性,即“柳暗花明又一村”,並且在一定程度上防止點集陷入局部最優的情況。在此問題中,我使用的變異策略是:每一次迭代更新種群時,讓每一個個體都有一定的相等概率隨機改變其基因型中的一節。
6、迭代終止條件:從簡處理,令其迭代100次就停止。實驗證明,6次迭代基本上就能收斂到最優解。
流程圖:

f
Basic application of Genetic Algorithm(sample source code;TZ,All Rights Reserved):
/* this code was first initiated by TZ,COI,HZAU contact email:xmb028@163.com personal website:wnm1503303791.github.io personal blogs:www.cnblogs.com/acm-icpcer/ this code has been posted on my personal blog,checking URL:www.cnblogs.com/acm-icpcer/p/8932874.html Copyright 2018/4/22 TZ. All Rights Reserved. */ //mission:求發散函數f(x)=x*sin(10*pi*x)+2在[1,2]上能取到的最大值 #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<set> #include<list> #include<deque> #include<map> #include<queue> #include<ctime> using namespace std; const double PI=3.14/*15926535897932384626*/; class point { private: double x,y; public: char d_a[21];//digital x point() { d_a[20]='\0'; } double getx() { return x; } double setx(double mx) { x=mx; } double gety() { return y; } double sety(double my)//not being used { y=my; } bool renew_x()//renew x accroding to d_a { double x1=1; for(int i=2;i<20;i++) { if(d_a[i]=='1') { x1+=pow(2,-(i-1)); } else continue; } x=x1; return true; } bool renew_y()//renew y accroding to x { y=x*sin(10*PI*x)+2; return true; } bool renew_d_a()//renew d_a accroding to x { double a=x-1; d_a[0]='1'; d_a[1]='.'; for(int i=2;i<20;i++) { a=a*2; if(a>=1) { d_a[i]='1'; a-=1; } else d_a[i]='0'; } return true; } }; bool y_sort(point p[]) { for(int i=0;i<100;i++) { double max=p[i].gety(); int key=i; for(int j=i+1;j<100;j++) { if(p[j].gety()>max) { max=p[j].gety(); key=j; } } point temp=p[i]; p[i]=p[key]; p[key]=temp; } return true; } //令fitness函數排名前40的個體的基因型兩兩交叉 ,並取代后20名,以此實現進化 bool crossgene(point p[]) { srand((unsigned)time(NULL)); point new_general[20]; int t=0; //j=0,2,4,...,,38 for(int i=0;i<20;i++) { new_general[i]=p[t]; t+=2; } t=1;//reuse the 't' variable for(int i=0;i<20;i++) { //多點隨機交叉5次 for(int j=0;j<5;j++) { int position=rand()%20; if(position>=2)//從字符串第三位開始交叉 { new_general[i].d_a[position]=p[t].d_a[position]; } } t+=2;//j=1,3,5,...,39 } for(int i=0;i<20;i++) { new_general[i].renew_x(); new_general[i].renew_y(); p[i+80]=new_general[i]; } return true; } //模擬變異過程,以防止局部最優 int variation(point p[]) { srand((unsigned)time(NULL)); int flag=0; for(int i=0;i<100;i++)//每一個個體都有相同的變異概率 { if(rand()%50==1)//設置變異概率為1/50 { //假設每個個體的基因一次變異只改變一節 int position=rand()%20; if(position>=2)//從基因的第三位開始變異 { flag++; if(p[i].d_a[position]=='0') p[i].d_a[position]='1'; else p[i].d_a[position]='0'; } p[i].renew_x(); p[i].renew_y(); } } return flag; } int main() { point pset[100]; for(int i=0;i<100;i++) { pset[i].setx(1+0.01*i); pset[i].renew_y(); pset[i].renew_d_a(); //cout<<pset[i].d_a<<endl; } //present the situation before sorting /* for(int i=0;i<100;i++) { cout<<p[i].gety()<<endl; } */ y_sort(pset); //following code used to present the result after sorting /* cout<<"break testing:"<<endl; for(int i=0;i<100;i++) { cout<<pset[i].gety()<<endl; } cout<<pset[0].getx()<<endl; */ //由於不便找合適的終止條件(我證明了:不管是求一階導數找駐點還是求二階導數找拐點都不能找出最值點),故直接令其循環100代以求最佳結果 for(int i=0;i<100;i++) { crossgene(pset); int times=variation(pset); y_sort(pset); cout<<i<<":"<<'('<<pset[0].getx()<<','<<pset[0].gety()<<')'<<endl;//輸出每代的最優個體 if(times>0) cout<<"variation happened "<<times<<" times"<<" in this general!"<<endl; } /* cout<<"finall result"<<endl; for(int i=0;i<100;i++) { cout<<pset[i].gety()<<endl; } */ cout<<"the maxium point is:"<<'('<<pset[0].getx()<<','<<pset[0].gety()<<')'<<endl; return 0; }
運行結果:

結果令人滿意
tz
first posted@COI HZAU,2018/4/24
last updated@COI HZAU,2018/5/8
