【问题标题】:A* Performance at large mapsA* 在大地图上的表现
【发布时间】:2016-06-06 19:23:08
【问题描述】:

我想为我的 AStar 算法搜索提供一些帮助,从我的角度来看,这需要很长时间。即使我的地图具有 500 * 400 坐标(客观上是我的瓷砖图有点小,因为我没有将墙壁放入 TileGraph。)大,我希望几秒钟后得到结果。世界是这样的,尽管任务不是我的

我想从标记的坐标“Start”(120|180) 搜索到“Ziel”(320|220),目前需要 48 分钟。对不起所有不会说德语的人,但图片上的文字并不重要。

首先,我想向您展示我为 A* 编写的程序。总的来说,我自己适应了https://en.wikipedia.org/wiki/A*_search_algorithm 的伪代码。

bool AStarPath::Processing(Node* Start, Node* End)
m_Start = Start;
m_End = End;
for (Node* n : m_SearchRoom->GetAllNodes())
{
    DistanceToStart[n] = std::numeric_limits<float>::infinity();
    CameFrom[n] = nullptr;
}
DistanceToStart[m_Start] = 0;
NotEvaluatedNodes.AddElement(0, m_Start);
while (NotEvaluatedNodes.IsEmpty() == false)
{
    Node* currentNode = NotEvaluatedNodes.GetElement();
    NotEvaluatedNodes.DeleteElement();
    if (currentNode == m_End)
    {
        ReconstructPath();
        return true;
    }
    EvaluatedNodes.insert(currentNode);
    ExamineNeighbours(currentNode);
}
return false;
//End Processing

void AStarPath::ExamineNeighbours(Node* current)
for (Node* neighbour : m_SearchRoom->GetNeighbours(current))
{

    if (std::find(EvaluatedNodes.begin(), EvaluatedNodes.end(), neighbour) != EvaluatedNodes.end())
    {
        continue;
    }

    bool InOpenSet = NotEvaluatedNodes.ContainsElement(neighbour);

    float tentative_g_score = DistanceToStart[current] + DistanceBetween(current, neighbour);

    if (InOpenSet == true && tentative_g_score >= DistanceToStart[neighbour])
    {
        continue;
    }

    CameFrom[neighbour] = current;

    DistanceToStart[neighbour] = tentative_g_score;

    float Valuation = tentative_g_score + DistanceBetween(neighbour, m_End);

    if (InOpenSet == false)
    {
        NotEvaluatedNodes.AddElement(Valuation, neighbour);
    }
    else
    {
        NotEvaluatedNodes.UpdatePriority(neighbour, Valuation);
    }
}

//结束检查邻居

double AStarPath::DistanceBetween(Node* a, Node* b)

return sqrt(pow(m_SearchRoom->GetNodeX(a) - m_SearchRoom->GetNodeX(b), 2) 
    + pow(m_SearchRoom->GetNodeY(a) - m_SearchRoom->GetNodeY(b), 2));
//END DistanceBetween

我很抱歉格式错误,但我真的不知道如何处理这里的代码块。

类 AStarPath

私人:

std::unordered_set<Node*> EvaluatedNodes;


Binary_Heap NotEvaluatedNodes;


std::unordered_map<Node*, float> DistanceToStart;


std::unordered_map<Node*, Node*> CameFrom;

std::vector<Node*> m_path;

TileGraph* m_SearchRoom;

//END类AStarPath

无论如何,我已经考虑过自己的问题并改变了一些事情。 首先,我实现了一个二进制堆而不是 std::priority_queue。我在 policyalmanac 上使用了一个页面,但我不允许添加另一个链接,所以我不能真正给你地址。它提高了性能,但正如我一开始所说的那样,它仍然需要很长时间。 其次,我使用了无序容器(如果有两个选项),这样容器就不必在更改后进行排序。对于我的 EvaluatedNodes,我采用了 std::unordered_set,因为据我所知,std::find 是最快的,我将其用于遏制检查。 std::unordered_map 的使用是由于需要具有单独的键和值。 第三,我考虑将我的地图拆分为节点,这些节点代表多个坐标(而不是现在一个节点代表一个坐标),但我不确定如何选择它们。我考虑过在位置设置点,算法根据地图的长度和宽度决定并添加相邻坐标,如果没有特定距离或距离基节点/坐标更远,我只能从以前添加的坐标。为了检查是否有行走能力,我会使用常规的 A*,只有坐标(转换为 A* 节点),它们位于这些大节点中。尽管如此,我不确定我应该在这个寻路的开始和结束时采用哪个坐标。如果我只使用作为大节点一部分的坐标/节点,这可能会减少检查的节点/坐标的数量。(因此只使用节点,其中较大节点的一部分位于上部等级)

对不起,我的英语很糟糕,但希望一切都可以理解。我期待着你的答案,学习新的技术和方法来处理问题,并了解我所犯的数百个愚蠢的错误。 如果任何重要方面不清楚,或者我是否应该添加更多代码/信息,请随时询问。

编辑:Binary_Heap

class Binary_Heap

private:

std::vector<int> Index;
std::vector<int> m_Valuation;
std::vector<Node*> elements;

