搜尋演算法:IDA*演算法

來源:互聯網
上載者:User

今天學習了IDA*演算法,在這裡總結一下:

 

IDA*演算法是A*演算法和迭代加深演算法的結合。

 

迭代加深演算法是在dfs搜尋演算法的基礎上逐步加深搜尋的深度,它避免了廣度優先搜尋佔用搜尋空間太大的缺點,也減少了深度優先搜尋的盲目性。它主要是在遞迴搜尋函數的開頭判斷當前搜尋的深度是否大於預定義的最大搜尋深度,如果大於,就退出這一層的搜尋,如果不大於,就繼續進行搜尋。這樣最終獲得的解必然是最優解。

 

而在A*演算法中,我們通過使用合理的估價函數,然後在獲得的子節點中選擇fCost最小的節點進行擴充,以此完成搜尋最優解的目的。但是A*演算法中,需要維護關閉列表和開放列表,需要對擴充出來的節點進行檢測,忽略已經進入到關閉列表中的節點(也就是所謂的“已經檢測過的節點”),另外也要檢測是否與待擴充的節點重複,如果重複進行相應的更新操作。所以A*演算法主要的代價花在了狀態檢測和選擇代價最小節點的排序上,這個過程中佔用的記憶體是比較大的,一般為了提高效率都是使用hash進行重複狀態檢測。

 

