我对8-Puzzle的A*搜索有什么问题?
本文关键字:什么 问题 搜索 8-Puzzle 我对 | 更新日期: 2023-09-27 18:14:07
我正试图使用这些启发式搜索来解决8-Puzzle:- h1:错位瓷砖的数量- h2:曼哈顿总距离- h3:以上内容之和
移动的贴图被称为0。
我的目标是解决这些集合:
4 1 2
5 8 3
7 0 6
和
8 6 7
2 5 4
3 0 1
我遇到的问题是,用我目前的A*实现,它能够解决第一个问题,但不能解决第二个问题。
所以请帮助我了解我的A*代码有什么问题:
int[,] current =从控制台输入字符串(412583706)并转换为2D int表示谜题。与correct相同,其中0在右下角。
var openList = new List<int[,]> { current };
var closedList = new List<int[,]>();
while (openList.Count > 0)
{
steps++;
current = GetBestNodeFromList(correct, dimensions, openList, useHeuristic);
// "GetBestNodeFromList()" finds the cheapest node in the openList.
// cheapest node: lowest value of h3.
openList.Remove(current);
h1 = getHeuristic1b(current, correct, dimensions);
h2 = getHeuristic2b(current, correct, dimensions);
h3 = h1 + h2;
if (h1 == 0 && h2 == 0) { break; }
openList = Puzzle_PossibleNext(current, closedList);
// the method "PossibleNext()" finds possible next moves from the current
// position. if the next move exists in the closedList, it is discarded.
// Drawing the puzzle and showing heuristics.
DrawCurrentState(h1, h2, h3, current, steps);
// adding last visited position to the closedlist.
closedList.Add(current);
}
第一个问题用7个步骤解决。根据我测试的另一个程序,下一个问题可以用32步解决。
我的程序与另一个程序的不同之处在于,前4步是相同的,然后另一个程序选择了不同的路线,而我的程序只是一直走下去,找不到解决方案。看起来我的程序确实选择了最便宜的节点,所以这就是为什么我不明白哪里出错了。
这是我第一次使用寻径算法,所以我想解决它。我已经有这个问题3天了,我觉得我已经尝试了很多解决方案,但没有一个工作T_T
致以最亲切的问候。
——编辑额外的代码:
// Put heuristic value from all in list, then return list item with lowest h-value.
static int[,] GetBestNodeFromList(int[,] correct, int d, List<int[,]> list, string useHeuristic)
{
int[,] n = new int[d,d];
if (list.Count > 0)
{
List<Int32> heuristicsValueList = new List<Int32>();
for (int i = 0; i < list.Count; i++)
{
if (useHeuristic == "h1") { heuristicsValueList.Add(getHeuristic1b(list[i], correct, d)); }
else if (useHeuristic == "h2") { heuristicsValueList.Add(getHeuristic2b(list[i], correct, d)); }
else { heuristicsValueList.Add(getHeuristic3(list[i], correct, d)); }
}
n = list[heuristicsValueList.IndexOf(heuristicsValueList.Min())];
}
return n;
}
--------- 编辑2,我稍微修改了一下代码,但还是没有成功谜题设置/节点及其启发式都在PuzzleNode对象中。
//返回当前节点下一个可能移动的列表。//不包括在closedNodeList中找到的移动
static List<PuzzleNode> Puzzle_GenerateNextNodes(PuzzleNode node, List<PuzzleNode> closedNodeList)
{
List<PuzzleNode> nextList = new List<PuzzleNode>();
Point isNow = new Point(0, 0);
// 1) Find where [0] is.
int dimensions = (int)Math.Sqrt((double)node.n.Length);
for (int x = 0; x < dimensions; x++) {
for (int y = 0; y < dimensions; y++) { if (node.n[x, y] == 0) { isNow.X = y; isNow.Y = x; break; } }
}
// 2) Check possible moves.
bool moveUp = false, moveDown = false, moveLeft = false, moveRight = false;
if (isNow.X == 0)
{
moveRight = true;
if (isNow.Y == 0) { moveDown = true; }
else if (isNow.Y == 1) { moveUp = true; moveDown = true; }
else if (isNow.Y == 2) { moveUp = true; }
}
else if (isNow.X == 1)
{
moveRight = true;
moveLeft = true;
if (isNow.Y == 0) { moveDown = true; }
else if (isNow.Y == 1) { moveUp = true; moveDown = true; }
else if (isNow.Y == 2) { moveUp = true; }
}
else if (isNow.X == 2)
{
moveLeft = true;
if (isNow.Y == 0) { moveDown = true; }
else if (isNow.Y == 1) { moveUp = true; moveDown = true; }
else if (isNow.Y == 2) { moveUp = true; }
}
// 3) Create list of possible moves.
// Add moved puzzle node to list over next moves
if (moveRight)
{
int[,] right = new int[dimensions, dimensions];
Array.Copy(node.n, right, node.n.Length);
PuzzleNode tmp = new PuzzleNode( PuzzleMoveRight(right, isNow.X, isNow.Y) );
if (!ListHasThisValue(tmp.n, closedNodeList, dimensions)) { nextList.Add(tmp); }
}
// moveleft, up, down, same structure as moveRight
if (moveLeft)
{
..
}
if (moveUp)
{
..
}
if (moveDown)
{
..
}
return nextList;
}
----------- 编辑3 ----------------
顺便问一下,我对A*的不同步骤的实现是否理解正确?目前,我的程序的A*搜索是这样做的:
- 创建初始列表OPEN和CLOSED,将起始节点添加到OPEN
- 启动循环,从OPEN中删除最便宜的节点,将其添加到CLOSED中*最便宜的节点由它的曼哈顿距离值决定。
- 使用节点查找邻居/子/下一步移动,添加这些
- 查看继任者列表,检查是否包含目标状态;否则添加到OPEN列表
- 重复2-4,探索列表中的节点。
当我用Q1尝试这些步骤时,我得到了7步的解决方案,这是正确的。这也是用手找到的。但是对于Q2,它会一直进行下去,直到OPEN列表为空,并且没有其他可探索的内容。那么我错过了什么呢?
我能够通过蛮力快速找到解决方案。如果你使用的是完全愚蠢的启发式算法,那么A*应该恢复为蛮力。你如何将你的状态与封闭状态列表进行比较?
var set = new int[,] {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 0 }
};
var clone = (int[,])set.Clone();
var foo = clone == set; // foo is false
var bar = clone.Equals(set); // bar is false
var closedStates = new List<int[,]>();
closedStates.Contains(state); // wrong - contains is using Equals
closedStates.Any(cs => AreEqual(cs, state)); // correct
static bool AreEqual(int[,] stateA, int[,] stateB) {
for (var x = 0; x < DIMENSIONS; x++) {
for (var y = 0; y < DIMENSIONS; y++) {
if (stateA[x, y] != stateB[x, y]) {
return false;
}
}
}
return true;
}
我要感谢所有帮助我解决这个问题的人。
我今天找到了问题。我不知道该怎么说,这太蠢了。问题不在于代码或A*实现(我当前的代码可能与之前发布的不同)。
问题在于过度依赖所使用的启发式方法。似乎对于Q1,启发式h1, h2和h3(h1和h2具有相同的代价)都可以找到解。然而对于Q2, h2和h3都不能找到解的路径,但是h1可以。在我的程序中,我一直使用h3作为调试和测试的默认启发式,这也是我失败的原因。
所以我们应该学到的是知道你在做什么。即使是最简单的启发式方法,我也无法意识到其中的区别。
我现在能够解Q1和Q2。再次感谢大家。作为一名程序员,我确实从中吸取了教训。
我希望我能给你们更多的功劳,因为你们花了时间来帮忙。