我在尝试进行广度优先搜索(BFS),在搜索状态空间时动态创建图的节点。我按照书上的方法做了,但它不起作用,前沿队列一直在运行,而已探索集合始终停留在初始值。请帮帮我,我已经卡在这里4天了。还是一个初学者程序员。
问题类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Test{ internal class Problem { public State start { get; set; } public State end { get; set; } public string[] action = { "UP", "LEFT", "DOWN", "RIGHT" }; public Problem(State x, State y) { start = x; end = y; } public State Result(State s , string a) { State up; if (a == "UP" && s.X - 1 >= 0) { up = new State(s.X - 1, s.Y); } else if (a == "LEFT" && s.Y - 1 >= 0) { up = new State(s.X, s.Y-1); } else if (a == "DOWN" && s.X + 1 < 5) { up = new State(s.X, s.Y+1); } else if( a == "RIGHT" && s.Y + 1 < 11) { up = new State(s.X + 1, s.Y); } return up; } public bool GoalTest(Node<State> goal) { if (goal.Data.Equals(end)) { return true; } return false; } }}
搜索类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Test{ internal class Search { public void BFS(Problem p) { Queue<Node<State>> frontier = new Queue<Node<State>>(); HashSet<string> explored = new HashSet<string>(); List<Node<State>> nNodes = new List<Node<State>>(); Node<State> root = new Node<State>() {Data = p.start,}; frontier.Enqueue(root); while(frontier.Count > 0) { Node<State> next = frontier.Dequeue(); if(!explored.Add(next.Data.Data)) continue; next.Children = new List<Node<State>>(); foreach(string action in p.action) { next.Children.Add(new Node<State>() { Data = p.Result(next.Data, action), Parent = next }); } for(int i = 0; i < next.Children.Count; i++) { if (p.GoalTest(next.Children[i]) == true) { //Solution(next.Children[i]); break; } frontier.Enqueue(next.Children[i]); } } } public void Solution(Node<State> n) { while(n.Parent != null) { Console.WriteLine(n.Parent.Data); } } }}
节点类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Test{ internal class Node<T> { public T Data { get; set; } public Node<T>? Parent { get; set; } public List<Node<T>> Children { get; set; } }}
状态类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Test{ internal class State { public int X { get; set; } public int Y { get; set; } public string Data { get; } public State(int x, int y) { X = x; Y = y; Data = $"{X}-{Y}"; } }}
主程序
using Test;State start = new State(0, 1);State end = new State(3, 7);Problem p = new Problem(start, end);Search s = new Search();s.BFS(p);
这实际上是我的作业,所以我命名为test。
我正在尝试实现这里的伪代码:(第82页)https://zoo.cs.yale.edu/classes/cs470/materials/aima2010.pdf
更新
现在它不会卡住了,但循环运行后它不会在控制台输入任何内容,它应该通过“Parent属性”获取目标节点和起始节点之间的链接。
回答:
你从未检查节点是否已经被探索过,因此你可能会进入无限循环:
explored.Add(next.Data.Data);
应该改为
if(!explored.Add(next.Data.Data)) continue;
但代码中可能还有其他问题。我强烈推荐阅读Eric Lippert的文章如何调试小程序,因为像这样的问题应该很容易通过调试器自己找到并修复。学习如何使用调试器是一项非常宝贵的技能,会使故障排除变得更加容易。
我还建议从你的程序中删除所有字符串。相反,你应该创建一个表示坐标的类型,即一个Point或vector2Int。我建议将其制作成一个只读结构,并添加ToString
重载,IEquality<Point>
实现,添加减法、相等性等运算符。这种类型可以用于表示状态,也可以用于表示偏移量。这种类型可以重复用于各种不同的项目中。