A*搜索

又是最重要的32个算法之一。是一种静态路网中求解最短路最有效的直接搜索方法。后面还有更多改进的算法。


和其它的图搜索算法一样,A*潜在地搜索图中一个很大的区域。和Dijkstra一样,A*能用于搜索最短路径。和BFS(Best-First-Search而非Breadth-First-Search)一样,A*能用启发式函数引导它自己。在简单的情况中,它和BFS一样快。1968年发明的A*算法就是把启发式方法(heuristic approaches)如BFS,和常规方法如Dijsktra算法结合在一起的算法。有点不同的是,类似BFS的启发式方法经常给出一个近似解而不是保证最佳解。比较神奇的是,尽管A*基于无法保证最佳解的启发式方法,A*却能保证找到一条最短路径。
成功的秘决在于,它把Dijkstra算法(靠近初始点的结点)和BFS算法(靠近目标点的结点)的信息块结合起来。在讨论A*的标准术语中,g(n)表示从初始结点到任意结点n的代价,h(n)表示从结点n到目标点的启发式评估代价(heuristic estimated cost)。当从初始点向目标点移动时,A*权衡这两者。每次进行主循环时,它检查f(n)最小的结点n,其中f(n) = g(n) + h(n)


启发式函数h(n)决定了A*算法从任意结点n到目标结点的最小代价评估值。这个函数可以控制A*的行为:

  • 一种极端情况,如果h(n)是0,则只有g(n)起作用,此时A*演变成Dijkstra算法,这保证能找到最短路径。
  • 如果h(n)经常都比从n移动到目标的实际代价小(或者相等),则A*保证能找到一条最短路径。h(n)越小,A*扩展的结点越多,运行就得越慢。
  • 如果h(n)精确地等于从n移动到目标的代价,则A*将会仅仅寻找最佳路径而不扩展别的任何结点,这会运行得非常快。尽管这不可能在所有情况下发生,你仍可以在一些特殊情况下让它们精确地相等(译者:指让h(n)精确地等于实际值)。只要提供完美的信息,A*会运行得很完美,认识这一点很好。
  • 如果h(n)有时比从n移动到目标的实际代价高,则A*不能保证找到一条最短路径,但它运行得更快。
  • 另一种极端情况,如果h(n)比g(n)大很多,则只有h(n)起作用,A*演变成BFS算法。
接下来搞个代码看看,例如游戏寻路A*算法

<span style="font-size:18px;">/*
	A star 算法的基础处理
*/
#ifndef _A_STAR_BASE_H_
#define _A_STAR_BASE_H_
#include "windows.h"

typedef struct _APoint{
	int x; // x 坐标
	int y; // y 坐标
	int type; // 类型
	int f; // f = g + h
	int g; 
	int h;
} APoint,*PAPoint;

enum APointType{
	APT_UNKNOWN, // 未知状态
	APT_OPENED, // 开放列表中
	APT_CLOSED, // 关闭列表中
	APT_STARTPOINT, // 起始点
	APT_ENDPOINT // 结束点
};


class CAStarBase{
public:
	CAStarBase();
	~CAStarBase();
private:
	PAPoint m_pAPointArr;
	int m_nAPointArrWidth;
	int m_nAPointArrHeight;

	PAPoint m_pStartPoint,m_pEndPoint,m_pCurPoint;
	char* m_pOldArr;
public:
	BOOL Create(char* pDateArr,int nWidth,int nHeight);
	void SetStartPoint(int x,int y);
	void SetEndPoint(int x,int y);
	void SetOpened(int x,int y);
	void SetClosed(int x,int y);
	void SetCurrent( int x,int y );
	void PrintCharArr();

	PAPoint CalcNextPoint(PAPoint ptCalc); // 应用迭代的办法进行查询
};

#endif
</span>

<span style="font-size:18px;">#include "stdafx.h"
#include "AStarBase.h"


CAStarBase::CAStarBase()
{
	m_pAPointArr = NULL;
	m_nAPointArrWidth = 0;
	m_nAPointArrHeight = 0;

	m_pStartPoint = NULL;
	m_pEndPoint = NULL;
	m_pCurPoint = NULL;

}

CAStarBase::~CAStarBase()
{

}

