曼哈顿距离过于高估,让我感到很疯狂。

24
我正在使用曼哈顿距离来实现A星算法以解决8数码难题(使用C语言编写)。它似乎工作得非常好,并通过了许多单元测试,但在一个案例中未能找到最短路径(它找到了27步而不是25步)。
当我将启发式函数更改为汉明距离时,它可以在25步内找到答案。 当我让曼哈顿距离函数返回实际成本的一半时,也可以在25步内找到答案。
这就是为什么我认为问题出在曼哈顿距离函数中,它高估了成本(因此不可接受)。我想也许C程序中还有其他问题,所以我编写了一个小的Python脚本来测试和验证仅曼哈顿距离函数的输出,它们都产生了完全相同的结果。
我真的很困惑,因为启发式函数似乎是唯一的失败点,同时它似乎是正确的。

8-puzzle start goal

你可以尝试这个求解器,并将拼图序列按照"2,6,1,0,7,8,3,5,4"的顺序排列。 选择算法曼哈顿距离,它可以在25步内找到答案。 现在改为曼哈顿距离+线性冲突,它需要27步才能找到答案。

但我的曼哈顿距离(不包括线性冲突)只需要27步。

这是我的通用算法:

manhattan_distance = 0
iterate over all tiles
if the tile is not the blank tile:
find the coordinates of this tile on the goal board
manhattan_distance += abs(x - goal_x) + abs(y - goal_y)

我认为如果某个重要部分出现了严重问题,它不可能通过之前的25项以上的测试,所以这可能是某种边缘情况。

以下是C语言中的曼哈顿距离函数:

int ManhattanDistance(Puzzle p, State b){
   State goal = getFinalState(p);
   int size = getSize(b);
   int distance = 0;
   if (getSize(goal) == size){ // both states are the same size
      int i, j;
      for(i=0; i<size; i++){
         for(j=0; j<size; j++){ // iterate over all tiles
            int a = getStateValue(b, i, j); // what is the number on this tile?
            if (a != 'B'){ // if it's not the blank tile
               int final_cordinates[2];
               getTileCoords(goal, a, final_cordinates); // find the coordinates on the other board
               int final_i = final_cordinates[0];
               int final_j = final_cordinates[1];
               distance +=  abs(i - final_i) + abs(j - final_j);
            }
         }
      }
   }
   return distance;
}

请帮我。 编辑:如评论中所讨论的,打开节点的代码可以在这里找到。

1
你的曼哈顿距离函数看起来很好……[至少从我读代码的角度来看是这样],你确定它是问题所在吗?也许你的A*算法实现没有在寻找更短路径时重新打开已关闭的节点?这可能解释为什么这个错误不总是发生。 - amit
1
@amit 我已经检查了那些if块,由于从一个状态到另一个状态的移动成本始终为1(而不是例如城市之间道路的长度),因此程序永远不会到达那些if块,所以它永远不需要重新打开已关闭的节点,因为随着你走得越远,每一步的成本都会增加1,所以不可能找到一个之前见过且成本比当前移动更低的节点。 - Babak
快速查看了你的A*代码(AStar.java),似乎你正在使用f函数(距离加成本)来确定是否找到了通往给定节点的更好路径,而你应该使用g函数(路径成本)来做这件事。尝试更改一下,看看是否能得到更好的结果。 - Sander De Dycker
无论如何,如果您发布您的A*代码,很可能会有所帮助。因为一切都指向那个方向。 - Sander De Dycker
过去的链接已经失效了 :( - EaterOfCode
显示剩余14条评论
1个回答

6
问题似乎不是在你的启发式函数中,而是在算法本身。根据你对问题的描述和事实上它只出现在某些特定情况下,我认为这与重新打开一个已经关闭的顶点有关,一旦你找到了更好的路径。
阅读你提供的代码[在注释中]时,我想我理解了问题所在,它在第20行:
if(getG(current) + 1 < getG(children[i])){

这是错误的!你正在检查是否满足g(current) + 1 < g(children[i]),实际上你想要检查的是:f(current) + 1 + h(children[i]) < g(children[i]),因为你想要用children[i]的启发式函数来检查这个值,而不是current的启发式函数!请注意,这与设置f(children[i]) = min{f(children[i]),f(current)+1}相同,然后添加h(children[i])以获取g值。

1
感谢您检查代码,阿米特。我完全不明白你的逻辑。f(current) = g(current) + h(current)为什么你要将h(current)和h(children[i])相加,并期望它小于g(children[i])呢?正如您所知,每个"h"值都代表从该节点到目标的近似成本。此外,请查看Wikipedia页面中的算法,该部分属于该代码中的"tentative_g_score",其等于g_score[x] + dist_between(x,y),我不明白"h"在这里的作用是什么? - Babak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接