int NodesChecked;

int m_NumberOfHeapItems;

void TryToMoveElementUp(int i_pos);

void TryToMoveElementDown(int i_pos);

public:

Binary_Heap(int i_numberOfElements);


void AddElement(int Valuation, Node* element);


void DeleteElement();


Node* GetElement();

bool IsEmpty();

bool ContainsElement(Node* i_node);

void UpdatePriority(Node* i_node, float newValuation);


Binary_Heap::Binary_Heap(int i_numberOfElements)

Index.resize(i_numberOfElements);
elements.resize(i_numberOfElements);
m_Valuation.resize(i_numberOfElements);

NodesChecked = 0;

m_NumberOfHeapItems = 0;

void Binary_Heap::AddElement(int 估值,Node* 元素)

++NodesChecked;
++m_NumberOfHeapItems;

Index[m_NumberOfHeapItems] = NodesChecked;

m_Valuation[NodesChecked] = valuation;

elements[NodesChecked] = element;

TryToMoveElementUp(m_NumberOfHeapItems);

void Binary_Heap::DeleteElement()

elements[Index[1]] = nullptr;
m_Valuation[Index[1]] = 0;

Index[1] = Index[m_NumberOfHeapItems];
--m_NumberOfHeapItems;

TryToMoveElementDown(1);

bool Binary_Heap::IsEmpty()

return m_NumberOfHeapItems == 0;

节点* Binary_Heap::GetElement()

return elements[Index[1]];

bool Binary_Heap::ContainsElement(Node* i_element)

return std::find(elements.begin(), elements.end(), i_element) != elements.end();

void Binary_Heap::UpdatePriority(Node* i_node, float newValuation)

if (ContainsElement(i_node) == false)
{
    AddElement(newValuation, i_node);
}
else
{
    int treePosition;

    for (int i = 1; i < Index.size(); i++)
    {
        if (elements[Index[i]] == i_node)
        {
            treePosition = i;

            break;
        }
    }

    //Won't influence each other, since only one of them will change the position
    TryToMoveElementUp(treePosition);
    TryToMoveElementDown(treePosition);
}

void Binary_Heap::TryToMoveElementDown(int i_pos)

int nextPosition = i_pos;

while (true)
{
    int currentPosition = nextPosition;

    if (2 * currentPosition + 1 <= m_NumberOfHeapItems)
    {
        if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition]])
        {
            nextPosition = 2 * currentPosition;
        }
        if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition + 1]])
        {
            nextPosition = 2 * currentPosition + 1;
        }
    }
    else
    {
        if (2 * currentPosition <= m_NumberOfHeapItems)
        {
            if (m_Valuation[Index[currentPosition]] >= m_Valuation[Index[2 * currentPosition]])
            {
                nextPosition = 2 * currentPosition;
            }
        }
    }

    if (currentPosition != nextPosition)
    {
        int tmp = Index[currentPosition];
        Index[currentPosition] = Index[nextPosition];
        Index[nextPosition] = tmp;
    }
    else
    {
        break;
    }
}

void Binary_Heap::TryToMoveElementUp(int i_pos)

int treePosition = i_pos;

while (treePosition != 1)
{
    if (m_Valuation[Index[treePosition]] <= m_Valuation[Index[treePosition / 2]])
    {
        int tmp = Index[treePosition / 2];

        Index[treePosition / 2] = Index[treePosition];

        Index[treePosition] = tmp;

        treePosition = treePosition / 2;
    }
    else
    {
        break;
    }
}

【问题讨论】:

  • 我不确定这是否适合 SO 但是,在具有 20k 个节点的图表上获得 A* 需要 48 分钟?!要么您在非常非常非常 [...] 非常旧的计算机上运行此程序,要么您的实施存在严重问题。即使您每次必须检查某些内容时都要遍历整个节点集,也不会花费 48 分钟!您应该分析您的程序以检查它在哪里花费时间,因为您提供的当前没有人可以帮助您(尤其是您没有提供 Binary_Heap 实现)。
  • 为什么几乎没有人发布他们在构建应用程序时使用了哪些优化?如果您正在计时未优化或“调试构建”,请通过计时发布、优化版本来重新测试。在 SO 上有太多帖子,我们会收到“为什么我的程序很慢”的问题,一旦请求开启优化,速度问题就会消失。
  • 这应该移到代码审查。
  • @ColinBasnett 好的,那么对不起。
  • @ColinBasnett 代码审查有一些非常严格的要求。这将需要进行大量清理,然后才能遇到除了一连串的反对票和搁置之外的任何事情。 JohTa,帮自己一个忙,在转发之前先阅读一下:codereview.stackexchange.com/help/how-to-ask

标签: c++ algorithm performance a-star


【解决方案1】:

这一行引入了严重的低效率,因为它需要在每次迭代中遍历队列中的所有节点。

bool InOpenSet = NotEvaluatedNodes.ContainsElement(neighbour);

尝试使用更高效的数据结构,例如用于 EvaluatedNodes 的 unordered_set。每当您从堆中推送或弹出节点时,相应地修改集合以始终仅包含堆中的节点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    相关资源
    最近更新 更多