求教,关于A*算法的优化,达人们请进

cwbcwb505 2011-01-28 05:40:26
以下代码只能在MFC里面运行,因为用到了mfc的CPoint之类的东西,我已经尽力优化了,但是在地图达到400*600的时候,而且阻挡点较多的时候,寻路就会达到0.5秒左右,这个太慢了,无法实际应用,有人曾经对我说过真正的A*算法用在游戏的路中是有问题的,求各位达人帮忙优化,或是有别的解决方案,还是怎么将这个A*进行变种?让小弟开开眼界。

// Astar.h
#pragma once
#include "MemAlloc.h"

#include <vector>
#include <list>

using namespace std;

class CMapNode
{
public:
CMapNode(){Clear();}

void Clear()
{
m_Point.x = 0;
m_Point.y = 0;
m_nF = 0;
m_nG = 0;
m_nH = 0;
m_pParent = NULL;
m_nSerialNumber = 0;
}

inline void* operator new(size_t size){return m_memAlloc.Alloc(size);}
inline void operator delete(void* p){m_memAlloc.Delete(p);}

public:
CPoint m_Point;
int m_nF;
int m_nG;
int m_nH;
size_t m_nSerialNumber; //代表这个节点的编号
CMapNode* m_pParent;

private:
static CMemAlloc m_memAlloc;
};

//这里面统一不能通过的点为1;能通过的为0
class CAStar
{
public:
CAStar(void);
~CAStar(void);

bool FindPath(CPoint psrc, CPoint pdes);
void InitMap(CSize mapSize, long* pObstacle);
vector<CPoint> GetPointPath()const;
void PrintMap()const;
void PrintMap(CSize mapSize, long* map)const;

private:
bool IsPointInMap(const CPoint& pt)const;
void Init();
void Destroy();
void ResetPath();
CMapNode* GetBestNode();
CMapNode* CreateMapNode(const CPoint& pt, CMapNode* parent = NULL);
void CheckCircumjacentNode(CMapNode* pNode);
void CheckACircumjacentNode(CMapNode* pSrcNode, const CPoint& desPt);
CMapNode* GetOpenNode(const CPoint& pt);
void AddToOpenList(CMapNode* pt);
void RemoveOpenListFirstElement();
size_t GetMinChildIdx(size_t parentIdx)const;
inline int GetG(const CPoint& pt1, const CPoint& pt2)const;
void ValuePoint(CPoint ptSrc, CPoint& ptDest, bool bCheckNPC = false);

inline bool IsCanPass(const CPoint& pt){return 0 == m_pnMap[GetSerialNumber(pt)];}
inline size_t GetSerialNumber(const CPoint& pt)const{return pt.y * m_MapSize.cx + pt.x;}

private:
long* m_pnMap; //用一维数组来表示一张地图
size_t m_MapLenth; //就是地图的大小
CSize m_MapSize;
list<CMapNode*> m_Close;
CMapNode** m_pOpen;
CMapNode* m_DesNode;
size_t m_OpenNodeNum;
CPoint m_DesPt;
CPoint m_OffsetPt;
};

inline int abs(int n)
{
if (n > 0)
{
return n;
}
else
{
return -n;
}
}


// MemAlloc.h
#pragma once
#include <vector>
#include <string>

using namespace std;

class CMemError
{
public:
CMemError(const char* p){m_strError = p;}
const char* GetErrorMsg()const{return m_strError.c_str();}

private:
string m_strError;
};
struct MemList
{
MemList* pNext;
};

// 这个只能用于单线程!!
class CMemAlloc
{
public:
enum{
ChunkSize = 256, //一块内存有多少个小内存组成
MinMemSize = 4, //一个内存最小为4字节
};

CMemAlloc(void);
~CMemAlloc(void);
inline void* Alloc(size_t size);
inline void Delete(void* p);

private:
inline void AllocChunk(size_t size);

private:
vector<char*> m_MemChunk; //申请的所有内存块
MemList* m_pMemList; //备用内存
size_t m_nMemSize; //每一个内存的大小
};

