A *寻路 - 慢速打开巨大的地图

时间:2016-12-07 18:19:11

标签: c++ algorithm qt dijkstra path-finding

我已经实现了A *寻路,它可以完美地适用于较小的网格。但是,当地图变大并且不再是迷宫结构时,例如下图所示的地图,算法变得越来越慢。

open map

根据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;
}

1 个答案:

答案 0 :(得分:0)

std::set切换到std::priority_queue。在将节点添加到队列之前,无需检查节点是否已在开放集中。如果它已经存在,那么将它插入封闭的集合中会更便宜。