这是我从网站这里一个code我想知道这个实现的*是否正确。我见过他,并与维基百科页面进行比较,似乎有效。为什么我想问的原因是因为在网站上说,它仍然有这个code中的错误,我试图找到它,但不能狼狈不堪。我希望如此,它需要的始发地和目的地作为输入参数,但改变它
This is a code that I got from the site here and I'd like to know whether this implementation of A* is correct. I have looked at it and compare it with the wikipedia page and it seems valid.. The reason why I ask is because in the site it says there is still a bug in this code, I tried finding it but can't find any. I want to change it though so that it takes an origin and destination as input parameter
<?php
class AStarSolver
{
function solve(&$s)
{
include_once('PQueue.class.php');
$o = new PQueue();
$l = array();
$c = array();
$p = array();
$a = $s->getStartIndex();
$z = $s->getGoalIndex();
$d = $s->goalDistance($a);
$n0 = array('g'=>0, 'h'=>$d, 'i'=>$a, 'p'=>NULL, 'f'=>$d);
$o->push($n0, -$d);
$l[$a] = TRUE;
while (! $o->isEmpty())
{
$n = $o->pop();
if ($n['i'] == $z)
{
while ($n)
{
$p[] = $n['i'];
$n = $n['p'];
}
break;
}
foreach ($s->getNeighbors($n['i']) as $j => $w)
{
if ((isset($l[$j]) || isset($c[$j])) && isset($m) && $m['g'] <= $n['g']+$w)
continue;
$d = $s->goalDistance($j);
$m = array('g'=>$n['g']+$w, 'h'=>$d, 'i'=>$j, 'p'=>$n, 'f'=>$n['g']+$w+$d);
if (isset($c[$j]))
unset($c[$j]);
if (! isset($l[$j]))
{
$o->push($m, -$m['f']);
$l[$j] = TRUE;
}
}
$c[$n['i']] = $n;
}
return $p;
}
}
?>
在code到Pqueue可以发现这里
该网站显示,该漏洞可能会在 PQueue
类。
The site suggests that the bug might be in the PQueue
class.
在 PQueue ::流行
此
$j+1 < $m
是测试是否堆节点在 $ I
有一个孩子(在附加$ J
)或两个(在附加$ J
和 $ J + 1
)。
is a test whether the heap node at $i
has one child (at $j
) or two (at $j
and $j+1
).
但 $ M
这里是计数($ H)
只能通过循环的第一次迭代,因为 - $ M
的循环条件,每次评估
But $m
here is count($h)
only on the first iteration through the loop, since the --$m
in the loop condition is evaluated every time.
此举 - $ M
旁边的 array_pop
它所属,这将是少了一个错误
Move that --$m
next to the array_pop
where it belongs, and that will be one less bug.
现在的 AStarSolver
。
的变量(相对于维基百科伪code )
$ 0
- 开集的优先级队列;
$ L
- 开集的地图通过索引键控;
$ C
- 闭集的映射通过索引键控;
$ N
- 当前节点( X 的);
$ M
- 邻居节点(是的);
附加$ J
- 邻居节点指数
$o
– open set as priority queue;
$l
– open set as map keyed by index;
$c
– closed set as map keyed by index;
$n
– current node (x);
$m
– neighbour node (y) ?;
$j
– neighbour node index.
的问题,我看到的:
$ N = $邻&GT;流行()
后面没有取消设置($ L [$ N ['我'])
。由于两个 $ 0
和 $ L
重present同一组,它们应该保持同步。
$n = $o->pop()
isn't followed by unset($l[$n['i']])
. Since both $o
and $l
represent the same set, they should be kept in sync.
根据维基百科的闭集仅用于当启发是的单调的(我认为的距离启发式是单调),在这种情况下,一旦一个节点添加到闭集,它永远不会再次访问。这code似乎实现一些其它伪code ,它不删除节点从闭集。我认为这违背了闭集的目的,并在内部循环的首要条件应该是
According to Wikipedia the closed set is only used if the heuristic is monotone (and I think a distance heuristic is monotone), and in that case, once a node is added to the closed set, it is never visited again. This code seems to implement some other pseudocode, which does remove nodes from the closed set. I think this defeats the purpose of the closed set, and the first condition in the inner loop should be
如果(使用isset($ C [$ J])||使用isset($ L [$ J])及和放大器;使用isset($ M)及和放大器; $ M ['G' ]&LT; = $ N ['G'] + $ W)
然后我们就可以删除取消设置($ C [$ J]。)
。
$ M ['G']
在这种情况下应该是先按g 的-score当前的邻居通过附加$ J 。但 $ M
的任何值从previous循环遗留下来:该节点对应于附加$ J
上一个previous迭代。
$m['g']
in this condition should be the g-score of the current neighbour indexed by $j
. But $m
has whatever value is left over from the previous loop: the node corresponding to $j
on a previous iteration.
我们需要的是一种能够找到一个节点,它的先按g 的-score由节点索引。我们可以存储在 $ L
数组中的节点:我们做的,而不是 $ L [$ J] = TRUE
$ L [$ J] = $ M
和上面的条件变得
What we need is a way to find a node and its g-score by node index. We can store the node in the $l
array: instead of $l[$j] = TRUE
we do $l[$j] = $m
and the above condition becomes
如果(使用isset($ C [$ J])||使用isset($ L [$ J])及和放大器; $ L [$ J] ['G']&LT; = $ N ['G'] + $ W)
现在的有点棘手。如果我们只是找到该节点不是在开集,我们将其添加有(这是 $邻&GT;推
和 $ L [$ J] =
)。
Now the tricky bit. If the node we just found is not in the open set, we add it there (that's the $o->push
and $l[$j] =
).
不过,如果是在开集,我们只是找到了一个更好的路径,所以我们必须更新。在code没有做到这一点,它的棘手,因为优先级队列不增加元素的优先级提供程序。然而,我们可以完全重建优先级队列和code在内部循环的最后一个比特
However, if it is in the open set we just found a better path to it, so we must update it. The code doesn't do this and it's tricky because the priority queue doesn't provide a routine for increasing the priority of an element. However, we can rebuild the priority queue completely and the last bit of code in the inner loop becomes
如果(!使用isset($ L [$ J])){
$邻&GT;推($ M, - $ M ['F']);
$ L [$ J] = $米; //添加新元素
}其他{
$ L [$ J] = $米; //替换现有的元素
$ O =新PQueue();
的foreach($ L为$ M)
$邻&GT;推($ M, - $ M ['F']);
}
if (! isset($l[$j])) {
$o->push($m, -$m['f']);
$l[$j] = $m; // add a new element
} else {
$l[$j] = $m; // replace existing element
$o = new PQueue();
foreach ($l as $m)
$o->push($m, -$m['f']);
}
这是不是非常有效,但它是一个起点。在更改优先级队列中的元素不是有效的,无论如何,因为你首先得的查找的吧。
This is not terribly efficient, but it's a starting point. Changing an element in a priority queue isn't efficient anyway, because you first have to find it.
即使没有这些改变的算法确实找到一个路径,只是没有的最佳路径。你可以看到它在提到迷宫:
Even without these changes the algorithm does find a path, just not the best path. You can see it in the mentioned mazes:
在疯狂
迷宫第三内部电路:在拍摄上的路径周围略长于下路径将是因为在该障碍离开了。
In the crazy
maze in the third inner circuit: the taken upper path around is slightly longer than the lower path would have been because of the obstacles on the left.
在大
迷宫路径中有不必要的循环上升的右上部分。
In the big
maze in the upper-right part of the path there's an unnecessary loop up.
由于这是在我的脑海,我实现了我自己的版本的算法并张贴在回答你的$p$pvious问题。
Since this was on my mind, I implemented my own version of the algorithm and posted it in an answer to your previous question.
上一篇:迭代最近点实施迭代、近点