【发布时间】: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