我已经实现了A *寻路,它可以完美地适用于较小的网格。但是,当地图变大并且不再是迷宫结构时,例如下图所示的地图,算法变得越来越慢。
根据A *的定义,我使用的是开放列表和封闭列表。打开列表使用std::set
实现。封闭列表使用Qt的QSet
实现。
QSet
是Qt对std::unordered_list
的实现。
在分析我的应用程序之后,我注意到std::set
树的重新平衡是最昂贵的操作。当在两个不同的地图中运行算法时,这是显而易见的,下面显示的是一个大的打开列表大小,另一个是类似迷宫的地图,打开列表大小要低得多。
在类似迷宫的地图中,我的打开列表的大小会在20到120个节点之间波动。开放地图慢慢增长到超过2000个节点。
所以我的问题是,是否有办法减少开放清单的大小?
我尝试了以下方法:
将打开列表更改为std::priority_queue
:我无法实现此操作,因为我需要检查打开的列表以查看它是否已包含该元素。如果我错了,请纠正我,但是priority_queue会不会遇到同样的重新平衡问题?
使用更高的启发式权重:这没有解决问题,打开列表中节点的数量级仍然相同。
剪切打开列表中的节点:这导致了更快的直通方式,但通常导致找不到路径。最初我认为这会起作用,因为我只会用更高的F(启发式+运动)成本修剪这些成本,这本身就变得无关紧要了。这个假设被证明是错误的。
提前致谢。
EDIT1: 添加了一些代码以便澄清。
std::shared_ptr<Node> Pathfinding::findPath(float heuristicWeight) {
int i = 0;
while (!m_sOpen.empty()) {
++i;
std::shared_ptr<Node> current = *m_sOpen.begin();
m_sOpen.erase(current);
m_sClosed.insert(*current);
if (updateNeighbours(current, heuristicWeight)) {
return std::make_shared<Node>(*m_sClosed.find(*m_nEnd));
}
if (i % 100 == 0) {
qInfo() << "Sizes: " << i << " open_size= " << m_sOpen.size() << " & closed_size= " << m_sClosed.size();
}
}
return NULL;
}
bool Pathfinding::updateNeighbours(std::shared_ptr<Node> current, float heuristicWeight) {
int maxRows = wm.getRows(); // Rows in map
int maxCols = wm.getCols(); // Cols in map
for (int x = clamp((current->getX()-1),0,maxCols-1); x <= clamp((current->getX()+1),0,maxCols-1); ++x) {
for (int y = clamp((current->getY()-1),0,maxRows-1); y <= clamp((current->getY()+1),0,maxRows-1); ++y) {
bool exists = false;
Node n = Node(x,y); // Node to compare against and insert if nessecary.
// Tile contains information about the location in the grid.
Tile * t = wm.m_tTiles[(x)+(maxCols * y)].get();
if (t->getValue() != INFINITY) { // Tile is not a wall.
for (std::set<std::shared_ptr<Node>>::iterator it = m_sOpen.begin(); it != m_sOpen.end(); ++it) {
if (**it == n) {
exists = true;
if ((*it)->getF() > (current->getG() + moveCost(*it,current)) + (*it)->getH()) {
(*it)->setG(current->getG() + moveCost(*it,current));
(*it)->setParent(current);
}
break;
}
}
bool exists_closed = (m_sClosed.find(n) != m_sClosed.end());
if (!exists && !exists_closed) {
std::shared_ptr<Node> sN = std::make_shared<Node>(n);
sN->setParent(current);
sN->setG(current->getG() + moveCost(sN,current));
sN->setH(manhattenCost(sN,m_nEnd)*heuristicWeight);
if (sN->getH() == 0) { m_sClosed.insert(*sN); return true; }
else m_sOpen.insert(sN);
}
}
}
}
return false;
}
答案 0 :(得分:0)
从std::set
切换到std::priority_queue
。在将节点添加到队列之前,无需检查节点是否已在开放集中。如果它已经存在,那么将它插入封闭的集合中会更便宜。