【问题标题】:What is wrong with my A* search for 8-Puzzle?我对 8-Puzzle 的 A* 搜索有什么问题?
【发布时间】:2014-09-12 19:00:15
【问题描述】:

我正在尝试使用带有这些启发式的 A* 搜索来解决 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。 正确也是一样,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* 搜索是这样的:

  1. 创建初始列表 OPEN 和 CLOSED,将起始节点添加到 OPEN
  2. 开始循环,从 OPEN 中删除最便宜的节点,将其添加到 CLOSED *最便宜的节点由其曼哈顿距离值决定。
  3. 使用节点查找邻居/孩子/下一步移动,添加这些 到 SUCCESSOR 列表。
  4. 探索 SUCCESSOR 列表,检查其中是否包含目标状态, 否则添加到 OPEN 列表
  5. 重复 2-4,探索列表中的节点。

当我在 Q1 中尝试这些步骤时,我分 7 步得到了解决方案,这是正确的。这也是手工发现的。 但是在 Q2 中,它一直持续到 OPEN 列表为空并且没有其他可探索的内容。 那我错过了什么?

【问题讨论】:

  • 你有没有在第四步查看你的openList?为什么你的算法选择了“错误”的步骤?你的启发式在这一点上说了什么?
  • @MarcelB 根据数字(启发式)它确实选择了最便宜的路线,但与其他程序(使用略有不同的启发式)相比它最终是错误的
  • 你能把GetBestNodeFromList(…)的代码贴出来吗?
  • @MarcelB 已添加到开幕帖。
  • 您还可以包含“Puzzle_PossibleNext”的代码吗?在最坏的情况下(完全愚蠢的启发式),A* 应该恢复为蛮力。如果你永远被困住,我的钱就在你身上,你正在重新访问已经关闭的状态。

标签: c# a-star heuristics sliding-tile-puzzle


【解决方案1】:

我能够通过蛮力快速找到解决方案。如果您使用完全愚蠢的启发式方法,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;
}

【讨论】:

  • 我的状态比较方法和你一样,但是程序仍然找不到Q2的解决方案。另外,我在之前链接的 wikipedia 文章中读到,解决 8 谜题的最大步骤数是 31 步。使用 A* 之类的算法时,这也算吗?或者如果需要,它可以走得更远吗?这令人沮丧,我不知道我的代码有什么问题>__>顺便说一句,我正在使用曼哈顿启发式算法。
  • @Mads M - 您的代码中似乎有一些小错误;在没有完整来源的情况下悬停,很难说出在哪里。例如,你在 git 上的某个地方有它吗?
  • 您可以从该驱动器链接中获取源代码link 感谢您的帮助。我只是无法理解问题是什么,被这个问题困扰了好几天..
【解决方案2】:

我要感谢所有帮助我解决这个问题的人。

我今天能够找到问题所在。 我不知道该怎么说,真的很蠢。 问题不在于代码或 A* 实现(我当前的代码可能与之前发布的不同)。

问题在于过度依赖所使用的启发式方法。 似乎对于 Q1,启发式 h1、h2 和 h3(h1 和 h2 具有相同的成本)都可以找到解决方案。 但是对于 Q2,h2 和 h3 都无法找到解决方案的路径,但 h1 可以。 在我的程序中,我一直使用 h3 作为调试和测试的默认启发式,这也是我的失败。

因此,应该吸取的教训是了解您正在使用什么。 即使是最简单的启发式方法,我也无法意识到差异。

我现在能够解决 Q1 和 Q2。 再次感谢大家。 作为一名程序员,我确实从中吸取了教训。

不过,我希望我能给大家更多的表扬,因为他们花时间提供帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-17
    相关资源
    最近更新 更多