BOOL CAStarBase::Create( char* pDateArr,int nWidth,int nHeight )
{
	if(!pDateArr) return FALSE;
	if(nWidth<1 || nHeight<1) return FALSE;
	m_pAPointArr = new APoint[nWidth*nHeight];
	if(!m_pAPointArr) return FALSE;
	m_pOldArr = pDateArr;
	m_nAPointArrWidth = nWidth;
	m_nAPointArrHeight = nHeight;
	// 初始化数组内容
	for ( int y = 0;y<m_nAPointArrHeight;y++)
	{
		for ( int x=0;x<m_nAPointArrWidth;x++)
		{
			m_pAPointArr[y*m_nAPointArrWidth+x].x = x;
			m_pAPointArr[y*m_nAPointArrWidth+x].y = y;
			m_pAPointArr[y*m_nAPointArrWidth+x].g = 0;
			m_pAPointArr[y*m_nAPointArrWidth+x].f = 0;
			m_pAPointArr[y*m_nAPointArrWidth+x].h = 0;

			if ( pDateArr[y*m_nAPointArrWidth+x] == '0')
			{
				m_pAPointArr[y*m_nAPointArrWidth+x].type = APT_OPENED;
			}else if ( pDateArr[y*m_nAPointArrWidth+x] == '1')
			{
				m_pAPointArr[y*m_nAPointArrWidth+x].type = APT_CLOSED;
			}else if ( pDateArr[y*m_nAPointArrWidth+x] == 'S')
			{
				m_pAPointArr[y*m_nAPointArrWidth+x].type = APT_STARTPOINT;
				m_pStartPoint = m_pAPointArr + y*m_nAPointArrWidth+x;
				m_pCurPoint = m_pStartPoint;
			}else if ( pDateArr[y*m_nAPointArrWidth+x] == 'E')
			{
				m_pAPointArr[y*m_nAPointArrWidth+x].type = APT_ENDPOINT;
				m_pEndPoint = m_pAPointArr + y*m_nAPointArrWidth+x;
			}else{
				m_pAPointArr[y*m_nAPointArrWidth+x].type = APT_UNKNOWN;
			}

		}
	}
	return TRUE;
}

void CAStarBase::SetStartPoint( int x,int y )
{
	if ( m_pStartPoint && m_pAPointArr[y*m_nAPointArrWidth+x].type!=APT_CLOSED )
	{
		m_pStartPoint->type = APT_OPENED;
		// 设置新的值
		m_pStartPoint = m_pAPointArr + y*m_nAPointArrWidth+x;
		m_pStartPoint->type = APT_STARTPOINT;
		m_pCurPoint = m_pStartPoint;
	}
}

void CAStarBase::SetEndPoint( int x,int y )
{
	if ( m_pStartPoint && m_pAPointArr[y*m_nAPointArrWidth+x].type!=APT_CLOSED )
	{
		m_pStartPoint->type = APT_OPENED;
		// 设置新的值
		m_pStartPoint = m_pAPointArr + y*m_nAPointArrWidth+x;
		m_pStartPoint->type = APT_ENDPOINT;
	}
}

void CAStarBase::SetCurrent( int x,int y )
{
//	if ( m_pAPointArr[y*m_nAPointArrWidth+x].type==APT_OPENED )
	{
		m_pCurPoint = m_pAPointArr+y*m_nAPointArrWidth+x;
	}
}

void CAStarBase::SetOpened( int x,int y )
{
	if ( m_pAPointArr[y*m_nAPointArrWidth+x].type!=APT_OPENED )
	{
		m_pAPointArr[y*m_nAPointArrWidth+x].type = APT_OPENED;
	}
}

void CAStarBase::SetClosed( int x,int y )
{
	if ( m_pAPointArr[y*m_nAPointArrWidth+x].type!=APT_CLOSED )
	{
		m_pAPointArr[y*m_nAPointArrWidth+x].type = APT_CLOSED;
	}
}

void CAStarBase::PrintCharArr()
{
	if ( m_pOldArr )
	{
		for ( int y=0; y<m_nAPointArrHeight;y++)
		{
			for ( int x=0;x<m_nAPointArrWidth;x++)
			{
				printf("%c ",m_pOldArr[x+m_nAPointArrWidth*y]);
			}
			printf("\r\n");
		}
		printf("\r\n");
	}
}