void* CMemAlloc::Alloc(size_t size)
{
if (0 == m_nMemSize)
{
m_nMemSize = size;
}
if (m_nMemSize != size)
{
throw CMemError("一个 CMemAlloc 对象只能分配一种大小的内存");
}
if (NULL == m_pMemList)
{
AllocChunk(size);
}

void* pRet = reinterpret_cast<void*>(m_pMemList);
m_pMemList = m_pMemList->pNext;
return pRet;
}

void CMemAlloc::Delete(void* p)
{
if (NULL == p) //删除空指针是安全的
{
return;
}

reinterpret_cast<MemList*>(p)->pNext = m_pMemList;
m_pMemList = reinterpret_cast<MemList*>(p);
}

void CMemAlloc::AllocChunk(size_t size)
{
if (size < MinMemSize)
{
size = MinMemSize;
}
char* p = reinterpret_cast<char*>(malloc(ChunkSize * size));
MemList* pList;
for (int i = 0; i < ChunkSize; ++i)
{
pList = reinterpret_cast<MemList*>(p + (i * size));
pList->pNext = m_pMemList;
m_pMemList = pList;
}
m_MemChunk.push_back(p);
}


// MemAlloc.cpp

#include "StdAfx.h"
#include "MemAlloc.h"

CMemAlloc::CMemAlloc(void)
{
m_pMemList = NULL;
m_nMemSize = 0;
}

CMemAlloc::~CMemAlloc(void)
{
m_pMemList = NULL;
for (vector<char*>::iterator it = m_MemChunk.begin(); it != m_MemChunk.end(); ++it)
{
free(*it);
}
m_MemChunk.clear();
}


...全文
283 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
cwbcwb505 2011-02-10
  • 打赏
  • 举报
回复
yyfhz 2011-02-10
  • 打赏
  • 举报
回复
如果条件更加复杂一点,比方说不同的区域内行走的单位长度耗费不一样,上面的算法就不能直接拿来用了,对于任意两个点,需要利用物理上的光线折射公式来求出折射点(可能还不止1个),然后再判断这条光线会不会被障碍物挡住。
yyfhz 2011-02-10
  • 打赏
  • 举报
回复
8L用的确不是A*算法,其实是先对图中的障碍物进行预处理所得到的。
LZ可以设想一下,在地图上从起始点A到终点B随便画一条线,然后用力把它绷紧,结果会怎样?结果这条线肯定会变成一段一段的折线段。
折点在哪里?折点自然在那些障碍物的边缘。
是否可能会出现弧形?可能的,如果障碍物的边缘是一个曲线的话。
那么,如果障碍物的边缘是直线段的话,这条线是否还可能出现弧形?不可能了(这就是我一开始说要用直线段来模拟障碍物边缘的原因)

所以,假设现在一共有n个障碍物线段(其端点编号依次为Z[1],Z[2],...,Z[2n]),则从起始点A到目标点B
的最短路径必然可以由以下格式表达的点前后互联形成
1) {A, Z[i1], Z[i2],...,Z[ik], B}
这条路径表示从先从A直线连接到端点Z[i1],再从Z[i1]直线连接到端点Z[i2],如此继续,一直连接到端点Z[ik],最后从Z[ik]直线连接到B。
既然这样,那么实际运算的时候,只要考虑那些端点的位置以及哪些端点可以直接用直线段连接就可以了(这就是我说的为什么要重新画一个端点图的原因)

如果LZ做的是静态地图的寻径问题,那么地图中障碍物的位置就不会发生变化,因此可以进行预先处理,以便节省实时处理的时间。

如果有足够多的内存可用,LZ甚至可以先计算端点图中任意两个端点之间的最小代价路径(需要2n*2n个存储结构体),然后在以后的计算中就可以直接省略掉1)中Z[i1]~Z[ik]之间的路径计算了,这样更快。
qq120848369 2011-02-09
  • 打赏
  • 举报
回复
优先队列,位压缩哈希。
cwbcwb505 2011-02-09
  • 打赏
  • 举报
回复
上面的确是使用的堆排序,所以找那个最优点不会花太多的时间,还有就是我这个也没有频繁的使用net和delete,使用的是内存池,是在一开始的时候就把内存已经申请好了。还有就是8楼的那个算法好像和A*差别有点大,有点深奥。
yyfhz 2011-02-09
  • 打赏
  • 举报