而IDA*演算法具有如下的特點:(

綜合了A*演算法的人工智慧性和回溯法對空間的消耗較少的優點,在一些規模很大的搜尋問題中會起意想不到的效果。它的具體名稱是 Iterative Deepening A*, 1985年由Korf提出。該演算法的最初目的是為了利用深度搜尋的優勢解決廣度A*的空間問題,其代價是會產生重複搜尋。歸納一下,IDA*的基本思路是:首先將初始狀態結點的H值設為閾值maxH,然後進行深度優先搜尋,搜尋過程中忽略所有H值大於maxH的結點;如果沒有找到解,則加大閾值maxH,再重複上述搜尋,直到找到一個解。在保證H值的計算滿足A*演算法的要求下,可以證明找到的這個解一定是最優解。在程式實現上,IDA* 要比 A* 方便,因為不需要儲存結點,不需要判重複,也不需要根據 H值對結點排序,佔用空間小。
而這裡在IDA*演算法中也使用合適的估價函數,來評估與目標狀態的距離。

在一般的問題中是這樣使用IDA*演算法的,當前局面的估價函數值+當前的搜尋深度 > 預定義的最大搜尋深度時,就進行剪枝。

這個估計函數的選取沒有統一的標準,找到合適的該函數並不容易,但是可以大致按照這個原則:在一定範圍內加大各個狀態啟發函數值的差別。

 

下面是poj2286 the rotation game的一道題目,使用了IDA*演算法:

 

 

 

 #include <iostream><br />#include <string><br />using namespace std;<br />//a:記錄數位數組<br />int a[25], cnt[25];<br />char out[1000];<br />int deep;</p><p>//最後的局面中心的結果數字<br />int res;</p><p>//判斷是否到達目標狀態<br />bool ok(int *a) {<br /> int tmp=a[7];</p><p> if( tmp!=a[8] || tmp!=a[9] || tmp!=a[12] || tmp!=a[13]<br /> || tmp!=a[16] || tmp!=a[17] || tmp!=a[18] )<br /> return false;<br /> return true;<br />}<br />//旋轉<br />inline void change(int* cur,int a,int b,int c,int d,int e,int f,int g)<br />{<br /> int tmp=cur[a];<br /> cur[a]=cur[b],cur[b]=cur[c],cur[c]=cur[d],cur[d]=cur[e],cur[e]=cur[f],cur[f]=cur[g],cur[g]=tmp;<br />}</p><p>//計算8個標準位置中數量最多的數字有多少個<br />inline int count(int* a)<br />{<br /> memset(cnt,0,sizeof(cnt));<br /> cnt[a[7]]++;cnt[a[8]]++;cnt[a[9]]++;<br /> cnt[a[12]]++;cnt[a[13]]++;cnt[a[16]]++;<br /> cnt[a[17]]++;cnt[a[18]]++;<br /> cnt[2]=cnt[1]>cnt[2]?cnt[1]:cnt[2];<br /> return max(cnt[2],cnt[3]);<br />}</p><p>//pre:上一次遞迴移動的方向<br />//now:當前遞迴的步數<br />bool dfs(int *a, int now, int pre) {<br />//剪枝:如果當前可以遞迴的深度已經小於局面中心數字所需量<br /> if (deep - now < 8 - count(a))<br /> return false;<br /> //超過當前的搜尋深度<br /> if (now >= deep)<br /> return false;<br /> int temp[25];//備份當前的局面<br /> for (int i = 0; i < 8; ++i) {<br /> //如果前後兩次拉動的方向恰好相反,那麼就剪枝<br /> if( (pre==0 && i==5) || (pre==5 && i==0) ) continue;<br /> if( (pre==1 && i==4) || (pre==4 && i==1) ) continue;<br /> if( (pre==2 && i==7) || (pre==7 && i==2) ) continue;<br /> if( (pre==3 && i==6) || (pre==6 && i==3) ) continue;<br /> for (int j = 1; j <= 24; ++j) {<br /> temp[j] = a[j];<br /> }<br /> switch(i) {<br /> case 0:out[now]='A';change(temp,1,3,7,12,16,21,23);break;<br /> case 1:out[now]='B';change(temp,2,4,9,13,18,22,24);break;<br /> case 2:out[now]='C';change(temp,11,10,9,8,7,6,5);break;<br /> case 3:out[now]='D';change(temp,20,19,18,17,16,15,14);break;<br /> case 4:out[now]='E';change(temp,24,22,18,13,9,4,2);break;<br /> case 5:out[now]='F';change(temp,23,21,16,12,7,3,1);break;<br /> case 6:out[now]='G';change(temp,14,15,16,17,18,19,20);break;<br /> case 7:out[now]='H';change(temp,5,6,7,8,9,10,11);break;<br /> }<br /> if (ok(temp)) {<br /> res = temp[7];<br /> out[now+1] = '/0';<br /> return true;<br /> }<br /> if (dfs(temp, now + 1, i))<br /> return true;</p><p> }<br /> return false;<br />}</p><p>int main() {<br /> while (scanf("%d", &a[1]) && a[1]) {<br /> for (int i = 2; i <= 24; ++i)<br /> scanf("%d", a+i);<br /> if (ok(a)) {//如果已經符合要求了<br /> printf("No moves needed/n");<br /> printf("%d/n", a[7]);<br /> }<br /> else {<br /> deep = 1;<br /> //clock_t time= clock();<br /> while (1) {<br /> if (dfs(a, 0, -1))<br /> break;<br /> ++deep;</p><p> }<br /> printf("%s/n", out);<br /> printf("%d/n", res);</p><p> }<br /> }<br /> return 0;<br />}<br />

 

有空找一下錯誤,發現錯誤的哥們可以給我留個言:(ps:錯誤解決了,在IDA*演算法的遞迴調用if語句為true時,應該返回的是true,而不是false)。

 

/*<br /> *POJ2286 The Rotation Game<br /> *解題思路:使用迭代加深的深度搜尋演算法,這裡非常要注意還是剪枝的問題<br /> *只有較好的針對題目環境的剪枝,才能提高搜尋效率<br />*/<br />#include<iostream><br />#include<fstream><br />#include<string><br />using namespace std;</p><p>//使用數組表示遊戲局面<br />int map[25],countArray[25];<br />//搜尋深度 , 最終的局面中心數字<br />int DEPTH,res;</p><p>char outPath[100];</p><p>//判斷是否到達了目標局面<br />bool isOk(const int* state){</p><p>int tmp = state[7];<br />if( tmp!= state[8] || tmp!=state[9] || tmp!= state[12]<br />|| tmp!= state[13] || tmp!=state[16] || tmp!= state[17]<br />|| tmp!= state[18] ){</p><p>return false ;<br />}<br />return true ;<br />}</p><p>//統計局面的中心地區含有相同數位最大數量<br />int countMaxSameNumber(const int* state){</p><p>memset(countArray , 0 ,sizeof(countArray) ) ;<br />countArray[ state[7] ]++; countArray[state[8]] ++; countArray[state[9]]++;<br />countArray[ state[12] ]++; countArray[ state[13] ]++ ; countArray[state[16]]++;<br />countArray[state[17]] ++ ; countArray[state[18]]++;</p><p>countArray[2] = (countArray[2]>countArray[1]) ? countArray[2]: countArray[1];<br />return max(countArray[2],countArray[3]);<br />}</p><p>void changeState(int *state,int a1,int a2,int a3,int a4,int a5,int a6,int a7){</p><p>int tmp = state[a1];<br />state[a1]=state[a2],state[a2]=state[a3],state[a3]=state[a4],<br />state[a4]=state[a5],state[a5]=state[a6],state[a6]=state[a7],state[a7]=tmp;</p><p>}</p><p>//迭代加深搜尋<br />//state:當前局面 currDepth :當前所處的搜尋深度 preDir:當前搜尋選擇的旋轉的方向<br />bool dfsSearch( int* state ,int currDepth , int preDir) {</p><p>//剪枝 1 : 本質上使用的就是IDA*估價函數進行剪枝<br />if( DEPTH - currDepth < 8- countMaxSameNumber(state))<br />return false ;</p><p>//超過了當前的搜尋深度<br />if( DEPTH <= currDepth )<br />return false ;</p><p>int tmp[25];<br />for(int i=1 ; i<=8 ; i++){</p><p>//剪枝2 :前後連續的相反方向的兩次旋轉是沒有意義的<br />if( (1 == i && 6 == preDir) || (6==i && 1== preDir) )continue ;<br />if( (2 == i && 5 == preDir) || (5==i && 2== preDir) )continue ;<br />if( (3 == i && 8 == preDir) || (8==i && 3== preDir) )continue ;<br />if( (4 == i && 7 == preDir) || (7==i && 4== preDir) )continue ;</p><p>//memcpy( tmp , state , sizeof(state)) ;<br />for(int k=1 ; k<=24 ; k++)<br />tmp[k]=state[k];</p><p>switch(i){<br />//記錄搜尋路徑<br />case 1 : outPath[currDepth] = 'A' ; changeState(tmp,1,3,7,12,16,21,23); break;<br />case 2 : outPath[currDepth] = 'B' ; changeState(tmp,2,4,9,13,18,22,24); break;<br />case 3 : outPath[currDepth] = 'C' ; changeState(tmp,11,10,9,8,7,6,5); break;<br />case 4 : outPath[currDepth] = 'D' ; changeState(tmp,20,19,18,17,16,15,14); break;<br />case 5 : outPath[currDepth] = 'E' ; changeState(tmp,24,22,18,13,9,4,2); break;<br />case 6 : outPath[currDepth] = 'F' ; changeState(tmp,23,21,16,12,7,3,1); break;<br />case 7 : outPath[currDepth] = 'G' ; changeState(tmp,14,15,16,17,18,19,20); break;<br />case 8 : outPath[currDepth] = 'H' ; changeState(tmp,5,6,7,8,9,10,11); break;<br />default : cout<<"ERROR!"<<endl;<br />}</p><p>if( isOk(tmp) ){</p><p>res = tmp[7];<br /> outPath[currDepth +1 ]='/0';<br />return true ;<br />}</p><p>if( dfsSearch(tmp , currDepth+1 , i))<br />return true ;<br />}</p><p>return false ;<br />}</p><p>int main(){</p><p>ifstream in("test.txt");</p><p>while(1){</p><p>in>>map[1];<br />if( 0 == map[1])<br />break;</p><p>for(int i=2 ; i<=24 ; i++)<br />in>>map[i];</p><p>if( isOk(map)){<br />cout<<"No moves needed"<<endl;<br />cout<<map[7]<<endl;<br />}</p><p>else{<br />DEPTH =1 ;<br />while(1){<br />if(dfsSearch(map , 0 , -1 ))<br />break;<br />DEPTH ++ ;<br />}</p><p>cout<<outPath<<endl;<br />cout<<res<<endl;<br />}</p><p>}<br />}<br />

 

下面是一道自己做的poj2870的搜尋題,在這道題中,我也使用了IDA*演算法來剪枝,比起純粹的dfs,效果還是很明顯的。

/*<br /> *POJ2870 Light Up<br />*/<br />//#include<cstdio><br />#include<iostream><br />#include<fstream><br />#include<ctime><br />#define MAXSIZE 8<br />#define MAX_BARRIAR 50<br />#define NOT !<br />#define __DEBUG 0<br />using namespace std;</p><p>char board[MAXSIZE][MAXSIZE];<br />int wid,height,barriarNum;<br />//表示搜尋的深度<br />int depth,minLamp;</p><p>//記錄有編號的障礙的位置的結構體<br />typedef struct numedbarriar{</p><p>int x;<br />int y;<br />int no;<br />}NumedBarriar;<br />NumedBarriar barriarArray[MAX_BARRIAR] ;</p><p>static int dir[4][2]={ {0,-1},{-1,0},{0,1},{1,0} };</p><p>//列印當前局面<br />void print(char c[][MAXSIZE]){</p><p>for(int i=1 ; i<=height ; i++){</p><p>for(int j=1 ; j<=wid ; j++){</p><p>cout<<c[i][j]<<" ";<br />}<br />cout<<endl;<br />}<br />cout<<endl<<endl;<br />}</p><p>inline bool isInBoard(int x,int y){</p><p>return ( x<1 || x>height || y<1 || y>wid ) ? false : true ;<br />}</p><p>//標記被照亮的地方<br />void change(char map[MAXSIZE][MAXSIZE],int x,int y){</p><p>map[x][y]='L';<br />int m,n ;</p><p>//往左<br />for(int j=0 ; j<4 ; j++){</p><p>for( m=x+dir[j][0] , n=y+dir[j][1]; isInBoard(m,n) ; m+=dir[j][0], n+=dir[j][1] ){</p><p>if( '.'== map[m][n] || 'm' == map[m][n]){</p><p>map[m][n]='m';<br />}<br />else{</p><p>break;<br />}<br />}<br />}</p><p>}</p><p>//計算有編號的障礙周圍還缺少的Lamp數目<br />int gCost(char map[][MAXSIZE]){</p><p>int num=0 ,x,y , dist =0 ;</p><p>for(int i=0 ; i<barriarNum ;i++) {</p><p>if( -1 == barriarArray[i].no )<br />continue ;</p><p>num = 0;<br />for(int j=0; j<4 ;j++){</p><p>x = barriarArray[i].x + dir[j][0];<br />y = barriarArray[i].y + dir[j][1];</p><p>if( isInBoard(x,y) && 'L' == map[x][y] ){</p><p>num ++ ;<br />}<br />}</p><p>dist += barriarArray[i].no - num ;<br />}</p><p>return dist ;<br />}</p><p>inline bool isCompleted(char map[][MAXSIZE]){</p><p>for(int i=1 ; i<=height ;i++){</p><p>for(int j=1; j<=wid ; j++){</p><p>if( '.' == map[i][j])<br />return false ;<br />}<br />}<br />return (true && 0 == gCost(map));<br />}</p><p>//檢查是否超出帶編號barriar規定的周圍最大的lamp數量<br />bool isAllowed(char map[][MAXSIZE]){</p><p>int x,y,num;</p><p>for(int i=0 ; i<barriarNum ;i++) {</p><p>if( -1 == barriarArray[i].no )<br />continue ;</p><p>//每次檢查前初始化<br />num = 0;<br />for(int j=0; j<4 ;j++){</p><p>x = barriarArray[i].x + dir[j][0];<br />y = barriarArray[i].y + dir[j][1];</p><p>if( isInBoard(x,y) && 'L' == map[x][y] ){</p><p>num ++ ;<br />}<br />}</p><p>if( num > barriarArray[i].no)<br />return false ;<br />}</p><p>return true ;</p><p>}</p><p>bool dfsSearch(char map[MAXSIZE][MAXSIZE],int currDepth) {</p><p>//IDA*剪枝<br />if( currDepth + gCost(map) > depth )<br />return false ;</p><p>if( currDepth >=minLamp )<br />return false ;</p><p>//剪枝1<br />if(NOT isAllowed(map)){</p><p>return false ;<br />}</p><p>int i,j;<br />char tmpMap[MAXSIZE][MAXSIZE];</p><p>for(i=1 ; i<= height ; i++){<br />for(j=1 ; j<=wid ; j++){</p><p>//找到可放置的位置<br />if( '.' == map[i][j] ){</p><p>//memcpy(tmpMap , map , sizeof(map) );<br />for(int m=1; m<= height ; m++){</p><p>for(int n=1 ; n<=wid ; n++){</p><p>tmpMap[m][n]= map[m][n];<br />}<br />}</p><p>//改變當前的局面,標記當前lamp照亮的位置<br />change(tmpMap,i,j);</p><p>if( isCompleted(tmpMap) ) {</p><p>if( currDepth < minLamp )<br />minLamp = currDepth+1;<br />//cout<<"find"<<minLamp<<endl;<br />print(tmpMap);<br />return true;<br />}</p><p>if(dfsSearch( tmpMap , currDepth+1)){<br />//print(tmpMap);<br />return true ;<br />}</p><p>//memcpy(map,tmpMap,sizeof(tmpMap));<br />//for(int m=1; m<= height ; m++){<br />//<br />//for(int n=1 ; n<=wid ; n++){<br />//<br />//map[m][n]= tmpMap[m][n];<br />//}<br />//}</p><p>}<br />}<br />}<br />return false ;<br />}</p><p>int main(){</p><p>ifstream in("test.txt");<br />bool findSolution;</p><p>while(1){</p><p>in>>height>>wid ;<br />if( 0==wid && 0== height)<br />break;</p><p>//初始化局面<br />for(int i=0 ; i< MAXSIZE ; i++){</p><p>for(int j=0 ; j<MAXSIZE ; j++){</p><p>board[i][j] = '.';<br />}<br />}<br />findSolution = true ;<br />minLamp=100;</p><p>in>>barriarNum;<br />for(int i=0 ; i<barriarNum ; i++){</p><p>in>>barriarArray[i].x>>barriarArray[i].y>>barriarArray[i].no;<br />//普通的barriar<br />if( -1 == barriarArray[i].no)<br />board[ barriarArray[i].x ][barriarArray[i].y]='b';<br />//有編號的barriar<br />else{</p><p>board[ barriarArray[i].x ][barriarArray[i].y]=barriarArray[i].no+'0';<br />}<br />}</p><p>depth = 0;</p><p>clock_t time=clock();<br />while(1){</p><p>if( NOT dfsSearch(board , 0))<br />depth ++;<br />else<br />break;</p><p>if(depth>100){</p><p>cout<<"No solution"<<endl;<br />findSolution = false ;<br />break;<br />}</p><p>}<br />cout <<"time: "<< clock()-time<<" MS" <<endl;<br />cout<<"Depth: "<<depth<<endl;<br />if(findSolution )<br />cout<<minLamp<<endl;</p><p>}<br />}<br />

 

在這裡我總結一下IDA*搜尋演算法的一些注意點:

 

1、IDA*演算法中,main函數一般使用逐步加大搜尋深度來獲得最優解,而如果使用dfs搜尋策略的話,就需要對整個搜尋空間進行搜尋,在搜尋函數中判斷出所有合法解中的最優解(通過比較合法解的深度,選擇深度最小的,其實在bfs搜尋中,深度對應的就是題目中所要求的最優解量)

2、在IDA*的搜尋核心演算法中,前半部分就是剪枝,返回false的情況,這點和dfs是一樣的。而在常規的搜尋中,當找到最優解時,就返回true,而當後面的if語句中的dfs遞迴為true時,返回true,如此退出整個迴圈,否則會死迴圈。為了提高效率,可以把目前狀態拷貝到臨時狀態,然後使用臨時狀態進行遞迴搜尋,這樣就不需要在遞迴函式返回時,還原原來的整個狀態。

 

另外可以看到通過下面的代碼中添加適當的語句就可以還原出整個最優解的路徑:

if( isCompleted(tmpMap) ) {</p><p>if( currDepth < minLamp )<br />minLamp = currDepth+1;<br />//cout<<"find"<<minLamp<<endl;<br />print(tmpMap);<br />return true;<br />}</p><p>if(dfsSearch( tmpMap , currDepth+1)){<br />//print(tmpMap);<br />return true ;<br />}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.