【问题标题】:A* Algorithm System.StackOverflowExceptionA* 算法 System.StackOverflowException
【发布时间】:2012-04-09 19:57:58
【问题描述】:
public List<Location2D> Path(Location2D start, Location2D goal)
{
    openset = new List<NodeInfo>(); // The set of tentative nodes to be evaluated, initially containing the start node.
    closedset = new List<NodeInfo>(); // The set of nodes already evaluated.
    path = new List<Location2D>(); // The path result.
    came_from = new Dictionary<Location2D, Location2D>();

    NodeInfo Start = new NodeInfo();
    Start.SetLoction(start.X, start.Y);
    Start.H = GetHValue(start, goal);
    openset.Add(Start);

    while (openset.Count > 0) { // while openset is not empty
        NodeInfo current = CheckBestNode(); //the node in openset having the lowest f_score[] value

        if (current.Location.Equals(goal)) {
            ReconstructPathRecursive(current.Location);
            return path;
        }

        for (int i = 0; i < 8; i++) { // neighbor nodes.
            NodeInfo neighbor = new NodeInfo();
            neighbor.SetLoction((ushort)(current.Location.X + ArrayX[i]), (ushort)(current.Location.Y + ArrayY[i]));

            bool tentative_is_better = false;

            if (closedset.Contains(neighbor))
                continue;
            if (!map.Cells[neighbor.Location.X, neighbor.Location.Y].Walkable) { closedset.Add(neighbor); continue; }

            Double tentative_g_score = current.G + DistanceBetween(current.Location, neighbor.Location);

            if (!openset.Contains(neighbor)) {
                openset.Add(neighbor);
                neighbor.H = GetHValue(neighbor.Location, goal);
                tentative_is_better = true;
            } else if (tentative_g_score < neighbor.G) {
                tentative_is_better = true;
            } else {
                tentative_is_better = false;
            }

            if (tentative_is_better) {
                came_from[neighbor.Location] = current.Location;
                neighbor.G = tentative_g_score;
            }
        }
    }
    return null;
}

private void ReconstructPathRecursive(Location2D current_node)
{
    Location2D temp;
    if (came_from.TryGetValue(current_node, out temp)) {
        path.Add(temp);
        ReconstructPathRecursive(temp);
    } else {
        path.Add(current_node);
    }
}

所以我正在实施 A* 算法,当它找到目标时,它会转到 ReconstructPathRecursive 然后应用程序崩溃并抛出此异常An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

This thread is stopped with only external code frames on the call stack. External code frames are typically from framework code but can also include other optimized modules which are loaded in the target process.

知道怎么了!

【问题讨论】:

  • 某处可能存在无限递归,请检查您的基本情况。
  • came_from.TryGetValue(current_node, out temp)) 总是返回 true 吗?如果是这样,你肯定有堆栈溢出。检查路径中是否有循环。
  • 无限递归的原因可能是图中的循环;您可能可以通过使用调试器单步执行代码来非常快速地识别问题。
  • ReconstructPathRecursive 方法看起来相当简单。添加一些调试语句,例如打印递归数,甚至更好 - 打印 ReconstructPathRecursive 走过的路径。从一个小测试用例开始,你会发现递归循环正在吃掉你的堆栈。

标签: c# a-star


【解决方案1】:

它不一定是递归的,因为它是尾递归的。所以你可以这样重写它:

private void ReconstructPathIterative(Location2D current_node)
{
    Location2D temp;
    while (came_from.TryGetValue(current_node, out temp))
    {
        path.Add(temp);
        current_node = temp;
    }
    path.Add(current_node);
}

这仅在路径太长而无法放入堆栈时才有帮助,如果它是无限的,则无济于事。

【讨论】:

  • 第一次它需要大量的时间和 CPU 使用率然后它会中断并抛出相同的异常路径里面有大约 500k 个元素!
  • @MixedCoder 这段代码不可能自己抛出 StackOverflowException 异常,因为它只使用固定的少量堆栈空间。它应该处理 500k 个节点没问题。 path.Add 坏了吗?
  • 这就像一个无限循环不断添加到 path ,这是一个图片显示错误i44.tinypic.com/20zd66f.jpg
  • @MixedCoder 这是一个非常不同的错误 - 67m 与 500k 完全不同。确实,看起来路径本身已损坏并循环。可能是封闭集维护/测试中的错误。或者也许是父母更新。
  • 谢谢你的时间,我已经修好了:)
【解决方案2】:

我通过在 NodeInfo 类中添加 NodeInfo Parent {get; set; } 作为文件来修复它,当暂定更好时,我添加了新的 List&lt;NodeInfo&gt;,称为 Nodes:-

if (tentative_is_better) {
                        neighbor.Parent = current;
                        nodes.Add(neighbor);
                        neighbor.G = tentative_g_score;
                    }

然后当它找到目标时:-

if (current.Location.Equals(goal)){
                    ReconstructPath(current);
                    path.Reverse();
                    return path;
                }

ReconstructPath:-

private void ReconstructPath(NodeInfo current) {
            if (current.Parent == null) return;
            path.Add(current.Parent.Location);
            ReconstructPath(current.Parent);
        }

现在可以正常使用了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多