回复
LZ大概是用一个2D数组之类的来表示一张地图的连通性,然后给出起止位置来找最短路径吧?
粗略的想了一下,有个思路,不知道有没有用。

1. 用短的直线段来模拟障碍物的边界,假设一共有n条障碍物线段,这样一共会出现2n个端点。
2. 在1张白纸上将这2n个点画在上面,计算这2n个端点之间的两两的直线距离。如果某2个端点之间的直线距离上会出现其它障碍物,则不必计算这2个端点之间的直线距离,否则计算这2个端点之间的直线距离,并将这2个点标记为连通。
3. 经过上述预处理步骤,现在在白纸上已经出现了各个障碍物线段的端点的可连通图了,后面的计算主要要靠它。
4. 当需要进行最短路径计算时,直接将起始点和终止点加入到白纸上,然后
4.1如果起始点和终止点之间没有其它障碍物,计算起始点和终止点的直线距离并返回
4.2如果起始点和终止点之间有其它障碍物(哪怕只有1个),则仿照第2步,分别计算起始点到各个障碍物线段端点的可连通性以及直线距离(如果起始点到该端点之间没有其它障碍物的话),对终止点也照此方式处理。
然后在经过4.2处理的白纸上(现在这张白纸上已经画了一个包含起止点的图了)求这个图中起止点之间的最短路径(什么Dijkstra, SPFA等等)并将这个最短路径中所有经过的点拿直线连接起来就行了,

从道理上来讲,这样子做的话地图的预处理需要花费比较长的时间,但那只是1次性的工作量,后面做起来就很快了。
xz0404 2011-01-30
  • 打赏
  • 举报
回复
使用堆排序
瓶盒 2011-01-29
  • 打赏
  • 举报
回复
代码太长,没法细看,不过如果对A*算法调用比较频繁的话,就不能用new和delete操作。
cwbcwb505 2011-01-29
  • 打赏
  • 举报
回复
优先列队是什么东东,请大虾指出来? 我那个最优点是排序的,不会一个一个的找,还有我那个排序用时间复杂度也不大,应该是o(logn)之类的,反正没有o(n)大
leonardWang 2011-01-28
  • 打赏
  • 举报
回复
A*至少要有优先队列这个数据结构 找最大值最小值不是线性的一个个去找的
cwbcwb505 2011-01-28
  • 打赏
  • 举报
回复
以上代码有任何问题请大虾们指出,比如说什么逻辑错误,或是规划不对,或是代码风格不好,总之只要你有觉得不爽的地方,请你指出来,并给出改进方法,小弟感激不尽!
cwbcwb505 2011-01-28
  • 打赏
  • 举报
回复

// CAstar.cpp
#include "StdAfx.h"
#include ".\astar.h"

#include <cstring>
#include <fstream>

using namespace std;

CMemAlloc CMapNode::m_memAlloc;

CAStar::CAStar(void)
{
Init();
}

CAStar::~CAStar(void)
{
Destroy();
}

void CAStar::InitMap(CSize mapSize, long* pObstacle)
{
Destroy();
m_MapSize = mapSize;
//将地图的周边都加一个位置,设置成障碍物,以免寻路的时候,会走的边界以外去
m_MapSize.cx += 2;
m_MapSize.cy += 2;
m_OffsetPt.x = -1;
m_OffsetPt.y = -1;
m_MapLenth = m_MapSize.cx * m_MapSize.cy;
m_pnMap = new long[m_MapLenth];

memset(m_pnMap, 0, sizeof(long) * m_MapLenth);
for (int i = 0; i < m_MapSize.cx; ++i)
{
m_pnMap[i] = 1; //上底
m_pnMap[m_MapLenth - i - 1] = 1; //下底
}
for (int i = 0; i < m_MapLenth; i += m_MapSize.cx)
{
m_pnMap[i] = 1; //左边
m_pnMap[i + m_MapSize.cx - 1] = 1;//右边
}

//设置地图中间的障碍物
for (int i = 0; i < mapSize.cy; ++i)
{
memcpy(m_pnMap + (m_MapSize.cx * (i + 1) + 1), pObstacle + (mapSize.cx * i), sizeof(long) * mapSize.cx);
}
}

