实现A*算法

6
所以我正在使用C语言实现A*算法。以下是步骤。
我使用优先队列[使用数组]来存储所有开放的节点。由于我会有重复的距离,也就是说有多个具有相同距离/优先级的节点,因此在将节点插入PQ时,如果插入节点的父节点具有相同的优先级,则仍然交换它们两个,以便我的最新成员保持在顶部(或尽可能高),以便我继续沿着特定的方向前进。另外,在移除时,当我将最上面的元素与最后一个元素交换时,如果交换的最后一个元素与其任一子元素具有相同的元素,则再次将其交换到底部。(我不确定这是否会对任何方面产生影响)。
现在的问题是,假设我有一个100 * 100的矩阵,并且我在移动的2D数组中从(0,20)到(15,20)都有障碍物。现在对于起始位置(2,2)和结束位置(16,20),我得到了一条直线路径,即首先一直向右走,然后向下走到15,然后向右移动一格,就完成了。
但是,如果我将起点设置为(2,2),终点设置为(12,78),即点之间被障碍物分开,路径必须绕过它,则我仍然通过(16,20),并且我的路径在(16,20)之后仍然是直线,但是我的路径到(16,20)是曲折的,即我先向下走一段距离,然后向右走,然后向下走,然后向右走,以此类推,最终到达(16,20)并直接前往目的地。
为什么这条路径的前半部分会是曲折的?我该怎么做才能确保我的路径是直线的,就像当我的目的地是(16,20)而不是(12,78)时一样。
谢谢。
void findPath(array[ROW][COLUMN],sourceX,sourceY,destX,destY) {
  PQ pq[SIZE];
  int x,y;

  insert(pq,sourceX,sourceY);

  while(!empty(pq)) {
    remove(pq);
    if(removedIsDestination)
        break;                  //Path Found
    insertAdjacent(pq,x,y,destX,destY);
  }
}

void insert(PQ pq[SIZE],element){
  ++sizeOfPQ;
  PQ[sizeOfPQ]==element
  int i=sizeOfPQ;
  while(i>0){
    if(pq[i].priority <= pq[(i-1)/2].priority){
      swapWithParent
      i=(i-1)/2;
    }
    else
      break;
  }
}

3
请展示你代码中相关的部分。代码胜过千言万语。 - Femaref
@Femaref 已经这样做了,请告诉我是否有帮助。 - user1868357
这可能与你的得分有关 -- 你没有展示出它。 - Hogan
@Hogan 哦,是的,我刚刚得到了从源头到目的地的距离总和加上绝对距离。 - user1868357
@Hogan 为什么评分会决定这种行为呢?无论我使用什么评分方法,我的 PQ 在峰值处都是最小的,如果我有平局,我已经说过了,在那种情况下我会使用什么。 - user1868357
@Hogan,我不确定我是否理解了你的意思?假设我的当前位置是(x,y),目的地是(dX,dY),那么你建议我的评分是什么?源距离+___目前我正在执行absolute(x-dx) + absolute(y-dy)。 - user1868357
1个回答

3

您应该更改评分部分。目前,您计算的是绝对距离。相反,应计算最小移动距离。如果将每次移动视为一次,则如果您在(x,y)处并要前往(dX,dY),那将是

distance moved + (max(x,dX) - min(x,dx) + max(y,dY) - min(y,dY))

较小的数值被认为是更高的分数。

这个启发式算法是对没有障碍物的情况下需要多少步骤的猜测。


启发式算法的好处在于你可以通过改变它来得到你想要的结果,比如,如果你希望沿着直线移动,那么你可以进行以下更改:

= distance moved + (max(x,dX) - min(x,dx) + max(y,dY) - min(y,dY)) 
     + (1 if this is a turn from the last move)

这将使你“发现”解决方案,这些方案往往朝着相同的方向发展。
如果您想尽可能少地强制转弯:
= distance moved + (max(x,dX) - min(x,dx) + max(y,dY) - min(y,dY)) 
     + (1 times the number of turns made)

这就是A*算法的优点 - 启发式搜索会提供信息 - 你总能找到一个解决方案,但如果有多个解决方案,你可以影响其搜索顺序 - 这使得它非常适合模拟AI行为。第一种方法对于转向移动的优先级较低,而第二种方法对拐弯更多的路径的优先级较低。在某些情况下(例如,第一个转向),值将是相同的,但是整体来看,第二种方式会选择尽可能少转向的路径,而第一种方式可能不会。是的,1表示上一次移动是转向。是的,确实如此。你不希望首先查看具有转向的选项。这样会使它们的优先级降低,您的算法将调查具有更高优先级的其他选项 - 这正是您想要的。不,我不理解这个问题 - 我认为它在这个背景下没有意义 - 所有移动都必须与前一个单元格接壤,不允许对角线移动。很难没有看到您的算法的详细信息,但以下可能起作用:红色为块,绿色是我期望第一种方法执行的操作,它局部地尝试找到最少的转向。蓝色是最少的转向解决方案。请注意,红色区域互相之间的距离有多远以及您的算法细节如何影响此操作是否可行。就像上面那样 - 额外的转向只会在启发式函数中花费1个单位。因此,如果要确保这将起作用,请更改启发式函数:
= distance moved + (max(x,dX) - min(x,dx) + max(y,dY) - min(y,dY)) 
     + (25 times the number of turns made)

当25大于通过绿色路径第二个转弯的距离时,将搜索蓝色路径。(因此在第二个转弯后将搜索蓝色路径。)


我猜我在那里使用了错误的绝对值,我目前正在做的是 absolute(x-dx) + absolute(y-dy),我想这与您提出的相同。 - user1868357
好的...我会修改我的回答...一秒钟。 - Hogan
我认为我在某种程度上理解了它,但是请帮我解决这个问题。当我的目的地是(16,20)时,我会沿着直线路径前进。但是,当我的目的地位于障碍物的对面,并且起点和目的地之间的路径必须绕过障碍物时,即使它经过相同的点,我的路径也不是直线路径,直到(16,20)。为什么会有这种变化? - user1868357
我猜,我也有一个可能的原因,比如说我一直朝着正确的方向直线移动,直到遇到障碍物,然后我开始向下移动,但在某个点上,我的某个单元格得分比之前的单元格更高,那个单元格是沿着另一条直线路径移动的,现在沿着这条路径(在这种情况下是向下),再次发生了类似的事情,然后我不得不选择另一条即向右的路径,这种情况持续下去吗? 此外,我试图减小数组的大小,但在这种情况下它可以正常工作。 - user1868357
是的,完全正确。一旦您移动障碍物(可能是从边缘移动),这将更改搜索方式,导致列表中项目的顺序发生变化,从而引起抖动 - 调试将使其清晰明了。 - Hogan
显示剩余12条评论

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