PAPoint CAStarBase::CalcNextPoint( PAPoint ptCalc )
{
	if ( ptCalc == NULL )
	{
		ptCalc = m_pStartPoint;
	}
	int x = ptCalc->x;
	int y = ptCalc->y;
	int dx = m_pEndPoint->x;
	int dy = m_pEndPoint->y;
	int xmin = x,ymin = y,vmin = 0; // 最优步骤的坐标和值
	// 判断是否已经到了最终的位置
	if ( (x==dx && abs(y-dy)==1) || (y==dy && abs(x-dx)==1) )
	{
		return m_pEndPoint;
	}
	// 上
	if ( m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].type == APT_OPENED && y>0)
	{
		m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].g = 10;
		m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].h = 
			10*(abs(x - dx) + abs(y-1 - dy));
		m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].f = 
			m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].g + m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].h;
		if ( vmin==0 )
		{
			xmin = x;
			ymin = y-1;
			vmin = m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].f;
		}else{
			if ( vmin > m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].f )
			{
				xmin = x;
				ymin = y-1;
				vmin = m_pAPointArr[(x+0)+m_nAPointArrWidth*(y-1)].f;
			}
		}
	}
	// 下
	if ( m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].type == APT_OPENED && y<m_nAPointArrHeight)
	{
		m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].g = 10;
		m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].h = 
			10*(abs(x - dx) + abs(y+1 - dy));
		m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].f = 
			m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].g + m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].h;
		if ( vmin==0 )
		{
			xmin = x;
			ymin = y+1;
			vmin = m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].f;
		}else{
			if ( vmin > m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].f )
			{
				xmin = x;
				ymin = y+1;
				vmin = m_pAPointArr[(x+0)+m_nAPointArrWidth*(y+1)].f;
			}
		}
	}
	// 左
	if ( m_pAPointArr[(x-1)+m_nAPointArrWidth*y].type == APT_OPENED && x>0)
	{
		m_pAPointArr[(x-1)+m_nAPointArrWidth*y].g = 10;
		m_pAPointArr[(x-1)+m_nAPointArrWidth*y].h = 
			10*(abs(x-1 - dx) + abs(y - dy));
		m_pAPointArr[(x-1)+m_nAPointArrWidth*y].f = 
			m_pAPointArr[(x-1)+m_nAPointArrWidth*y].g + m_pAPointArr[(x-1)+m_nAPointArrWidth*y].h;
		if ( vmin==0 )
		{
			xmin = x-1;
			ymin = y;
			vmin = m_pAPointArr[(x-1)+m_nAPointArrWidth*y].f;
		}else{
			if ( vmin > m_pAPointArr[(x-1)+m_nAPointArrWidth*y].f )
			{
				xmin = x-1;
				ymin = y;
				vmin = m_pAPointArr[(x-1)+m_nAPointArrWidth*y].f;
			}
		}
	}
	// 右
	if ( m_pAPointArr[(x+1)+m_nAPointArrWidth*y].type == APT_OPENED && x<m_nAPointArrWidth)
	{
		m_pAPointArr[(x+1)+m_nAPointArrWidth*y].g = 10;
		m_pAPointArr[(x+1)+m_nAPointArrWidth*y].h = 
			10*(abs(x+1 - dx) + abs(y - dy));
		m_pAPointArr[(x+1)+m_nAPointArrWidth*y].f = 
			m_pAPointArr[(x+1)+m_nAPointArrWidth*y].g + m_pAPointArr[(x+1)+m_nAPointArrWidth*y].h;
		if ( vmin==0 )
		{
			xmin = x+1;
			ymin = y;
			vmin = m_pAPointArr[(x+1)+m_nAPointArrWidth*y].f;
		}else{
			if ( vmin > m_pAPointArr[(x+1)+m_nAPointArrWidth*y].f )
			{
				xmin = x+1;
				ymin = y;
				vmin = m_pAPointArr[(x+1)+m_nAPointArrWidth*y].f;
			}
		}
	}

	// 如果有最优点则迭代,则否就返回NULL
	if ( vmin )
	{
		SetCurrent(xmin,ymin);
		SetClosed(xmin,ymin);
		*(m_pOldArr+xmin+m_nAPointArrWidth*ymin) = '-';
		PrintCharArr();
		PAPoint pApoint = CalcNextPoint(m_pCurPoint);
		if ( pApoint == NULL )
		{
			SetCurrent(x,y);
			SetClosed(xmin,ymin);
			*(m_pOldArr+xmin+m_nAPointArrWidth*ymin) = '0';
			return CalcNextPoint(m_pCurPoint);
		}
		return pApoint;
	}else{
		return NULL;
	}

}
</span>

看看上面的代码,算法的伪程序如下:

  A_Star_Search()
  {
    Open = [起始节点];
    Closed = [];
    while ( Open表非空 )
    {
      从Open中取得一个节点X, 并从OPEN表中删除.
      if (X是目标节点)
      {
        求得路径PATH;
        返回路径PATH;
      }
      for (每一个X的子节点Y)
      {
        if( Y不在OPEN表和CLOSE表中 )
        {
          求Y的估价值;
          并将Y插入OPEN表中; 
//还没有排序
        }
        else if( Y在OPEN表中 )
        {
          if( Y的估价值小于OPEN表的估价值 )
            更新OPEN表中的估价值;
        }
        else
//Y在CLOSE表中
        {
          if( Y的估价值小于CLOSE表的估价值 )
          {
            更新CLOSE表中的估价值;
            从CLOSE表中移出节点, 并放入OPEN表中;
          }
        }
        将X节点插入CLOSE表中;
        按照估价值将OPEN表中的节点排序;
      }
 //end for
    } //end while
  } //end func


上下左右注释后面的if代码片,就是估值过程。

A*算法也有变种,Beam Search(集束搜索)就是其中一种,只是BS中的open集有大小限制,不像A*保存所有需要检查的节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值