bool CAStar::FindPath(CPoint psrc, CPoint pdes)
{
//因为我们加了地图边界
psrc -= m_OffsetPt;
pdes -= m_OffsetPt;

if (!IsPointInMap(psrc) || !IsPointInMap(pdes))
{
return false;
}

ResetPath();
ValuePoint(psrc, pdes, true);
size_t desSerialNumber = GetSerialNumber(psrc);
m_DesPt = psrc;
m_pOpen = new CMapNode*[m_MapSize.cx * m_MapSize.cy];
CMapNode* pNode = CreateMapNode(pdes);
AddToOpenList(pNode);

while(true)
{
pNode = GetBestNode();
if (NULL == pNode) //没有路可走了
{
return false;
}
if (pNode->m_nSerialNumber == desSerialNumber)//找到目的地了
{
m_DesNode = pNode;
return true;
}
CheckCircumjacentNode(pNode);
}
return true;
}

vector<CPoint> CAStar::GetPointPath()const
{
vector<CPoint> vpt;
CPoint pt;
CMapNode* pNode = m_DesNode;
while(NULL != pNode)
{
pt.x = pNode->m_Point.x + m_OffsetPt.x;
pt.y = pNode->m_Point.y + m_OffsetPt.y;
vpt.push_back(pt);
pNode = pNode->m_pParent;
}
return vpt;
}

bool CAStar::IsPointInMap(const CPoint& pt)const
{
if (pt.x < 1 || pt.y < 1)
{
return false;
}
else if (pt.x >= m_MapSize.cx - 1 || pt.y >= m_MapSize.cy - 1)
{
return false;
}
else
{
return true;
}
}

void CAStar::PrintMap()const
{
if (NULL == m_pnMap)
{
return;
}

ofstream m_file;
m_file.open("mmap.txt", ios_base::out | ios_base::app);
for (int i = 0; i < m_MapSize.cy; ++i)
{
CString strLine;
for (int j = 0; j < m_MapSize.cx; ++j)
{
if (0 == m_pnMap[(i * m_MapSize.cx) + j])
{
strLine += '0';
}
else if(10 == m_pnMap[(i * m_MapSize.cx) + j])
{
strLine += '+';
}
else
{
strLine += '-';
}
}
m_file << (LPCSTR)strLine << endl;
}
m_file.flush();
m_file.close();
}

void CAStar::PrintMap(CSize mapSize, long* map)const
{
if (NULL == m_pnMap)
{
return;
}

ofstream m_file;
m_file.open("mmap.txt", ios_base::out | ios_base::app);
for (int i = 0; i < mapSize.cy; ++i)
{
CString strtemp;
CString strLine;
for (int j = 0; j < mapSize.cx; ++j)
{
strtemp.Format("%d", map[(i * m_MapSize.cx) + j]);
strLine += strtemp;
}
m_file << (LPCSTR)strLine << endl;
}
m_file.flush();
m_file.close();
}

void CAStar::Init()
{
m_pnMap = NULL;
m_pOpen = NULL;
m_DesNode = NULL;
m_OpenNodeNum = 0;
m_MapSize.cx = 0;
m_MapSize.cy = 0;
}

void CAStar::Destroy()
{
delete m_pnMap;
m_pnMap = NULL;

m_MapSize.cx = 0;
m_MapSize.cy = 0;

ResetPath();
}

void CAStar::ResetPath()
{
for(int i = 0; i < m_OpenNodeNum; ++i)
{
ASSERT(m_pOpen);
delete m_pOpen[i];
m_pOpen[i] = NULL;
}
delete[] m_pOpen;
m_pOpen = NULL;
m_OpenNodeNum = 0;

for (list<CMapNode*>::iterator it = m_Close.begin(); it != m_Close.end(); ++it)
{
delete *it;
}
m_Close.clear();

m_DesNode = NULL; //它同时也在m_Close里面,所以这里就不用delete了
}

