今天学习了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
- #include
- using namespace std;
-
- int a[25], cnt[25];
- char out[1000];
- int deep;
-
-
- int res;
-
-
- bool ok(int *a) {
- int tmp=a[7];
-
- if( tmp!=a[8] || tmp!=a[9] || tmp!=a[12] || tmp!=a[13]
- || tmp!=a[16] || tmp!=a[17] || tmp!=a[18] )
- return false;
- return true;
- }
-
- inline void change(int* cur,int a,int b,int c,int d,int e,int f,int g)
- {
- int tmp=cur[a];
- 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;
- }
-
-
- inline int count(int* a)
- {
- memset(cnt,0,sizeof(cnt));
- cnt[a[7]]++;cnt[a[8]]++;cnt[a[9]]++;
- cnt[a[12]]++;cnt[a[13]]++;cnt[a[16]]++;
- cnt[a[17]]++;cnt[a[18]]++;
- cnt[2]=cnt[1]>cnt[2]?cnt[1]:cnt[2];
- return max(cnt[2],cnt[3]);
- }
-
-
-
- bool dfs(int *a, int now, int pre) {
-
- if (deep - now < 8 - count(a))
- return false;
-
- if (now >= deep)
- return false;
- int temp[25];
- for (int i = 0; i < 8; ++i) {
-
- if( (pre==0 && i==5) || (pre==5 && i==0) ) continue;
- if( (pre==1 && i==4) || (pre==4 && i==1) ) continue;
- if( (pre==2 && i==7) || (pre==7 && i==2) ) continue;
- if( (pre==3 && i==6) || (pre==6 && i==3) ) continue;
- for (int j = 1; j <= 24; ++j) {
- temp[j] = a[j];
- }
- switch(i) {
- case 0:out[now]='A';change(temp,1,3,7,12,16,21,23);break;
- case 1:out[now]='B';change(temp,2,4,9,13,18,22,24);break;
- case 2:out[now]='C';change(temp,11,10,9,8,7,6,5);break;
- case 3:out[now]='D';change(temp,20,19,18,17,16,15,14);break;
- case 4:out[now]='E';change(temp,24,22,18,13,9,4,2);break;
- case 5:out[now]='F';change(temp,23,21,16,12,7,3,1);break;
- case 6:out[now]='G';change(temp,14,15,16,17,18,19,20);break;
- case 7:out[now]='H';change(temp,5,6,7,8,9,10,11);break;
- }
- if (ok(temp)) {
- res = temp[7];
- out[now+1] = '/0';
- return true;
- }
- if (dfs(temp, now + 1, i))
- return true;
-
- }
- return false;
- }
-
- int main() {
- while (scanf("%d", &a[1]) && a[1]) {
- for (int i = 2; i <= 24; ++i)
- scanf("%d", a+i);
- if (ok(a)) {
- printf("No moves needed/n");
- printf("%d/n", a[7]);
- }
- else {
- deep = 1;
-
- while (1) {
- if (dfs(a, 0, -1))
- break;
- ++deep;
-
- }
- printf("%s/n", out);
- printf("%d/n", res);
-
- }
- }
- return 0;
- }
有空找一下错误,发现错误的哥们可以给我留个言:(ps:错误解决了,在IDA*算法的递归调用if语句为true时,应该返回的是true,而不是false)。
-
-
-
-
-
- #include
- #include
- #include
- using namespace std;
-
-
- int map[25],countArray[25];
-
- int DEPTH,res;
-
- char outPath[100];
-
-
- bool isOk(const int* state){
-
- int tmp = state[7];
- if( tmp!= state[8] || tmp!=state[9] || tmp!= state[12]
- || tmp!= state[13] || tmp!=state[16] || tmp!= state[17]
- || tmp!= state[18] ){
-
- return false ;
- }
- return true ;
- }
-
-
- int countMaxSameNumber(const int* state){
-
- memset(countArray , 0 ,sizeof(countArray) ) ;
- countArray[ state[7] ]++; countArray[state[8]] ++; countArray[state[9]]++;
- countArray[ state[12] ]++; countArray[ state[13] ]++ ; countArray[state[16]]++;
- countArray[state[17]] ++ ; countArray[state[18]]++;
-
- countArray[2] = (countArray[2]>countArray[1]) ? countArray[2]: countArray[1];
- return max(countArray[2],countArray[3]);
- }
-
- void changeState(int *state,int a1,int a2,int a3,int a4,int a5,int a6,int a7){
-
- int tmp = state[a1];
- state[a1]=state[a2],state[a2]=state[a3],state[a3]=state[a4],
- state[a4]=state[a5],state[a5]=state[a6],state[a6]=state[a7],state[a7]=tmp;
-
- }
-
-
-
- bool dfsSearch( int* state ,int currDepth , int preDir) {
-
-
- if( DEPTH - currDepth < 8- countMaxSameNumber(state))
- return false ;
-
-
- if( DEPTH <= currDepth )
- return false ;
-
- int tmp[25];
- for(int i=1 ; i<=8 ; i++){
-
-
- if( (1 == i && 6 == preDir) || (6==i && 1== preDir) ) continue ;
- if( (2 == i && 5 == preDir) || (5==i && 2== preDir) ) continue ;
- if( (3 == i && 8 == preDir) || (8==i && 3== preDir) ) continue ;
- if( (4 == i && 7 == preDir) || (7==i && 4== preDir) ) continue ;
-
-
- for(int k=1 ; k<=24 ; k++)
- tmp[k]=state[k];
-
- switch(i){
-
- case 1 : outPath[currDepth] = 'A' ; changeState(tmp,1,3,7,12,16,21,23); break;
- case 2 : outPath[currDepth] = 'B' ; changeState(tmp,2,4,9,13,18,22,24); break;
- case 3 : outPath[currDepth] = 'C' ; changeState(tmp,11,10,9,8,7,6,5); break;
- case 4 : outPath[currDepth] = 'D' ; changeState(tmp,20,19,18,17,16,15,14); break;
- case 5 : outPath[currDepth] = 'E' ; changeState(tmp,24,22,18,13,9,4,2); break;
- case 6 : outPath[currDepth] = 'F' ; changeState(tmp,23,21,16,12,7,3,1); break;
- case 7 : outPath[currDepth] = 'G' ; changeState(tmp,14,15,16,17,18,19,20); break;
- case 8 : outPath[currDepth] = 'H' ; changeState(tmp,5,6,7,8,9,10,11); break;
- default : cout<<"ERROR!"<
- }
-
- if( isOk(tmp) ){
-
- res = tmp[7];
- outPath[currDepth +1 ]='/0';
- return true ;
- }
-
- if( dfsSearch(tmp , currDepth+1 , i))
- return true ;
- }
-
- return false ;
- }
-
-
- int main(){
-
- ifstream in("test.txt");
-
- while(1){
-
- in>>map[1];
- if( 0 == map[1])
- break;
-
- for(int i=2 ; i<=24 ; i++)
- in>>map[i];
-
- if( isOk(map)){
- cout<<"No moves needed"<
- cout<
- }
-
- else{
- DEPTH =1 ;
- while(1){
- if(dfsSearch(map , 0 , -1 ))
- break;
- DEPTH ++ ;
- }
-
- cout<
- cout<
- }
-
- }
- }
下面是一道自己做的poj2870的搜索题,在这道题中,我也使用了IDA*算法来剪枝,比起纯粹的dfs,效果还是很明显的。
-
-
-
-
- #include
- #include
- #include
- #define MAXSIZE 8
- #define MAX_BARRIAR 50
- #define NOT !
- #define __DEBUG 0
- using namespace std;
-
- char board[MAXSIZE][MAXSIZE];
- int wid,height,barriarNum;
-
- int depth,minLamp;
-
-
- typedef struct numedbarriar{
-
- int x;
- int y;
- int no;
- }NumedBarriar;
- NumedBarriar barriarArray[MAX_BARRIAR] ;
-
- static int dir[4][2]={ {0,-1},{-1,0},{0,1},{1,0} };
-
-
- void print(char c[][MAXSIZE]){
-
- for(int i=1 ; i<=height ; i++){
-
- for(int j=1 ; j<=wid ; j++){
-
- cout<" ";
- }
- cout<
- }
- cout<
- }
-
- inline bool isInBoard(int x,int y){
-
- return ( x<1 || x>height || y<1 || y>wid ) ? false : true ;
- }
-
-
- void change(char map[MAXSIZE][MAXSIZE],int x,int y){
-
- map[x][y]='L';
- int m,n ;
-
-
- for(int j=0 ; j<4 ; j++){
-
- for( m=x+dir[j][0] , n=y+dir[j][1]; isInBoard(m,n) ; m+=dir[j][0], n+=dir[j][1] ){
-
- if( '.'== map[m][n] || 'm' == map[m][n]){
-
- map[m][n]='m';
- }
- else{
-
- break;
- }
- }
- }
-
- }
-
-
- int gCost(char map[][MAXSIZE]){
-
- int num=0 ,x,y , dist =0 ;
-
- for(int i=0 ; i
-
- if( -1 == barriarArray[i].no )
- continue ;
-
- num = 0;
- for(int j=0; j<4 ;j++){
-
- x = barriarArray[i].x + dir[j][0];
- y = barriarArray[i].y + dir[j][1];
-
- if( isInBoard(x,y) && 'L' == map[x][y] ){
-
- num ++ ;
- }
- }
-
- dist += barriarArray[i].no - num ;
- }
-
- return dist ;
- }
-
- inline bool isCompleted(char map[][MAXSIZE]){
-
- for(int i=1 ; i<=height ;i++){
-
- for(int j=1; j<=wid ; j++){
-
- if( '.' == map[i][j])
- return false ;
- }
- }
- return (true && 0 == gCost(map));
- }
-
-
-
- bool isAllowed(char map[][MAXSIZE]){
-
- int x,y,num;
-
- for(int i=0 ; i
-
- if( -1 == barriarArray[i].no )
- continue ;
-
-
- num = 0;
- for(int j=0; j<4 ;j++){
-
- x = barriarArray[i].x + dir[j][0];
- y = barriarArray[i].y + dir[j][1];
-
- if( isInBoard(x,y) && 'L' == map[x][y] ){
-
- num ++ ;
- }
- }
-
- if( num > barriarArray[i].no)
- return false ;
- }
-
- return true ;
-
- }
-
- bool dfsSearch(char map[MAXSIZE][MAXSIZE],int currDepth) {
-
-
-
- if( currDepth + gCost(map) > depth )
- return false ;
-
- if( currDepth >=minLamp )
- return false ;
-
-
- if(NOT isAllowed(map)){
-
- return false ;
- }
-
-
- int i,j;
- char tmpMap[MAXSIZE][MAXSIZE];
-
- for(i=1 ; i<= height ; i++){
- for(j=1 ; j<=wid ; j++){
-
-
- if( '.' == map[i][j] ){
-
-
-
- for(int m=1; m<= height ; m++){
-
- for(int n=1 ; n<=wid ; n++){
-
- tmpMap[m][n]= map[m][n];
- }
- }
-
-
- change(tmpMap,i,j);
-
- if( isCompleted(tmpMap) ) {
-
- if( currDepth < minLamp )
- minLamp = currDepth+1;
-
- print(tmpMap);
- return true;
- }
-
- if(dfsSearch( tmpMap , currDepth+1)){
-
- return true ;
- }
-
-
-
-
-
-
-
-
-
-
-
- }
- }
- }
- return false ;
- }
-
- int main(){
-
-
- ifstream in("test.txt");
- bool findSolution;
-
- while(1){
-
- in>>height>>wid ;
- if( 0==wid && 0== height)
- break;
-
-
- for(int i=0 ; i< MAXSIZE ; i++){
-
- for(int j=0 ; j
-
- board[i][j] = '.';
- }
- }
- findSolution = true ;
- minLamp=100;
-
- in>>barriarNum;
- for(int i=0 ; i
-
- in>>barriarArray[i].x>>barriarArray[i].y>>barriarArray[i].no;
-
- if( -1 == barriarArray[i].no)
- board[ barriarArray[i].x ][barriarArray[i].y]='b';
-
- else{
-
- board[ barriarArray[i].x ][barriarArray[i].y]=barriarArray[i].no+'0';
- }
- }
-
- depth = 0;
-
-
- clock_t time=clock();
- while(1){
-
-
- if( NOT dfsSearch(board , 0))
- depth ++;
- else
- break;
-
- if(depth>100){
-
- cout<<"No solution"<
- findSolution = false ;
- break;
- }
-
- }
- cout <<"time: "<< clock()-time<<" MS" <
- cout<<"Depth: "<
- if(findSolution )
- cout<
-
- }
- }
在这里我总结一下IDA*搜索算法的一些注意点:
1、IDA*算法中,main函数一般使用逐步加大搜索深度来获得最优解,而如果使用dfs搜索策略的话,就需要对整个搜索空间进行搜索,在搜索函数中判断出所有合法解中的最优解(通过比较合法解的深度,选择深度最小的,其实在bfs搜索中,深度对应的就是题目中所要求的最优解量)
2、在IDA*的搜索核心算法中,前半部分就是剪枝,返回false的情况,这点和dfs是一样的。而在常规的搜索中,当找到最优解时,就返回true,而当后面的if语句中的dfs递归为true时,返回true,如此退出整个循环,否则会死循环。为了提高效率,可以把当前状态拷贝到临时状态,然后使用临时状态进行递归搜索,这样就不需要在递归函数返回时,还原原来的整个状态。
另外可以看到通过下面的代码中添加适当的语句就可以还原出整个最优解的路径:
- if( isCompleted(tmpMap) ) {
-
- if( currDepth < minLamp )
- minLamp = currDepth+1;
-
- print(tmpMap);
- return true;
- }
-
- if(dfsSearch( tmpMap , currDepth+1)){
-
- return true ;
- }