A*算法 和 IDA*算法

A*算法概述:
     采用广度优先搜索策略,在搜索过程中使用启发函数,即有大致方向的向前进虽然目标有时候不是很明确。
A*算法核心:
     A*算法的关键在于启发函数,启发函数的优劣直接影响A*算法的效率。
          f(n)=g(n)+h(n); 
     这个式子中:f(n)表示从初始状态到目标状态的估测代价。
                         g(n)表示从初始状态到当前状态的代价(已经确定)。
                         h(n)表示从当前状态到目标状态的估测代价(预测)。
                 其中:h(n)的好坏直接影响评估函数的好坏。
                 一个好的f(n)总能明确指引算法前进的方向,可以迅速的到达目标状态。
            f*(n)=g*(n)+h*(n); 我们假设的从初始状态到目标状态的实际最小代价。
      这个式子中:f(n)表示从初始状态到目标状态的实际代价。
                         g*(n)表示从初始状态到当前状态的代价(已经确定)g*(n)和g(n)是相等的。
                         h*(n)表示从当前状态到目标状态的实际代价。
                若h(n)<=h*(n),则总能找到最优解。(当h(n)<h*(n)的时候,不可能找到一条从初始状态到达目标状态的路径。在搜索过程中使得h(n)逐渐接近h*(n),最终找到最优路径。
          
优点:
      与广度优先搜索策略和深度优先搜索策略相比,A*算法不是盲目搜索,而是有提示的搜索。
缺点:
     该算法一般要使用大量的空间用于存储已搜索过的中间状态,防止重复搜索。
用途:
     从初始状态出发  =>经过一系列中间状态 =>最终到达目标状态(或者无法到达)。
     该算法用于经过中间状态时候的行进策略(其中的中间状态或者由题目给出,或者在前边已经推导得出)。


IDA*算法:
     迭代加深搜索算法,在搜索过程中采用估值函数,以减少不必要的搜索。
IDA*算法核心:
     设置每次可达的最大深度depth,若没有到达目标状态则加深最大深度。
     采用估值函数,剪掉f(n)大于depth的路径。
优点:
     使用回溯方法,不用保存中间状态,大大节省了空间。
缺点:
     重复搜索:回溯过程中每次depth变大都要再次从头搜索。
用途:
     和A*算法大致相同。
     


实际上,估测函数f(n)也是剪枝的一种。



acm应用A*和IDA*算法的实例:

 
 
 

hdu 1043 ( Eight )  采用IDA*算法
      这里我在回溯过程中计算最大深度的时候不是每次加一而是有策略的进行增大(详见代码),如果是每次+1则687可见这个改进还是挺有效果的。
      在编程时候官方给的数据都通过了,但是总是不能ac,发愁了半天。灵机一动,我自己编写了一个自动生成测试数据代码,找到bug居然是sweap函数,就是两个数据进行交换,一定要先自我判重,即是不是自己和自己交换。


#include <iostream>
#include <time.h> 
using namespace std;

struct ED_Node{
    int arr[9];
    int blankPos;
}ed_node;

ED_Node tag_node = {1,2,3,4,5,6,7,8,0,8};
bool isMove[][4]= {{0,1,0,1},{0,1,1,1}, {0,1,1,0},
                    {1,1,0,1},{1,1,1,1}, {1,1,1,0}, 
                    {1,0,0,1}, {1,0,1,1},{1,0,1,0}};

int move_distance[] = {-3,3,-1,1};
int ED_weight[]={0,1,2,6,24,120,720,5040,40320}; //康托展开式中各项权重
int str[400000]; //记录路径(存储direction的下标)
char direction[] ="udlr";
int direc[]={-1,1,2,-2};//防止向回走

int currStep;//记录已走步数,其对应的方向存放在str中
int Max_deepth; //最大深度,不断递增
int Min_Distance;


int get_h(const ED_Node& node)
{
    int current_x,current_y;
    int tag_x,tag_y;
    int sum_distance = 0;
    for ( int i=0; i<9; ++i ){
        if ( node.arr[i] != 0 ){
            tag_x = (node.arr[i]-1)/3;
            tag_y = (node.arr[i]-1)%3;
            current_x = i/3;
            current_y = i%3;
            sum_distance += abs(tag_x-current_x)+abs(tag_y-current_y);
        }
    }
    return sum_distance;
}

bool isSloved (const ED_Node& src,const ED_Node& tag)
{
    int countSrcNX = 0;
    int countTagNX = 0;
    for ( int i=0; i<9; ++ i ){
        for ( int j=0; j<i; ++j ){
            if ( src .arr[i]>0 ){
                countSrcNX = src .arr[j]>src.arr[i] ?
                    ( countSrcNX+1) :countSrcNX ;
            } 
            if ( tag .arr[i]>0 ){
                countTagNX = tag.arr[j]>tag.arr[i] ?
                   ( countTagNX+1) :countTagNX ;
            } 
        } 
    } 
    return (countSrcNX &1) == (countTagNX&1);
}

void sweap(int& a,int &b)
{
    if ( a!=b ){ //切记自我检测
        a = a+b;
        b = a-b;
        a = a-b;
    }
}

bool DFS()
{
    int temp_distance = get_h(ed_node);
    if (  temp_distance == 0 ){
        return true;
    }
    int estimateDistance = temp_distance+currStep;
    //cout<<"estimateDistance::"<<estimateDistance<<endl;
     Min_Distance = Max_deepth>estimateDistance ? Min_Distance :
                (Min_Distance<estimateDistance ? estimateDistance:Min_Distance);
    if ( estimateDistance>Max_deepth ){
        return false;
    }
    for ( int i=0; i<4; ++i ){
        
        if ( !isMove[ed_node.blankPos][i] || !(direc[str[currStep]] +direc[i]) ){
            continue;
        }       
        str[++currStep] = i;
        sweap(ed_node.arr[ed_node.blankPos],ed_node.arr[ed_node.blankPos+move_distance[i]]);
        ed_node.blankPos = ed_node.blankPos+move_distance[i];
        if ( DFS() ){
            return true;
        }
        --currStep;
        ed_node.blankPos = ed_node.blankPos-move_distance[i];
        sweap(ed_node.arr[ed_node.blankPos],ed_node.arr[ed_node.blankPos+move_distance[i]]);
    }
    return false;
}

void IDAstar()
{
    if ( !isSloved(ed_node,tag_node) ){
        cout<<"unsolvable"<<endl;
        return ;
    }
    currStep = 0;
    Max_deepth = get_h(ed_node);
    Min_Distance = Max_deepth;
    while ( !DFS() ){
        //Max_deepth++;
        Max_deepth = Min_Distance>Max_deepth ? Min_Distance : (Max_deepth+1);
    }
    for (int i=1; i<=currStep; ++i) {
        cout<<direction[str[i]];
    }
    cout<<endl;
}

int main()
{
    char str [100];

     //自动生成数据测试
    //srand((unsigned)time(NULL));
    //int a[9];
    //int pos ;
    //while ( true ){

    //for ( int i=0; i<9 ; ++i ){
    //a[i]=i;
    //}
    //for ( int i=9; i>1 ; --i ){
    //pos = rand()%i;
    cout<<pos<<" "<<i-1<<endl;
    //sweap(a[pos],a[i-1]);
    //}
    //for ( int i=0; i<9 ; ++i ){
    //cout<<a[i];
    //}
    //cout<<endl;

    //for ( int i=0; i<9 ; ++i ){
    //    ed_node.arr [i] = a[i];
    //    if ( a[i] == 0 ){
    //        ed_node.blankPos = i;
    //    }
    //}

    while ( gets (str)!=NULL ){
        int k =0;
        for ( int i=0; i<9; ++ i,k ++ ){ 
            if ( str [k] == 'x' ){
                ed_node.arr [i] = 0;
                ed_node.blankPos = i;
            } else if ( str[k]>= '1' &&  str [k]<= '9'){
                ed_node.arr [i] = str[k]-'0';
            } else {
                -- i;
            }
        }
        IDAstar();
    }            


}



 
使用A*算法:可见在空间上和时间上都有明显优势(其实优势最明显的在空间,我的A*算法比较垃圾,效率不高所以时间比较长)。

#include <iostream>
#include <queue>
 
using namespace std;

static const int MAX_ED_SIZE      = 9;
static const int MAX_ED_HASH_SIZE = 400000;

struct ED_Hash {  //ED:eight diagital
                 int fatherHashPos ;
                 char direction ;  //父节点到当前节点的移动方向
} ed_hash[ MAX_ED_HASH_SIZE ];

struct ED_Node {
    int arr [9];
    int hashPos ;
    int blankPos ;
    int h , g;  //g:already cost h:estimate cost
    bool operator < (const ED_Node& node )const {
        //return h !=node.h ? h>node.h : g>node.g;
        return (h+g)==(node.h+node.g)? g<node.g :(h+g)>(node.h+node.g);
    }
                
}ed_keep, ed_temp;

int ED_weight [ MAX_ED_SIZE]={0,1,2,6,24,120,720,5040,40320}; //康托展开式中各项权重

char direction [4]={ 'l', 'r', 'u', 'd'};

int way [4][2]={{0,1},{0,-1},{1,0},{-1,0}};


bool isSloved ( const ED_Node& src ,const ED_Node& tag )
{
    int countSrcNX = 0;
    int countTagNX = 0;
    for ( int i=0; i< MAX_ED_SIZE; ++ i ){
        for ( int j=0; j< i; ++ j ){
            if ( src . arr[ i]>0 ){
                countSrcNX = src . arr[ j]> src. arr [ i] ?
                    ( countSrcNX +1) :countSrcNX ;
            } // end if
            if ( tag . arr[ i]>0 ){
                countTagNX = tag . arr[ j]> tag. arr [ i] ?
                    ( countTagNX +1) :countTagNX ;
            } //end if
        } //end for
    } //end for
    return (countSrcNX &1) == (countTagNX&1);
                
}

int getHashValue ( const ED_Node& node ) //利用康托展开式,计算hash值
{
    int HashValue =0;
    for ( int i=1; i< 9; ++ i ){
        int countNX = 0;
        for ( int j=0; j< i; ++ j ){
            countNX = node . arr[ j]> node. arr [ i] ?
                ( countNX +1) :countNX ;
        }
        HashValue += countNX * ED_weight[ i ];
    }
    return HashValue ;
                
}

int get_H ( const ED_Node& node ) // 获取曼哈顿距离,即估测代价
{
    int currentRow , currentCol;
    int tagRow , tagCol, temp;
    int sumCost = 0;
    for ( int i =0; i< 9; ++ i ){
        //if ( node .arr[i] ){
            temp = (node .arr[i]-1)%9;
            currentRow = i /3;
            currentCol = i %3;
            tagRow      = temp /3;
            tagCol      = temp %3;
            sumCost += abs ( tagRow - currentRow ) + abs ( tagCol - currentCol );
        //}    
    }
    return sumCost ;
                 
}


inline void swap( int& a ,int & b)
{
                 a ^=b ;
                 b ^=a ;
                 a ^=b ;
}


inline bool isOk (const int& x,const int& y)
{
    return x >=0 && y>=0 && x<3 && y <3;
}

void ED_Astar ( ED_Node node ,const ED_Node& tag )
{
    const int tagHashPos = getHashValue (tag );
    int fatherHashPos ,blankPos , numberPos;
    priority_queue <ED_Node > que;
    que .push ( node);
    while ( !que . empty() ){
        ed_keep = que .top();
        que .pop ();
        fatherHashPos = ed_keep .hashPos;
        blankPos = ed_keep .blankPos;
        for ( int i=0; i<4; ++ i ){
            int x = blankPos/3;
            int y = blankPos%3;
            x -= way [i][0];
            y -= way [i][1];

            if ( isOk (x,y) ){
                numberPos = x *3 +y;
                ed_temp = ed_keep ;
                swap(ed_temp .arr[blankPos], ed_temp.arr [numberPos]);
                ed_temp.hashPos = getHashValue( ed_temp);
                if ( ed_hash [ed_temp. hashPos].direction == 0 ){

                    ed_hash[ed_temp .hashPos]. direction     = direction [i] ;
                    ed_hash[ed_temp .hashPos]. fatherHashPos = fatherHashPos ;

                    ed_temp.blankPos = numberPos;
                    ed_temp.g ++;
                    ed_temp.h = get_H(ed_temp);
                    que.push (ed_temp);
                }
                if ( ed_temp .hashPos == tagHashPos ){
                    return  ;
                }

            }
        }

    }                                               
                               

}

void reserveStr (char* str,const int nSize)
{
    for ( int i=0; i<nSize/2; ++ i ){
        char temp = str[i];
        str[i ] = str[nSize-1- i];
        str[nSize -1-i] = temp;
    }
                
               
}

void getWay ( const ED_Node& node )
{
    int hashPos = getHashValue( node );
    char strRout [10000];
    int i =0;
    for ( ; i <10000 && hashPos!=-1; ++ i ){
         strRout [i ] = ed_hash[ hashPos ].direction ;
         hashPos = ed_hash [ hashPos]. fatherHashPos ;
         //cout<<strRout [i];
    }
    //strRout [--i ] = '\0';
    /*reserveStr(strRout ,i);
    cout <<strRout << endl;*/
    for( i -=2;i>=0; i--) 
        putchar(strRout [i]);
    puts("");                                
            
}

void main ()
{
    ED_Node src , tag;

    //初始化tag
    for ( int i=0; i< MAX_ED_SIZE-1; ++ i ){
        tag.arr [ i] = i+1;
    }
    tag.arr [ MAX_ED_SIZE-1] = 0;
    tag.hashPos = getHashValue ( tag );

    //初始化src
    char str [100];

    while ( gets (str)!=NULL ){
        int k =0;
        for ( int i=0; i<9; ++ i,k ++ ){ //初始化src
            if ( str [k] == 'x' ){
                src.arr [i] = 0;
                src.blankPos = i;
            } else if ( str[k]>= '1' &&  str [k]<= '9'){
                src.arr [i] = str[k]-'0';
            } else {
                -- i;
            }
        }
        src .g = 0;
        src .h = get_H( src);
        src.hashPos = getHashValue ( src );

        //判断初始值
        if ( !isSloved ( src, tag) ){
            cout<<"unsolvable\n";
            //printf("unsolvable\n" ); 
            continue;
        } else if ( src.hashPos == tag.hashPos ){
            cout<<endl;
            //puts("");
            continue;
        }
        memset (ed_hash ,0, sizeof( ED_Hash )*MAX_ED_HASH_SIZE );
        ed_hash [src .hashPos ]. direction     = 1 ;
        ed_hash [src .hashPos ]. fatherHashPos = -1 ;

        //使用A*算法
        ED_Astar(src ,tag);
        getWay(tag );
    }            
}
  • 9
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A*算法IDA*算法都是启发式搜索算法,它们都是用于解决最短路径问题的常用算法。其中,A*算法在搜索时利用了启发式函数来估计从当前节点到目标节点的距离,从而选择最优的节点进行搜索。而IDA*算法则是在A*算法基础上做出的改进,其主要区别是不使用任何额外的内存空间,而是将估价函数的值作为深度限制,直到找到目标节点或者超过深度限制为止。 具体来说,A*算法维护一个open列表和一个closed列表。open列表中保存了待搜索的节点,closed列表中保存已经被搜索过的节点。每次从open列表中选择一个f值最小的节点进行扩展,并将其加入closed列表中。扩展时,对于当前节点的每个子节点计算估价函数值f(n)=g(n)+h(n),其中g(n)是从起点到当前节点的实际代价,h(n)是从当前节点到目标节点的估计代价。这样,每次选择f值最小的节点进行扩展时,就能够尽可能地朝着目标节点前进。 IDA*算法则是将A*算法中的open列表替换成一个栈,在搜索时将当前搜索深度作为阈值,不断更新阈值来限制搜索深度。具体来说,每次从起点开始,将阈值设置为起点到目标节点的估价函数值,然后进行深度优先搜索。如果搜索到某个节点的f(n)>threshold,则将其返回,并将阈值更新为f(n)。这样,IDA*算法通过不断增加深度限制来逐步逼近最短路径,直到找到目标节点或者搜索到达最大深度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值