人工智慧-遺傳演算法解決推箱子問題現實
由於各種原因,可能存在諸多不足,歡迎斧正!
一、研究背景
推箱子游戲中的路徑查詢問題—給定一方格,求兩點最短距離。
傳統求兩點最短路徑的演算法有:
1.通用的搜尋演算法
2.解決無負權邊的帶權有向圖的單源最短路問題的Dijkstra演算法
3.求解含負權邊的帶權有向圖的單源最短路徑問題的Bellman-Ford演算法
4.Bellman-ford演算法改進版快速求解單源最短路經的Shortest Path Faster Algorithm
…
以上演算法雖能百分之百找到最短路徑,但是隨著問題規模的擴大,隨之而來的是組合爆炸問題,在有限的時間範圍內難以求解問題。於是人們開始探索在有限時間內可以求得最優解的近似解。遺傳演算法求解兩點最短路經就是在這樣的背景下誕生的。
遺傳演算法是基於生物進化原理的一種全域性性優化演算法,是借鑑生物的自然選擇和遺傳進化機制而開發出的一種全域性優化自適應概率搜尋演算法,是生物遺傳技術和計算機技術結合的產物。它採用的是啟發性知識的智慧搜尋演算法,在高空問複雜問題上比以往有更好的結果。
本題大致就是求兩點的最短路徑,所不同的是我們沒有給定問題的解空間,即在沒有給定兩點可達路徑的基礎上求兩點的最短路徑。可達路徑基本可以經過遺傳演算法演化而來。
二、組內分工
成員名 |
身份 |
分工 |
徐進 |
組長 |
程式實現 |
熊凱平 |
組員 |
論文撰寫 |
黃江 |
組員 |
演算法設計 |
三、問題分析
給出n*m的二維方格,給定障礙物(數目及座標),起點和終點,問繞過障礙物從起點到終點的最短路徑。
基於題目,構建符合題意的模型。使用者選擇二維方格的行和列,即n和m。然後隨機生成障礙物,為求合理真實,障礙物數目應控制在一定範圍內,位置既不能太稀也不能太密,起點終點隨機分佈。我們的設計目標是儘可能不認為干預,讓箱子自己跑。
在此說明,題目雖說是推箱子,但更我們玩的推箱子游戲是有所區別的。有圖為證:
此圖是例項給出的。不考慮推的方向,在此推箱子轉化為路徑查詢問題。
四、演算法設計
本題核心是求兩點(繞過障礙的前提下)最短的哈夫曼距離。
一個很好地解決方案是BFS
本題指定用遺傳演算法。於是問題可以細分為兩個方面分析:
1.找到從起點到終點的路徑
2.在所有的路徑中找到儘可能短的。
在遺傳演算法中二者必須得到權衡。
本題沒有用到高階的資料結構,用到了STL中的順序容器Vector來儲存每條染色體。然後開了些陣列。
遺傳演算法 ( GA , Genetic Algorithm ) ,也稱進化演算法 。 遺傳演算法是受達爾文的進化論的啟發,借鑑生物進化過程而提出的一種啟發式搜尋演算法。
我們的遺傳演算法通用流程框圖
種群(Population):生物的進化以群體的形式進行,這樣的一個群體稱為種群。
個體:組成種群的單個生物。
基因 ( Gene ):一個遺傳因子。
染色體 ( Chromosome ) :包含一組的基因。
生存競爭,適者生存:對環境適應度高的、牛B的個體參與繁殖的機會比較多,後代就會越來越多。適應度低的個體參與繁殖的機會比較少,後代就會越來越少。
遺傳與變異:新個體會遺傳父母雙方各一部分的基因,同時有一定的概率發生基因變異。
五、演算法實現
編碼:需要將問題的解編碼成字串的形式才能使用遺傳演算法。最簡單的一種編碼方式是二進位制編碼,即將問題的解編碼成二進位制位陣列的形式。
§ 00 = up
§ 01 = right
§ 10 = down
§ 11 = left
每條染色體由只含0、1的偶數字符串組成,以上是4個基本基因片段。本題染色體是變長的(需考慮到具體題目),在不人為干預的情況下染色體所代表的運動方向是不確定的,這給問題的求解帶來難度,如果染色體都過短則可能得不到路徑;反之,如果染色體都過長,則可能得不到最短路徑。我的方案是將染色體長度控制在[minlen,maxlen]之間,其中minlen為橫座標之差與縱座標之差的和,maxlen為n*m-number(number為障礙物數目)。這樣基本可以保證有染色體可以進化成最短路徑。所有的遺傳操作的基本單位都是2位0、1串。
適應度函式 ( Fitness Function ):用於評價種群中某個染色體的適應度,用Fitness(x)表示。本題及要找路徑,還要找到最短的路徑,即在從起點到目標點的可達路徑都不知道的情況下的要篩選出最短的路徑。這是本題面臨的主要挑戰。課件上給出的適應度函式如下:
Fitness[i] = 1 – (distance[i] / max possible distance)
其中distance[i]為染色體表示運動的終點與目標點的哈夫曼距離(在不可以障礙物的情況下)。這樣的適應度函式如果不做修改時很可能陷入區域性最優的。如圖:
上圖種群很可能找不到最優解。因為要找到最短的可達路徑必須繞過去,而在繞的過程中經過上述適應度函式計算出來的適應值是很大程度上減小的。
我的解決方案是將distance[i]修改為BetDis[x][y],BetDis[x][y]表示(x,y)與目標點的考慮障礙物的最短距離。從而可以有效避免上述原因造成的過早陷入區域性最優解。關於BetDis[x][y]可以通過預處理求得。通過一次BFS可以求得方格中所有點到目標點的最短距離,時間複雜度為O(n*m),空間複雜度也很低。
上述適應度度函式只能保證儘可能接近目標點,不能同時得到較短的路徑。我們做了一些嘗試,已知最短路徑應該與目標的接近程度成正比,與路徑長度成反比。我嘗試將適應度函式修改為
NewFitness[i] = a*Fitness[i]+b/len;
或 NewFitness[i] = a*Fitness[i]/len;
其中a,b為待定常數,len為染色體Chro[i]的長度。
但最終沒有找到比較合適的a,b。
於是我們還是選擇了 Fitness[i] = 1 – (BetDis[x][y]/ max possible distance) 作為適應度函式。我們通過修正染色體來權衡二者。由於每條染色體都要求適應度,一個想法是在求適應度的過程中完成染色體的修正。我的修正方案有兩個:
1.若某染色體Chro[i]在運動過程中經過目標節點,則直接將Fitness[i]置為1,同時截斷後面的基因片段,即縮短染色體。如:
01 10 10 01 01 00 11 00 10 10 01 10 11 11 11 00 00 10 01
前8個基因片段就到達目標點,則後面的都是有害的,於是截斷,修正染色體為
01 10 10 01 01 00 11 00
看似不符自然規律,但卻給解題帶來極大的優化。
2.若某染色體Chro[i]在運動過程中遇到不合法狀態,即走到障礙物上或越出方格,則要修正相應的基因片段,我們採用不回溯法。若當前狀態不合法,則轉入下一個狀態,
Direction=(Direction+1)%4,其中Direction取值為0,1,2,3。
Direction=0,對應基因片段00
Direction=1,對應基因片段01
Direction=2,對應基因片段10
Direction=3,對應基因片段11
這樣的修正方法導致染色體是可以回走的。回溯法可以避免回走,但一個長度為Size的染色體最壞的時間複雜度為O(3^size),即當前染色體所走的每一步都要回溯,這樣的時間複雜度是難以接受的。所以我們選擇了不回溯的。如果繁衍代數足夠多,在大量交叉、變異的前提下可以降低迴走的影響。
遺傳演算法有3個最基本的操作:選擇,交叉,變異。
選擇:選擇適應度高的染色體個體存活下來,淘汰適應度地的染色體個體。常用的選擇策略是 “比例選擇”,也就是個體被選中存活的概率與其適應度函式值成正比。假設群體的個體總數是PopulationSize,那麼那麼一個體Chro[i]被選中存活的概率為Fitness(Chro[i])/( Chro[0]+ Chro[1] + …….. + Chro[PopulationSize-1] ) 。比例選擇演算法可以通過“輪盤賭演算法”( Roulette Wheel Selection ) 實現。關於輪盤賭演算法的實現在這就不多說了。
為了防止進化過程中產生的最優解被交叉或變異所破壞,可以將每一代中的最優解原封不動的複製到下一代中,即我們採用了精英主義(Elitist Strategy)選擇,這是我們採用的一種優化方案。
交叉(Crossover):基因交叉,就是把兩個父體部分結構加以替換,生成新的個體的操作,本題的交叉操作比較簡單,就是隨機選擇選擇長度相同的染色體進行交叉,我們交叉的起始位置隨機。如:
交叉前:
父輩1: 01 10 10 01 0000 10 11 0101 10 10 00 00 11 11 00 01
父輩2: 01 10 00 01 10 11 10 00 01 10 10 10 00 10 11 01 01 11
交叉後
子輩1: 01 10 10 01 00 11 10 00 01 01 10 10 00 00 11 11 00 01
子輩2: 01 10 00 01 1000 10 11 0110 10 10 00 10 11 01 01 11
所有交叉都是在交叉率CrossoverRate的控制下進行的,通過交叉率計算出當前種群的交叉數CrossoverNum。
While(交叉數CrossoverNum--)
{
隨機選擇等長的兩條染色體;
隨機選擇起始位置進行交叉;
}
為了使程式效率不致太低,我們在交叉過程中並沒有防止含非法狀態的染色體產生,而是在求適應度的同時修正染色體。
變異(Mutation):在基因交叉之後產生的子代染色體個體中,有一部分個體的某個基因片段以很小的概率發生轉變,這個過程稱為變異(Mutation)。我們的變異操作也變較簡單,隨機選擇染色體,隨機選擇染色體變異位置,隨機選擇變異方向,這樣可以保證過早陷入區域性最優解,因為假設種群沒有最優解所必需的某個基因,而選擇、交叉操作不產生新基因,只有通過變異完成。如:
變異前:10 01 01 01 10 10 11 01 11 00 11 11 11 00 11 00 10 10 01
變異後: 10 01 11 01 10 10 11 01 11 00 11 11 11 00 11 00 10 10 01
所有變異都是在變異率MutationRate的控制下進行的,通過變異率計算出當前種群的變異數MutationNum。
While(變異數MutationNum--)
{
隨機選擇某個染色體在隨機位置向隨機方向變異;
}
同交叉操作,為了使程式效率不致太低,我們在變異過程中並沒有防止含非法狀態的染色體產生,而是在求適應度的同時修正染色體。
六、演算法實現虛擬碼
遺傳演算法虛擬碼GeneticAlgorithm
{
while(當前繁衍代數小於設定的繁衍代數)
{
當前繁衍代數加1;
進行選擇操作Selection();
進行交叉操作Crossover();
進行變異操作Mutation();
}
}
演算法的時間複雜度為O(Generation*popsize^2),在演算法時間複雜度的計算過程中除去視覺化時視窗延遲重新整理的時間損耗;空間複雜度為O(n).
七、程式執行結果
八、參考論文
九.源程式
BFS演算法實現
#include<iostream>
#include<windows.h>
#include<cstdio>
#include<string>
#include<queue>
#include<ctime>
#include<gl/glut.h>
using namespace std;
const int MAXNGRID=1000;//設定方格的總數
const int MAXN=50;//設定最大長、寬
int winHeight,winWidth;//當前視窗的長寬
bool MyLoop,SetRow,SetColumn,IsRun,First;//幾個開關變數控制滑鼠響應
int GridLen[2][10]=
{
{4,6,8,10,12,14,16,18,20,22},
{4,6,8,10,12,14,16,18,20,22}
};
int Move[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct state
{
int x,y;
string path;
}Start,Target;
queue<state>Qu;
struct point
{
int x,y;
};//建立方格的座標(規則同OpenGL),便於投影到視區
class grid
{
private :
int Row,Column;
int TotalPointNumber,UsedPointNumber;
string OptimalPath;
point Point[MAXNGRID];
double RowInc,ColumnInc;
bool Mark[MAXN][MAXN];//標記有沒有訪問
public:
int GetRow();
int GetColumn();
void SetRow(int);
void SetColumn(int);
bool BFS();
void DisplayGrid();
void InitGrid();
void DDALine(int,int,int,int);
}Grid;
/************************************************
*函式名GetRow
*無引數
*獲得方格行數
*************************************************/
int grid::GetRow()
{
return this->Row;
}
/************************************************
*函式名GetColumn
*無引數
*獲得方格列數
*************************************************/
int grid::GetColumn()
{
return this->Column;
}
/************************************************
*函式名SetRow
*無引數
*設定方格行數
*************************************************/
void grid::SetRow(int row)
{
this->Row=row;
}
/************************************************
*函式名SetColumn
*無引數
*設定方格列數
*************************************************/
void grid::SetColumn(int column)
{
this->Column=column;
}
/************************************************
*函式名BFS
*無引數
*利用廣度優先尋找最短路徑
*************************************************/
bool grid::BFS()
{
state Now,Next;
while(!Qu.empty())
Qu.pop();
Start.path.clear();
Qu.push(Start);
while(!Qu.empty())
{
Now=Qu.front();Qu.pop();
if(Now.x==Target.x&&Now.y==Target.y)
{
this->OptimalPath=Now.path;
cout<<OptimalPath<<endl;
return true;
}
for(int i=0;i<4;i++)
{
int tx=Now.x+Move[i][0];
int ty=Now.y+Move[i][1];
if(!(tx<0||tx>=this->Row||ty<0||ty>=this->Column||Mark[tx][ty]))
{
Next.x=tx;
Next.y=ty;
Next.path=Now.path;
/*
if(0==i)Next.path+='U';
else if(1==i)Next.path+='D';
else if(2==i)Next.path+='L';
else if(3==i)Next.path+='R';
this->Mark[tx][ty]=true;
*/
if(0==i)Next.path+="10";
else if(1==i)Next.path+="00";
else if(2==i)Next.path+="11";
else if(3==i)Next.path+="01";
this->Mark[tx][ty]=true;
Qu.push(Next);
}
}
}
return false;
}
/*******************************************************
*函式名DDALine
*無引數
*利用數值微分法由確定的兩個端點畫直線
********************************************************/
void grid::DDALine(int lx1,int ly1,int lx2,int ly2)
{
int dx,dy,epsl,k;
float x,y,xIncre,yIncre;
dx=lx2-lx1; dy=ly2-ly1;
x=lx1; y=ly1;
if(abs(dx)>abs(dy))
epsl=abs(dx);
else epsl=abs(dy);
xIncre=(float)dx/(float)epsl;
yIncre=(float)dy/(float)epsl;
glBegin(GL_POINTS);//
for(k=0;k<=epsl;k++)
{
glVertex2i(int(x+0.5),int(y+0.5));
x+=xIncre;
y+=yIncre;
}
glEnd();
}
/*******************************************************
*函式名InitGrid
*無引數
*顯示每局的障礙、起點、目標點
********************************************************/
void grid::InitGrid()
{
this->UsedPointNumber=0;
this->TotalPointNumber=this->Row*this->Column;
this->RowInc=winHeight/this->Row,this->ColumnInc=winWidth/this->Column;
memset(this->Mark,false,sizeof(this->Mark));
srand((unsigned)clock());
do
{
this->UsedPointNumber=rand()%(this->TotalPointNumber);
}while(this->UsedPointNumber>=this->TotalPointNumber/4||this->UsedPointNumber<=this->TotalPointNumber/8);
//儘可能合理的控制障礙的數目
int tmp=0;
while(tmp<this->UsedPointNumber)//產生障礙的座標並儘可能保證適當數目的障礙連成一串
{
int x=rand()%(this->Row),y=rand()%(this->Column),bel=rand()%2;
if(0==bel)//(x-1,y),(x,y),(x+1,y)
{
if(x-1>=0)
{
this->Point[tmp].y=y;this->Point[tmp].x=x-1;tmp++;
this->Mark[x-1][y]=true;
if(tmp>=this->UsedPointNumber)break;
}
if(x<this->Row)
{
this->Point[tmp].y=y;this->Point[tmp].x=x;tmp++;
this->Mark[x][y]=true;
if(tmp>=this->UsedPointNumber)break;
}
if(x+1<this->Row)
{
this->Point[tmp].y=y;this->Point[tmp].x=x+1;tmp++;
this->Mark[x+1][y]=true;
if(tmp>=this->UsedPointNumber)break;
}
}
else //(x,y-1),(x,y),(x,y+1)
{
if(y-1>=0)
{
this->Point[tmp].y=y-1;this->Point[tmp].x=x;tmp++;
this->Mark[x][y-1]=true;
if(tmp>=this->UsedPointNumber)break;
}
if(y<this->Column)
{
this->Point[tmp].y=y;this->Point[tmp].x=x;tmp++;
this->Mark[x][y]=true;
if(tmp>=this->UsedPointNumber)break;
}
if(y+1<this->Column)
{
this->Point[tmp].y=y+1;this->Point[tmp].x=x;tmp++;
this->Mark[x][y+1]=true;
if(tmp>=this->UsedPointNumber)break;
}
}
}
int sx,sy,tx,ty;
do
{
do
{
sx=rand()%(this->Row);
sy=rand()%(this->Column);
}while(this->Mark[sx][sy]);//控制起點無障礙物
do
{
tx=rand()%(this->Row);
ty=rand()%(this->Column);
}while(this->Mark[tx][ty]);//控制目標點無障礙物
}while(abs(sx-tx)+abs(sy-ty)<(this->Row+this->Column)/3);//控制起點和目標點保持適當遠的距離不小於方格任意兩點最大哈夫曼距離的三分之一
this->Mark[sx][sy]=true;
Start.x=sx;Start.y=sy;
// Mark[tx][ty]=true;
Target.x=tx;Target.y=ty;
DisplayGrid();
if(IsRun)First=false;
}
/*******************************************************
*函式名DisplayGrid
*無引數
*顯示視窗的當前狀態
********************************************************/
void grid::DisplayGrid()
{
int Left,Right,button,top;
Left=0,Right=winWidth,button=0,top=winHeight;
int lx1,ly1,lx2,ly2;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0f,0.0f,1.0f);
glPointSize(5);
//繪製放個水平線
for(int i=0;i<=winWidth;i+=this->ColumnInc)
{
DDALine(Left+i,button,Left+i,top);
}
//繪製放個豎直線線
for(int i=0;i<=winHeight;i+=this->RowInc)
{
DDALine(Left,button+i,Right,button+i);
}
//繪製障礙物
for(int i=0;i<this->UsedPointNumber;i++)
{
glColor3f(1.0f,0.0f,0.0f);
//glRectf(Left+PerRow[i]*ColumnInc,button+PerColumn[i]*RowInc,Left+PerRow[i]*ColumnInc+ColumnInc,button+PerColumn[i]*RowInc+RowInc);
glRectf(Left+this->Point[i].y*this->ColumnInc,button+this->Point[i].x*this->RowInc,
Left+this->Point[i].y*this->ColumnInc+this->ColumnInc,button+this->Point[i].x*this->RowInc+this->RowInc);
}
int inc=3;
glColor3f(1.0f,1.0f,0.0f);
glRectf(Left+Start.y*this->ColumnInc+inc,button+Start.x*this->RowInc+inc,
Left+Start.y*this->ColumnInc+this->ColumnInc-inc,button+Start.x*this->RowInc+this->RowInc-inc);
glColor3f(1.0f,.0f,1.0f);
glRectf(Left+Target.y*this->ColumnInc+inc,button+Target.x*this->RowInc+inc,
Left+Target.y*this->ColumnInc+this->ColumnInc-inc,button+Target.x*this->RowInc+this->RowInc-inc);
if(!First)//不是第一次初始化視窗,則會出現相應染色體對應的路徑
{
int tcnt=this->UsedPointNumber;
BFS();
int size=this->OptimalPath.size();
int tx=Start.x,ty=Start.y;
for(int i=0;i<size-1;i++)
{
if(this->OptimalPath[i]=='D')
{
tx=tx+1;ty=ty;
}
else if(this->OptimalPath[i]=='U')
{
tx=tx-1;ty=ty;
}
else if(this->OptimalPath[i]=='L')
{
tx=tx;ty=ty-1;
}
else if(this->OptimalPath[i]=='R')
{
tx=tx;ty=ty+1;
}
this->Point[this->UsedPointNumber].x=tx;this->Point[this->UsedPointNumber].y=ty;
this->UsedPointNumber++;
}
for(int j=tcnt;j<this->UsedPointNumber;j++)
{
glColor3f(0.0f,1.0f,0.0f);
glRectf(Left+this->Point[j].y*this->ColumnInc,button+this->Point[j].x*this->RowInc,
Left+this->Point[j].y*this->ColumnInc+this->ColumnInc,button+this->Point[j].x*this->RowInc+this->RowInc);
}
this->UsedPointNumber=tcnt;
}
glFlush();
}
/*******************************************************
*函式名init
*無引數
*初始化函式
********************************************************/
void init()
{
glClearColor(0.0f,0.0f,0.0f,1.0f);
glMatrixMode(GL_PROJECTION); //設定投影引數
gluOrtho2D(0.0f, 1000.0f,0.0f,600.0f);
MyLoop=false;
SetRow=false;SetColumn=false;
IsRun=false;
First=true;
}
/*******************************************************
*函式名Display()
*無引數
*視窗建立時顯示背景色
********************************************************/
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
}
/*******************************************************
*函式名ChangeSize
*無引數
*視窗發生變化時完成初始化
********************************************************/
void ChangeSize(int w, int h)
{
winWidth=w;winHeight=h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,winWidth,0.0,winHeight);
}
/*******************************************************
*函式名timer
*無關引數
*定期重新整理視窗
********************************************************/
void Timer(int p)
{
Grid.DisplayGrid();
}
/*******************************************************
*函式名MousePlot
*4個引數:按鍵型別、動作、對應的座標
*滑鼠響應函式
********************************************************/
void MousePlot(int button,int action,int x,int y)
{
if(SetRow&&SetColumn)
{
if(button==GLUT_LEFT_BUTTON&&action==GLUT_DOWN)
{
if(!MyLoop&&!IsRun)
Grid.InitGrid();
else MyLoop=false;
}
else if(button==GLUT_MIDDLE_BUTTON&&action==GLUT_DOWN)
{
MyLoop=true;
IsRun=true;
First=false;
glutTimerFunc(200,Timer,0);
}
}
}
/*******************************************************
*函式名ProcessMenu
*一個引數,選單選項
*選單響應函式
********************************************************/
void ProcessMenu(int value)
{
if(IsRun)//在執行過程中重置方格
{
SetRow=false;
SetColumn=false;
IsRun=false;
First=true;
MyLoop=false;
}
if(value<=10)
{
Grid.SetRow(GridLen[0][value-1]);
SetRow=true;
}
else if(value<=20)
{
Grid.SetColumn(GridLen[1][value-11]);
SetColumn=true;
}
}
/*******************************************************
*函式名MyCreateMenu
*無選單
*初始化選單
********************************************************/
void MyCreateMenu()
{
int SetWidth=glutCreateMenu(ProcessMenu);
glutAddMenuEntry("4",1);
glutAddMenuEntry("6",2);
glutAddMenuEntry("8",3);
glutAddMenuEntry("10",4);
glutAddMenuEntry("12",5);
glutAddMenuEntry("14",6);
glutAddMenuEntry("16",7);
glutAddMenuEntry("18",8);
glutAddMenuEntry("20",9);
glutAddMenuEntry("22",10);
int SetHeight=glutCreateMenu(ProcessMenu);
glutAddMenuEntry("4",11);
glutAddMenuEntry("6",12);
glutAddMenuEntry("8",13);
glutAddMenuEntry("10",14);
glutAddMenuEntry("12",15);
glutAddMenuEntry("14",16);
glutAddMenuEntry("16",17);
glutAddMenuEntry("18",18);
glutAddMenuEntry("20",19);
glutAddMenuEntry("22",20);
int Choice=glutCreateMenu(ProcessMenu);
glutAddSubMenu("水平方向方格數",SetWidth);
glutAddSubMenu("垂直方向方格數",SetHeight);
glutCreateMenu(ProcessMenu);
glutAddSubMenu("設定",Choice);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
int main(int argc, char *argv[])
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);
glutInitWindowPosition(0, 0);
glutInitWindowSize(1000, 600);
glutCreateWindow("BFS演算法在方格中的求最短路徑動畫演示");
glutDisplayFunc(Display);
glutReshapeFunc(ChangeSize);
glutMouseFunc(MousePlot);
MyCreateMenu();
init();
glutMainLoop();
return 0;
}
用BFS引導的
#include<windows.h>
#include<gl/glut.h>
#include<iostream>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<iomanip>
#include<map>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXNGRID=1000;//設定方格的總數
const int MAXROC=50;//設定最大長、寬
const int MAXCHRO=250;//染色體條數
const double eps=1e-10;
int winHeight,winWidth;//當前視窗的長寬
bool MyLoop,SetRow,SetColumn,IsRun,First;//幾個開關變數控制滑鼠響應
int GridLen[2][10]=
{
{4,6,8,10,12,14,16,18,20,22},
{4,6,8,10,12,14,16,18,20,22}
};
int Move[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct state
{
int x,y;
int dis;
string path;
}Start,Target;
queue<state>Qu;
struct point
{
int x,y;
};//建立方格的座標(規則同OpenGL),便於投影到視區
map<int,string>Mat;
void InitMap()
{
Mat[0]="00";Mat[1]="01";Mat[2]="10";Mat[3]="11";
}
class grid
{
private :
int Row,Column; //方格的行列
int TotalPointNumber,UsedPointNumber; //總的點數、已用點數
int GenerationNum,PopulationSize,MaxDis,MinDis;//繁殖代數、種群規模、最大距離、最短距離
int BetDis[MAXROC][MAXROC];//BetDis[x][y]表示(x,y)到目標點的最短距離
int tcnt; //全域性臨時變數
string OptimalPath;//遺傳演算法求得的最短路徑
string BFSPath; //BFS演算法最短路徑
double OptimalFitness;//已找到的最短路徑
point Point[MAXNGRID]; //待列印的點
double RowInc,ColumnInc; //視窗的行列步長
bool Mark[MAXROC][MAXROC];//標記有沒有訪問
double MutationRate; //變異率
double CrossoverRate; //雜交率
double MutationNum; //當代變異個數
double CrossoverNum; //當代交叉個數
int now_Gen; //當前代數
double Fitness[MAXCHRO]; //適應度
double Random[MAXCHRO]; //產生的隨機數
double ProSec[MAXCHRO]; //選擇概率
double ProAccu[MAXCHRO]; //累積選擇概率
vector<string>Chro; //染色體
bool IsUsed[MAXCHRO];//標記當前繁殖代數下該染色體是否已經交叉或變異過
public:
int GetRow();
int GetColumn();
void SetRow(int);
void SetColumn(int);
bool BFS1();
bool BFS2();
void DisplayGrid();
void InitGrid();
void DDALine(int,int,int,int);
void GetMinMaxDis();
bool IsBump(int,int);
bool GetNext(int &,int &,int);
int RunRoute(int &,int &,string &);
double GetFitness(string &);
void Manu_Init();
int GetRand(int);
void Mutation();
void My_Rand();
void Get_Sec_num();
void Selection();
void GetCros(int,int,int,int);
void Crossover();
friend void Timer(int);
friend void MousePlot(int,int,int,int);
}Grid;
/************************************************
*函式名GetRow
*無引數
*獲得方格行數
*************************************************/
int grid::GetRow()
{
return this->Row;
}
/************************************************
*函式名GetColumn
*無引數
*獲得方格列數
*************************************************/
int grid::GetColumn()
{
return this->Column;
}
/************************************************
*函式名SetRow
*無引數
*設定方格行數
*************************************************/
void grid::SetRow(int row)
{
this->Row=row;
}
/************************************************
*函式名SetColumn
*無引數
*設定方格列數
*************************************************/
void grid::SetColumn(int column)
{
this->Column=column;
}
/******************************************************************************
*函式名GetMinMaxDis
*無引數
*計算路徑可能的最小距離和最大距離
*******************************************************************************/
void grid::GetMinMaxDis()
{
this->MaxDis=(this->Row)*(this->Column);
this->MinDis=abs(Target.x-Start.x)+abs(Target.y-Start.y);
}
/******************************************************************************
*函式名IsBump
*兩個引數分別表示橫座標、縱座標
*判斷當前是否合法,及是否撞牆或越界
*******************************************************************************/
bool grid::IsBump(int tRow,int tColumn)
{
if(tRow<0||tRow>=this->Row||tColumn<0||tColumn>=this->Column||this->Mark[tRow][tColumn])
return true;
return false;
}
/******************************************************************************
*函式名GetNext
*當前的座標,移動方向
*若下一個是合法狀態,移到該狀態,返回true;否則不做操作返回false
*******************************************************************************/
bool grid::GetNext(int &tRow,int &tColumn,int tDirection)
{
int incx,incy;
if(0==tDirection) incx=1,incy= 0;
else if(1==tDirection) incx= 0,incy= 1;
else if(2==tDirection) incx= -1,incy= 0;
else incx= 0,incy=-1;
if(!(this->IsBump(tRow+incx,tColumn+incy)))
{
tRow=tRow+incx,tColumn=tColumn+incy;
return true;//當前方向有效
}
return false;//當前方向無效
}
/******************************************************************************
*函式名:RunRoute
*3個引數分別表示當前點的座標,和當前處理染色體的引用
* 尋找並記錄染色體對應的有效路徑同時修改不合法的染色體
*******************************************************************************/
int grid::RunRoute(int &tRow,int &tColumn,string &Chromosome)
{
int size=Chromosome.size();
int Direction;
int len=1;
string newchro;
string tmp;
for(int i=0;i<size;i+=2)
{
tmp.clear();
tmp+=Chromosome[i];
tmp+=Chromosome[i+1];
if(tmp=="00")
{
Direction=0;
}
else if(tmp=="01")
{
Direction=1;
}
else if(tmp=="10")
{
Direction=2;
}
else
{
Direction=3;
}
int tR=tRow,tC=tColumn;
while(!this->GetNext(tRow,tColumn,Direction))
//當前方向不合法,則轉到下一方向;如果除去來的其他3個方向都不合法,則返回當前位置的前一個位置
//此處最好的方法是回溯法,類比深搜,但時間複雜度是不能接受的,最壞可達O(4^n)
{
Direction=(Direction+1)%4;
tmp=Mat[Direction];
}
Chromosome[i]=tmp[0];//修正染色體
Chromosome[i+1]=tmp[1];
newchro+=tmp;
if(tRow==Target.x&&tColumn==Target.y)
{
Chromosome=newchro;//找到目標點也做修改染色體,保證路徑儘可能短
return len;
}
this->Point[this->UsedPointNumber].x=tRow;
this->Point[this->UsedPointNumber].y=tColumn;
this->UsedPointNumber++;
len++;
}
return len;
}
/******************************************************************************
*函式名GetFitness
*一個引數為當前處理染色體的引用
*計算個體適應度
*******************************************************************************/
double grid::GetFitness(string &Chromosome)
{
int Row=Start.x,Column=Start.y;
int len=this->RunRoute(Row,Column,Chromosome);
if(Row==Target.x&&Column==Target.y)
{
return 1.0;
}
//double tmpdis=abs(Target.x-Row)+abs(Target.y-Column);
double tmpdis=this->BetDis[Row][Column];//此時tmpdis表示到目標點的最短路徑長
double ret=1-tmpdis/(this->MaxDis);
//double ret=1.0*(1-tmpdis/MaxDis)+1.0/len;//保證len不為0
return ret;
}
/******************************************************************************
*函式名ChromosomeCmp
*兩個引數,分別表示兩條染色體
*依據長度從小到大對染色體進行排序,方便交叉
*******************************************************************************/
bool ChromosomeCmp(string tmpa,string tmpb)
{
return tmpa.size()<tmpb.size();
}
/******************************************************************************
*函式名Manu_Init
*無引數
*隨機初始化種群
難點在於染色體長度和表徵的路徑的控制,在此我沒有找到對應隨機遊戲局面的比較好的初始化方案
原因在於局面是隨機任意的,且找路徑的個體是看不知全域性的,只能隨便跑,我認為人為干預是不恰當的
*******************************************************************************/
void grid::Manu_Init()
{
this->Chro.clear();
this->GetMinMaxDis(); //獲得最大值、最小值
int minlen=this->MinDis;
int maxlen=((this->MaxDis)-(this->UsedPointNumber))/3;
this->PopulationSize=200;///
this->GenerationNum=1000;
srand((unsigned)clock());
string tmp;
for(int i=0;i<this->PopulationSize;i++)
{
tmp.clear();
int len;
do
{
len=rand()%maxlen;
if(len%2)len++;//保證染色體長度為偶數
}while(len<minlen);//控制染色體長度在[minlen,maxlen)之間
while(len)
{
int id=rand()%4;
tmp+=Mat[id];
len--;
}
(this->Chro).push_back(tmp);
}
this->OptimalPath=this->Chro[0];
this->OptimalFitness=0;
this->CrossoverRate=0.5;
this->MutationRate=0.05;
}
/******************************************************************************
*函式名GetRand
*一個引數,表示上限
*返回【0,len)的隨機數
*******************************************************************************/
int grid::GetRand(int len)
{
double be=1.0/(len);
double Rand=1.0*rand()/(RAND_MAX);
int ret=(int)(Rand/be);
if(ret>=len)ret--;
return ret;
}
/******************************************************************************
*函式名Mutation()
* 無引數
*隨機選染色體,再隨機選一個基因片段變異,變異方向不確定
*******************************************************************************/
void grid::Mutation()
{
int belong,id,to,size=this->Chro.size();
string tmp;
srand((unsigned)clock());
memset(this->IsUsed,0,sizeof(this->IsUsed));
while(this->MutationNum>1)
{
do
{
do
{
belong=this->GetRand(size);//屬於哪個染色體
}while(this->IsUsed[belong]);//選擇當前代沒變異過的
id=this->GetRand(this->Chro[belong].size()/2);//變異那個片段
to=GetRand(4);
tmp.clear();
tmp=this->Chro[belong];
tmp[id*2]=Mat[to][0];
tmp[id*2+1]=Mat[to][1];
}while(tmp==this->Chro[belong]);//保證變異後與原來不同
this->Chro[belong]=tmp;
this->MutationNum-=1;
}
}
/******************************************************************************
*函式名My_Rand
*無引數
*產生一組[0,1]內的分佈均勻的隨機數
*******************************************************************************/
void grid::My_Rand()
{
srand((unsigned)clock());
int size=this->Chro.size();
for(int i=0;i<size;i++)
{
this->Random[i]=1.0*rand()/RAND_MAX;
}
}
/******************************************************************************
*函式名Get_Sec_num
*無引數
*利用賭輪選擇法選擇適應度大的生存下來
*******************************************************************************/
void grid::Get_Sec_num()
{
this->My_Rand();
vector<string>TmpChromosome;
int j=0;
int size=this->Chro.size();
while(j<size)
{
int i;
for(i=1;i<=size;i++)
{
if(this->Random[j]<=this->ProAccu[i]+eps)//防止浮點數誤差
{
TmpChromosome.push_back(this->Chro[i-1]);
j++;
break;
}
}
}
for(int i=0;i<size;i++)
this->Chro[i]=TmpChromosome[i];
TmpChromosome.clear();
}
/******************************************************************************
*函式名
*無引數
*遺傳操作-選擇主函式
*******************************************************************************/
void grid::Selection()
{
int size=this->Chro.size();
double sum=0;
this->OptimalFitness=0;
this->OptimalPath.clear();
for(int i=0;i<size;i++)
{
sum+=this->Fitness[i];
if(this->Fitness[i]>this->OptimalFitness||((fabs(this->Fitness[i]-this->OptimalFitness)<=eps)&&this->OptimalPath.size()>this->Chro[i].size()))
{
this->OptimalFitness=this->Fitness[i];
this->OptimalPath=this->Chro[i];
}
}
for(int i=1;i<=size;i++)
{
this->ProSec[i]=this->Fitness[i-1]/sum;
}
this->ProAccu[0]=0;
for(int i=1;i<=size;i++)
this->ProAccu[i]=this->ProAccu[i-1]+this->ProSec[i];
this->Get_Sec_num();
}
/******************************************************************************
*函式名GetCros
*4個引數,分別表示帶交叉的兩條染色體的下標,以及對應的交叉位置
*功能遺傳操作-交叉函式
*******************************************************************************/
void grid::GetCros(int One,int Two,int From,int To)
{
string tmp;
for(int i=From;i<=To;i++)
{
tmp.clear();
tmp+=this->Chro[One][i*2];
tmp+=this->Chro[One][i*2+1];
this->Chro[One][i*2]=this->Chro[Two][i*2];
this->Chro[One][i*2+1]=this->Chro[Two][i*2+1];
this->Chro[Two][i*2]=tmp[0];
this->Chro[Two][i*2+1]=tmp[1];
}
}
/******************************************************************************
*函式名Crossover
*無引數
*功能遺傳操作-交叉主函式,選擇相同長度的染色體進行交叉
*******************************************************************************/
void grid::Crossover()
{
sort(this->Chro.begin(),this->Chro.end(),ChromosomeCmp);
int id,id1,id2,size=this->Chro.size();
bool flag;
memset(this->IsUsed,0,sizeof(this->IsUsed));
while(this->CrossoverNum>1.0)
{
flag=false;
do
{
id=this->GetRand(size);
}while(this->IsUsed[id]);//選擇沒交叉過的染色體
int tsize=(this->Chro[id]).size();
///////選擇長度相同且沒交叉過的染色體
if(id!=0&&!(this->IsUsed[id-1])&&tsize==this->Chro[id-1].size())
{
flag=true;
do
{
id1=this->GetRand(tsize/2);
id2=this->GetRand(tsize/2);
}while(id1>id2);
this->GetCros(id,id-1,id1,id2);
}
if(!flag&&id!=size-1&&!(this->IsUsed[id+1])&&tsize==this->Chro[id+1].size())
{
flag=true;
do
{
id1=this->GetRand(tsize/2);
id2=this->GetRand(tsize/2);
}while(id1>id2);
this->GetCros(id,id+1,id1,id2);
}
if(flag)this->CrossoverNum-=1;
}
}
/************************************************
*函式名BFS
*無引數
*利用廣度優先預處理所有的點到目標點的最近距離
*************************************************/
bool grid::BFS1()
{
state Now,Next;
while(!Qu.empty())
Qu.pop();
Qu.push(Target);
Target.dis=0;
bool Visited[MAXROC][MAXROC];
memcpy(Visited,Mark,sizeof(Mark));
Visited[Target.x][Target.y]=true;
while(!Qu.empty())
{
Now=Qu.front();Qu.pop();
for(int i=0;i<4;i++)
{
int tx=Now.x+Move[i][0];
int ty=Now.y+Move[i][1];
int dis=Now.dis+1;
if(!(tx<0||tx>=this->Row||ty<0||ty>=this->Column||Visited[tx][ty]))
{
Next.x=tx;
Next.y=ty;
Next.dis=dis;
this->BetDis[tx][ty]=dis;
Visited[tx][ty]=true;
Qu.push(Next);
}
}
}
return false;
}
/************************************************
*函式名BFS
*無引數
*利用廣度優先尋找最短路徑
*************************************************/
bool grid::BFS2()
{
state Now,Next;
while(!Qu.empty())
Qu.pop();
Start.path.clear();
bool Visited[MAXROC][MAXROC];
memcpy(Visited,Mark,sizeof(Mark));
Visited[Target.x][Target.y]=false;
Visited[Start.x][Start.y]=true;
Qu.push(Start);
while(!Qu.empty())
{
Now=Qu.front();Qu.pop();
if(Now.x==Target.x&&Now.y==Target.y)
{
this->OptimalPath=Now.path;
this->OptimalFitness=1.0;
return true;
}
for(int i=0;i<4;i++)
{
int tx=Now.x+Move[i][0];
int ty=Now.y+Move[i][1];
if(!(tx<0||tx>=this->Row||ty<0||ty>=this->Column||Visited[tx][ty]))
{
Next.x=tx;
Next.y=ty;
Next.path=Now.path;
if(0==i)Next.path+="10";
else if(1==i)Next.path+="00";
else if(2==i)Next.path+="11";
else if(3==i)Next.path+="01";
Visited[tx][ty]=true;
Qu.push(Next);
}
}
}
return false;
}
/*******************************************************
*函式名DDALine
*無引數
*利用數值微分法由確定的兩個端點畫直線
********************************************************/
void grid::DDALine(int lx1,int ly1,int lx2,int ly2)
{
int dx,dy,epsl,k;
float x,y,xIncre,yIncre;
dx=lx2-lx1; dy=ly2-ly1;
x=lx1; y=ly1;
if(abs(dx)>abs(dy))
epsl=abs(dx);
else epsl=abs(dy);
xIncre=(float)dx/(float)epsl;
yIncre=(float)dy/(float)epsl;
glBegin(GL_POINTS);//
for(k=0;k<=epsl;k++)
{
glVertex2i(int(x+0.5),int(y+0.5));
x+=xIncre;
y+=yIncre;
}
glEnd();
}
/*******************************************************
*函式名InitGrid
*無引數
*顯示每局的障礙、起點、目標點
********************************************************/
void grid::InitGrid()
{
this->UsedPointNumber=0;
this->TotalPointNumber=this->Row*this->Column;
this->RowInc=winHeight/this->Row,this->ColumnInc=winWidth/this->Column;
memset(this->Mark,false,sizeof(this->Mark));
srand((unsigned)clock());
do
{
this->UsedPointNumber=rand()%(this->TotalPointNumber);
}while(this->UsedPointNumber>=this->TotalPointNumber/4||this->UsedPointNumber<=this->TotalPointNumber/8);
//儘可能合理的控制障礙的數目
int tmp=0;
while(tmp<this->UsedPointNumber)//產生障礙的座標並儘可能保證適當數目的障礙連成一串
{
int x=rand()%(this->Row),y=rand()%(this->Column),bel=rand()%2;
if(0==bel)//(x-1,y),(x,y),(x+1,y)
{
if(x-1>=0)
{
this->Point[tmp].y=y;this->Point[tmp].x=x-1;tmp++;
this->Mark[x-1][y]=true;
if(tmp>=this->UsedPointNumber)break;
}
if(x<this->Row)
{
this->Point[tmp].y=y;this->Point[tmp].x=x;tmp++;
this->Mark[x][y]=true;
if(tmp>=this->UsedPointNumber)break;
}
if(x+1<this->Row)
{
this->Point[tmp].y=y;this->Point[tmp].x=x+1;tmp++;
this->Mark[x+1][y]=true;
if(tmp>=this->UsedPointNumber)break;
}
}
else //(x,y-1),(x,y),(x,y+1)
{
if(y-1>=0)
{
this->Point[tmp].y=y-1;this->Point[tmp].x=x;tmp++;
this->Mark[x][y-1]=true;
if(tmp>=this->UsedPointNumber)break;
}
if(y<this->Column)
{
this->Point[tmp].y=y;this->Point[tmp].x=x;tmp++;
this->Mark[x][y]=true;
if(tmp>=this->UsedPointNumber)break;
}
if(y+1<this->Column)
{
this->Point[tmp].y=y+1;this->Point[tmp].x=x;tmp++;
this->Mark[x][y+1]=true;
if(tmp>=this->UsedPointNumber)break;
}
}
}
int sx,sy,tx,ty;
do
{
do
{
sx=rand()%(this->Row);
sy=rand()%(this->Column);
}while(this->Mark[sx][sy]);//控制起點無障礙物
do
{
tx=rand()%(this->Row);
ty=rand()%(this->Column);
}while(this->Mark[tx][ty]);//控制目標點無障礙物
}while(abs(sx-tx)+abs(sy-ty)<(this->Row+this->Column)/3);//控制起點和目標點保持適當遠的距離不小於方格任意兩點最大哈夫曼距離的三分之一
this->Mark[sx][sy]=true;
Start.x=sx;Start.y=sy;
//this->Mark[tx][ty]=true;
Target.x=tx;Target.y=ty;
this->Manu_Init();
this->DisplayGrid();
this->BFS1();
this->BFS2();
if(IsRun)First=false;
if(IsRun)First=false;
}
/*******************************************************
*函式名DisplayGrid
*無引數
*顯示視窗的當前狀態
********************************************************/
void grid::DisplayGrid()
{
int Left,Right,button,top;
Left=0,Right=winWidth,button=0,top=winHeight;
int lx1,ly1,lx2,ly2;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0f,0.0f,1.0f);
glPointSize(5);
//繪製放個水平線
for(int i=0;i<=winWidth;i+=this->ColumnInc)
{
DDALine(Left+i,button,Left+i,top);
}
//繪製放個豎直線線
for(int i=0;i<=winHeight;i+=this->RowInc)
{
DDALine(Left,button+i,Right,button+i);
}
//繪製障礙物
for(int i=0;i<this->UsedPointNumber;i++)
{
glColor3f(1.0f,0.0f,0.0f);
glRectf(Left+this->Point[i].y*this->ColumnInc,button+this->Point[i].x*this->RowInc,
Left+this->Point[i].y*this->ColumnInc+this->ColumnInc,button+this->Point[i].x*this->RowInc+this->RowInc);
}
int inc=3;
glColor3f(0.0f,1.0f,0.0f);
glRectf(Left+Start.y*this->ColumnInc+inc,button+Start.x*this->RowInc+inc,
Left+Start.y*this->ColumnInc+this->ColumnInc-inc,button+Start.x*this->RowInc+this->RowInc-inc);
//cout<<"S.x="<<Start.x<<" S.y="<<Start.y<<endl;///
glColor3f(0.0f,.0f,1.0f);
glRectf(Left+Target.y*this->ColumnInc+inc,button+Target.x*this->RowInc+inc,
Left+Target.y*this->ColumnInc+this->ColumnInc-inc,button+Target.x*this->RowInc+this->RowInc-inc);
//cout<<"T.x="<<Target.x<<" T.y="<<Target.y<<endl;///
if(!First)//不是第一次初始化視窗,則會出現相應染色體對應的路徑
{
for(int j=this->tcnt;j<this->UsedPointNumber;j++)
{
glColor3f(0.0f,1.0f,0.0f);
glRectf(Left+this->Point[j].y*this->ColumnInc,button+this->Point[j].x*this->RowInc,
Left+this->Point[j].y*this->ColumnInc+this->ColumnInc,button+this->Point[j].x*this->RowInc+this->RowInc);
}
}
glFlush();
}
/*******************************************************
*函式名init
*無引數
*初始化函式
********************************************************/
void init()
{
glClearColor(0.0f,0.0f,0.0f,1.0f);
glMatrixMode(GL_PROJECTION); //設定投影引數
gluOrtho2D(0.0f, 1000.0f,0.0f,600.0f);
MyLoop=false;
SetRow=false;SetColumn=false;
IsRun=false;
First=true;
}
/*******************************************************
*函式名Display()
*無引數
*視窗建立時顯示背景色
********************************************************/
void Display()
{
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
}
/*******************************************************
*函式名ChangeSize
*無引數
*視窗發生變化時完成初始化
********************************************************/
void ChangeSize(int w, int h)
{
winWidth=w;winHeight=h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,winWidth,0.0,winHeight);
}
/*******************************************************
*函式名timer
*無關引數
*定期重新整理視窗
********************************************************/
void Timer(int p)
{
Grid.now_Gen++;
if(Grid.now_Gen>=Grid.GenerationNum*Grid.PopulationSize)
{
Grid.OptimalFitness=Grid.GetFitness(Grid.OptimalPath);
Grid.DisplayGrid();
return ;
}
int i=Grid.now_Gen%Grid.PopulationSize;
Grid.tcnt=Grid.UsedPointNumber;
Grid.Fitness[i]=Grid.GetFitness(Grid.Chro[i]);
Grid.DisplayGrid();
Grid.UsedPointNumber=Grid.tcnt;
if(Grid.now_Gen%Grid.PopulationSize==0)
{
cout<<Grid.OptimalPath<<endl;
cout<<"當前已是第"<<Grid.now_Gen/Grid.PopulationSize<<"代種群!"<<endl;
Grid.Selection();
Grid.MutationNum=Grid.PopulationSize*Grid.MutationRate;//計算變異個數
Grid.CrossoverNum=Grid.PopulationSize*Grid.CrossoverRate/2;//計算交叉個數
Grid.Crossover();
Grid.Mutation();
if(Grid.PopulationSize>10)//精英主義(Elitist Strategy)選擇///////////
{
srand((unsigned)clock());
int Index;
for(int i=0;i<10;i++)
{
Index=rand()%Grid.PopulationSize;
Grid.Chro[Index]=Grid.OptimalPath;
}
}
}
if(MyLoop)
glutTimerFunc(200,Timer,0);
}
/*******************************************************
*函式名MousePlot
*4個引數:按鍵型別、動作、對應的座標
*滑鼠響應函式
********************************************************/
void MousePlot(int button,int action,int x,int y)
{
if(SetRow&&SetColumn)
{
if(button==GLUT_LEFT_BUTTON&&action==GLUT_DOWN)
{
if(!MyLoop&&!IsRun)
{
memset(Grid.BetDis,0,sizeof(Grid.BetDis));
Grid.InitGrid();
}
else MyLoop=false;
}
else if(button==GLUT_MIDDLE_BUTTON&&action==GLUT_DOWN)
{
MyLoop=true;
IsRun=true;
First=false;
Grid.now_Gen=0;
cout<<"當前已是第"<<Grid.now_Gen/Grid.PopulationSize<<"代種群!"<<endl;
Grid.MutationNum=0;//交叉個數為0
Grid.CrossoverNum=0;//變異個數為0
if(Grid.PopulationSize>10)//精英主義(Elitist Strategy)選擇///////////
{
srand((unsigned)clock());
int Index;
for(int i=0;i<10;i++)
{
Index=rand()%Grid.PopulationSize;
Grid.Chro[Index]=Grid.OptimalPath;
}
}
glutTimerFunc(200,Timer,0);
}
}
}
/*******************************************************
*函式名ProcessMenu
*一個引數,選單選項
*選單響應函式
********************************************************/
void ProcessMenu(int value)
{
if(IsRun)//在執行過程中重置方格
{
SetRow=false;
SetColumn=false;
IsRun=false;
First=true;
MyLoop=false;
}
if(value<=10)
{
Grid.SetRow(GridLen[0][value-1]);
SetRow=true;
}
else if(value<=20)
{
Grid.SetColumn(GridLen[1][value-11]);
SetColumn=true;
}
}
/*******************************************************
*函式名MyCreateMenu
*無選單
*初始化選單
********************************************************/
void MyCreateMenu()
{
int SetWidth=glutCreateMenu(ProcessMenu);
glutAddMenuEntry("4",1);
glutAddMenuEntry("6",2);
glutAddMenuEntry("8",3);
glutAddMenuEntry("10",4);
glutAddMenuEntry("12",5);
glutAddMenuEntry("14",6);
glutAddMenuEntry("16",7);
glutAddMenuEntry("18",8);
glutAddMenuEntry("20",9);
glutAddMenuEntry("22",10);
int SetHeight=glutCreateMenu(ProcessMenu);
glutAddMenuEntry("4",11);
glutAddMenuEntry("6",12);
glutAddMenuEntry("8",13);
glutAddMenuEntry("10",14);
glutAddMenuEntry("12",15);
glutAddMenuEntry("14",16);
glutAddMenuEntry("16",17);
glutAddMenuEntry("18",18);
glutAddMenuEntry("20",19);
glutAddMenuEntry("22",20);
int Choice=glutCreateMenu(ProcessMenu);
glutAddSubMenu("水平方向方格數",SetWidth);
glutAddSubMenu("垂直方向方格數",SetHeight);
glutCreateMenu(ProcessMenu);
glutAddSubMenu("設定",Choice);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
int main(int argc, char *argv[])
{
InitMap();
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);
glutInitWindowPosition(0, 0);
glutInitWindowSize(1000, 600);
glutCreateWindow("遺傳演算法在方格中的求最短路徑動畫演示");
glutDisplayFunc(Display);
glutReshapeFunc(ChangeSize);
glutMouseFunc(MousePlot);
MyCreateMenu();
init();
glutMainLoop();
return 0;
}
不用BFS算髮引導
#include<windows.h>
#include<gl/glut.h>
#include<iostream>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<iomanip>
#include<map>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXNGRID=1000;//設定方格的總數
const int MAXROC=50;//設定最大長、寬
const int MAXCHRO=250;//染色體條數
const double eps=1e-10;
int winHeight,winWidth;//當前視窗的長寬
bool MyLoop,SetRow,SetColumn,IsRun,First;//幾個開關變數控制滑鼠響應
int GridLen[2][10]=
{
{4,6,8,10,12,14,16,18,20,22},
{4,6,8,10,12,14,16,18,20,22}
};
int Move[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct state
{
int x,y;
int dis;
string path;
}Start,Target;
queue<state>Qu;
struct point
{
int x,y;
};//建立方格的座標(規則同OpenGL),便於投影到視區
map<int,string>Mat;
void InitMap()
{
Mat[0]="00";Mat[1]="01";Mat[2]="10";Mat[3]="11";
}
class grid
{
private :
int Row,Column; //方格的行列
int TotalPointNumber,UsedPointNumber; //總的點數、已用點數
int GenerationNum,PopulationSize,MaxDis,MinDis;//繁殖代數、種群規模、最大距離、最短距離
int BetDis[MAXROC][MAXROC];//BetDis[x][y]表示(x,y)到目標點的最短距離
int tcnt; //全域性臨時變數
string OptimalPath;//遺傳演算法求得的最短路徑
string BFSPath; //BFS演算法最短路徑
double OptimalFitness;//已找到的最短路徑
point Point[MAXNGRID]; //待列印的點
double RowInc,ColumnInc; //視窗的行列步長
bool Mark[MAXROC][MAXROC];//標記有沒有訪問
double MutationRate; //變異率
double CrossoverRate; //雜交率
double MutationNum; //當代變異個數
double CrossoverNum; //當代交叉個數
int now_Gen; //當前代數
double Fitness[MAXCHRO]; //適應度
double Random[MAXCHRO]; //產生的隨機數
double ProSec[MAXCHRO]; //選擇概率
double ProAccu[MAXCHRO]; //累積選擇概率
vector<string>Chro; //染色體
bool IsUsed[MAXCHRO];//標記當前繁殖代數下該染色體是否已經交叉或變異過
public:
int GetRow();
int GetColumn();
void SetRow(int);
void SetColumn(int);
bool BFS();
void DisplayGrid();
void InitGrid();
void DDALine(int,int,int,int);
void GetMinMaxDis();
bool IsBump(int,int);
bool GetNext(int &,int &,int);
int RunRoute(int &,int &,string &);
double GetFitness(string &);
void Manu_Init();
int GetRand(int);
void Mutation();
void My_Rand();
void Get_Sec_num();
void Selection();
void GetCros(int,int,int,int);
void Crossover();
friend void Timer(int);
friend void MousePlot(int,int,int,int);
}Grid;
/************************************************
*函式名GetRow
*無引數
*獲得方格行數
*************************************************/
int grid::GetRow()
{
return this->Row;
}
/************************************************
*函式名GetColumn
*無引數
*獲得方格列數
*************************************************/
int grid::