程序在调试器中逐步正确运行,但不正常

时间:2021-04-21 14:03:10

标签: c# visual-studio-debugging .net-4.7.2

它是 .NET Framework 4.7.2 中的应用程序,用 C# 编写。它通过节点网格并通过 BFS 算法找到从节点 A 到 B 的最短路径。在算法的每次迭代中,在确认当前节点不是目标节点后,它通过将相邻节点放入队列来继续检查相邻节点,然后进入下一次迭代,在那里它使下一个节点出列以进行检查。当它想将相邻节点添加到队列中时,它首先检查该节点是否已被访问(每个节点中都有一个字段 isVisited : bool ),如果没有,则将其入队。 队列为先进先出

所以这个问题是一个相当奇怪的问题。我让程序在调试模式下运行一段时间,给它足够的时间卡住(通常非常快)并暂停它以查看 BFS 算法在哪里结束。我发现它跟踪的节点(路径)的轨迹是数百个,当 2 个输入节点非常接近时,相隔 2 个空格。绕过网格中的障碍物最多只能占 30 条轨迹长度,在最极端的情况下可能是 100 条,但绝不会超过数百条。所以很明显出了问题。我检查了根节点,我发现它的一个邻居甚至没有被访问过,它由于某种原因没有排队......

然后当根是导致整个事情卡住的节点时,我在搜索的开始处设置了一个断点。如果我只是从那里继续,搜索将立即找到节点(相对而言)。如果我只是在每次搜索调用之前放置一个断点,然后让它继续下一次搜索调用以获取一对新节点,这也有效......如果我这样做,它永远不会卡住并且非常快,但是如果我让它自己运行它会在几次搜索中卡住。 所以我的问题,最后,是...到底是什么?这到底是什么原因造成的?

代码如下:

public class Grid {
    public GraphNode[,] grid;
    
    public Grid(int resolution){
        grid = new GraphNode[resolution,resolution];
        foreach (GraphNode g in grid) {
            SetNeighbours(g);
        }
    }
    
    public void SetNeighbours(GraphNode node)
    {
        int x = node.x;
        int y = node.y;
        if (y != 0)
            grid[x, y].N = grid[x, y - 1];
        else
            grid[x, y].N = null;
        if (x != _resolution - 1)
            grid[x, y].E = grid[x + 1, y];
        else
            grid[x, y].E = null;
        if (y != _resolution - 1)
            grid[x, y].S = grid[x, y + 1];
        else
            grid[x, y].S = null;
        if (x != 0)
            grid[x, y].W = grid[x - 1, y];
        else
            grid[x, y].W = null;
    }
}

public class GraphNode {
    public long id;
    public int x, y;
    public GraphNode N, E, S, W;
    public bool isVisited;
    public List<bool> isPath = new List<bool>();
}

public class GraphPathSegment
{
    public GraphNode start;
    public GraphNode end;
    public int run;
}

public class BFS
{
    private Queue _searchQueue;
    private GraphNode _root;
    private Grid grid;

    private long id;

    public BFS(GraphNode rootNode, Grid context)
    {
        _searchQueue = new Queue();
        _visitedNodes = new List<Tuple<long, long>>();
        _root = rootNode;
        grid = context;
    }
        
    public List<GraphPathSegment> Search(long id, int run)
    {
        this.id = id;
        GraphNode _node = grid.grid[_root.x, _root.y];    // starting node
        _node.isVisited = true;
        _visitedNodes.Add(new Tuple<long, long>(_node.x, _node.y));
        List<GraphNode> _trace = new List<GraphNode>(); //  path trace
        _searchQueue.Enqueue(new Tuple<GraphNode, List<GraphNode>>(_node, _trace));
        bool wrongNode;

        while (_searchQueue.Count != 0)
        {               
            Tuple<GraphNode, List<GraphNode>> _current = (Tuple<GraphNode, List<GraphNode>>)_searchQueue.Dequeue();
            _node = _current.Item1;
            _trace = _current.Item2;
            wrongNode = false;

            if (_node.isNode)   // is the node an entity?
            {
                if (_node.Data.ID == id)    // is it the target node?
                {
                    _trace.Add(_node);
                    CleanUp(run, _trace);
                    List<GraphPathSegment> retSegments = new List<GraphPathSegment>();
                    for(int i = 1; i < _trace.Count; i++)
                    {
                        retSegments.Add(new GraphPathSegment(_trace[i - 1], _trace[i], run));
                    }
                    return retSegments;
                }
                if(!_root.Equals(_node))    // is not root
                    wrongNode = true;
            }
            if(!wrongNode)  //node is blank or path
            {
                List<GraphNode> newTrace = new List<GraphNode>(_trace);
                newTrace.Add(_node);
            //is there a node above |    is not visited     |   is not path in current run
                if (_node.N != null && !_node.N.isVisited && !_node.N.isPath[run])
                {
                    _node.N.isVisited = true;
                    _searchQueue.Enqueue(new Tuple<GraphNode, List<GraphNode>>(_node.N, newTrace)); // check next
                }
            //is there a node right |    is not visited     |   is not path in current run
                if (_node.E != null && !_node.E.isVisited && !_node.E.isPath[run])
                {
                    _node.E.isVisited = true;
                    _searchQueue.Enqueue(new Tuple<GraphNode, List<GraphNode>>(_node.E, newTrace));
                }
            //is there a node below |    is not visited     |   is not path in current run
                if (_node.S != null && !_node.S.isVisited && !_node.S.isPath[run])
                {
                    _node.S.isVisited = true;
                    _searchQueue.Enqueue(new Tuple<GraphNode, List<GraphNode>>(_node.S, newTrace));
                }
            //is there a node left  |    is not visited     |   is not path in current run
                if (_node.W != null && !_node.W.isVisited && !_node.W.isPath[run])
                {
                    _node.W.isVisited = true;
                    _searchQueue.Enqueue(new Tuple<GraphNode, List<GraphNode>>(_node.W, newTrace));
                }
            }
        }
        CleanUp(run);  // this sets all the nodes in the grid to unvisited
        return null;
    }
}

    private void CleanUp(int run = 0, List<GraphNode> trace = null)
    {
        foreach(GraphNode n in grid.grid)
        {
            n.isVisited = false;
        }
        if (trace == null) trace = new List<GraphNode>();

        for (int i = 1; i < trace.Count - 1; i++)
        {
            grid.grid[trace[i].x, trace[i].y].isPath[run] = true;
        }
    }

    

并且可以在初始化图后从main调用调用方法:

.
.
.
        BFS bfs = new BFS(grid.grid[N1.x, N1.y], grid); 
//param1 sets the root node, param2 gives context to the algorithm
        List<GraphPathSegment> pathSegments = bfs.Search(N2.Data.ID, run); 
// run is used when there can be no more nonoverlapping paths in the current run => go to next run, not relevant for this, just so there's no confusion
.
.
.

重要

此版本的应用程序在使用大型列表和网格的 GraphNode 对象矩阵的任何地方都使用哈希表。该应用还有另一个版本,它使用列表和节点列表矩阵,虽然速度慢得惊人。

0 个答案:

没有答案