CMapNode* CAStar::GetBestNode()
{
if (m_OpenNodeNum <= 0)
{
return NULL;
}

CMapNode* temp = m_pOpen[0];
m_Close.push_back(temp);
m_pnMap[temp->m_nSerialNumber] = 10;//将关闭列表中的结点通通设为不可达
RemoveOpenListFirstElement();
return temp;
}

CMapNode* CAStar::CreateMapNode(const CPoint& pt, CMapNode* parent /*= NULL*/)
{
CMapNode* pNode =new CMapNode;
pNode->m_Point = pt;
pNode->m_nSerialNumber = GetSerialNumber(pt);
if (NULL != parent)
{
pNode->m_pParent = parent;
pNode->m_nG = GetG(pt, parent->m_Point) + parent->m_nG;
pNode->m_nH = (abs(pt.x - m_DesPt.x) + abs(pt.y - m_DesPt.y)) * 10;
pNode->m_nF = pNode->m_nG + pNode->m_nH;
}

return pNode;
}

void CAStar::CheckCircumjacentNode(CMapNode* pNode)
{
ASSERT(pNode);
CPoint tempPt;

//右边
CheckACircumjacentNode(pNode, CPoint(pNode->m_Point.x + 1, pNode->m_Point.y));

//右下
CheckACircumjacentNode(pNode, CPoint(pNode->m_Point.x + 1, pNode->m_Point.y + 1));

//下边
CheckACircumjacentNode(pNode, CPoint(pNode->m_Point.x, pNode->m_Point.y + 1));

//左下
CheckACircumjacentNode(pNode, CPoint(pNode->m_Point.x - 1, pNode->m_Point.y + 1));

//左边
CheckACircumjacentNode(pNode, CPoint(pNode->m_Point.x - 1, pNode->m_Point.y));

//左上
CheckACircumjacentNode(pNode, CPoint(pNode->m_Point.x - 1, pNode->m_Point.y - 1));

//上边
CheckACircumjacentNode(pNode, CPoint(pNode->m_Point.x, pNode->m_Point.y - 1));

//右上
CheckACircumjacentNode(pNode, CPoint(pNode->m_Point.x + 1, pNode->m_Point.y - 1));

}

void CAStar::CheckACircumjacentNode(CMapNode* pSrcNode, const CPoint& desPt)
{
ASSERT(pSrcNode);
CMapNode* DesNode;
if (IsCanPass(desPt))
{
DesNode = GetOpenNode(desPt);
if (NULL == DesNode)
{
DesNode = CreateMapNode(desPt, pSrcNode);
AddToOpenList(DesNode);
}
else
{
if (DesNode->m_nG > (pSrcNode->m_nG + GetG(desPt, pSrcNode->m_Point)))
{
DesNode->m_pParent = pSrcNode;
DesNode->m_nG = pSrcNode->m_nG + GetG(desPt, pSrcNode->m_Point);
DesNode->m_nF = DesNode->m_nG + DesNode->m_nH;
}
}
}
}

CMapNode* CAStar::GetOpenNode(const CPoint& pt)
{
size_t serialNum = GetSerialNumber(pt);
for (int i = 0; i < m_OpenNodeNum; ++i)
{
if (serialNum == m_pOpen[i]->m_nSerialNumber)
{
return m_pOpen[i];
}
}
return NULL;
}

void CAStar::AddToOpenList(CMapNode* pNode)
{
m_pOpen[m_OpenNodeNum] = pNode;
++m_OpenNodeNum;
size_t childIdx = m_OpenNodeNum;
size_t parentIdx = childIdx >> 1;
while(parentIdx > 0 && m_pOpen[parentIdx - 1]->m_nF > m_pOpen[childIdx - 1]->m_nF)
{
CMapNode* temp;
temp = m_pOpen[parentIdx - 1];
m_pOpen[parentIdx - 1] = m_pOpen[childIdx - 1];
m_pOpen[childIdx - 1] = temp;

childIdx = parentIdx;
parentIdx = childIdx >> 1;
}
}

void CAStar::RemoveOpenListFirstElement()
{
if (0 == m_OpenNodeNum)
{
return;
}

--m_OpenNodeNum;
m_pOpen[0] = m_pOpen[m_OpenNodeNum];
m_pOpen[m_OpenNodeNum] = NULL;
size_t parentIdx = 1;
size_t minChildIdx = GetMinChildIdx(parentIdx);
while(minChildIdx <= m_OpenNodeNum && m_pOpen[parentIdx - 1]->m_nF > m_pOpen[minChildIdx - 1]->m_nF)
{
CMapNode* temp;
temp = m_pOpen[parentIdx - 1];
m_pOpen[parentIdx - 1] = m_pOpen[minChildIdx - 1];
m_pOpen[minChildIdx - 1] = temp;

parentIdx = minChildIdx;
minChildIdx = GetMinChildIdx(parentIdx);
}
}

size_t CAStar::GetMinChildIdx(size_t parentIdx)const
{
size_t childIdx = parentIdx * 2;
if (childIdx < m_OpenNodeNum)
{
if (m_pOpen[childIdx - 1]->m_nF > m_pOpen[childIdx]->m_nF)
{
return childIdx + 1;
}
else
{
return childIdx;
}
}
else if (childIdx == m_OpenNodeNum)
{
return childIdx;
}
else
{
return m_OpenNodeNum + 1; //返回一个不全法的位置,表示它没有孩子了
}
}

int CAStar::GetG(const CPoint& pt1, const CPoint& pt2)const
{
if ((pt1.x - pt2.x) && (pt1.y - pt2.y)) //斜角
{
return 14;
}
else
{
return 10;
}
}

void CAStar::ValuePoint(CPoint ptSrc, CPoint& ptDest, bool bCheckNPC /*= false*/)//修改ptDest
{
//查看目标点有没有被包围,如果是的话,帮它选一个最近的目标点,要在是NPC寻路的情况下
if (true == bCheckNPC)
{
int nObstacleNum = 0;
if (!IsCanPass(CPoint(ptDest.x + 1, ptDest.y)))
{
++nObstacleNum;
}
if (!IsCanPass(CPoint(ptDest.x + 1, ptDest.y + 1)))
{
++nObstacleNum;
}
if (!IsCanPass(CPoint(ptDest.x, ptDest.y + 1)))
{
++nObstacleNum;
}
if (!IsCanPass(CPoint(ptDest.x - 1, ptDest.y + 1)))
{
++nObstacleNum;
}
if (!IsCanPass(CPoint(ptDest.x - 1, ptDest.y)))
{
++nObstacleNum;
}
if (!IsCanPass(CPoint(ptDest.x - 1, ptDest.y - 1)))
{
++nObstacleNum;
}
if (!IsCanPass(CPoint(ptDest.x, ptDest.y - 1)))
{
++nObstacleNum;
}
if (!IsCanPass(CPoint(ptDest.x + 1, ptDest.y - 1)))
{
++nObstacleNum;
}

if (8 == nObstacleNum) //周围只有8格,如果它=8的话,就是说它周围都围满了
{
if (ptDest.y > ptSrc.y)
{
ptDest.y -= 2;
}
else
{
ptDest.y += 2;
}

if (ptDest.x > ptSrc.x)
{
ptDest.x -= 2;
}
else
{
ptDest.x += 2;
}
}
}

//将目标坐标转换成有效坐标 避免点中障碍后 没反映
//得到1 或 -1
if (!m_pnMap[ptDest.x + ptDest.y * m_MapSize.cx])
{
return;
}
int x = 0;
if (ptSrc.x != ptDest.x)
{
x = (ptSrc.x - ptDest.x)/abs(ptSrc.x - ptDest.x);
}
int y = 0;
if (ptSrc.y != ptDest.y)
{
y = (ptSrc.y - ptDest.y)/abs(ptSrc.y - ptDest.y);
}
while (m_pnMap[ptDest.x + ptDest.y * m_MapSize.cx])
{
if (ptDest.x != ptSrc.x)
{
ptDest.x = ptDest.x + x;
}
if (ptDest.y != ptSrc.y)
{
ptDest.y = ptDest.y + y;
}
}
}

33,010

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