一尘不染

如何从多路径Dijkstra重构路径?

algorithm

我目前正在为图形编写PHP库。我已经成功地实现了单路径Dijkstra的算法,但是现在在路径重构阶段难以实现多路径版本。

拍下图:

图形

为了简单起见,此图仅具有从顶点A到J的路径,并且经过多个其他顶点,这些路径的成本均相等,也就是说,每条路径的边权重总计为10。修改后的Dijkstra正确生成以下输出(是array
$this->prev):

Array
(
    [A] => 
    [B] => Array
        (
            [0] => A
        )

    [C] => Array
        (
            [0] => A
        )

    [D] => Array
        (
            [0] => C
        )

    [E] => Array
        (
            [0] => C
        )

    [F] => Array
        (
            [0] => E
            [1] => D
        )

    [G] => Array
        (
            [0] => A
        )

    [H] => Array
        (
            [0] => G
        )

    [I] => Array
        (
            [0] => H
        )

    [J] => Array
        (
            [0] => B
            [1] => F
            [2] => I
        )

)

当前的单路径Dijkstra路径重构算法是这样实现的:

public function get($dest)
{
    $destReal = $dest;
    $path = array();
    while (isset($this->prev[$dest])) {
        array_unshift($path, $dest);
        $dest = $this->prev[$dest];
    }
    if ($dest === $this->start) {
        array_unshift($path, $dest);
    }

    return array(
        'path' => $path,
        'dist' => $this->dist[$destReal]
    );
}

有没有办法修改上面的内容,以便它返回paths数组中的所有路径?我已经考虑过使用堆栈或DFS,但无法提出解决方案。我也foreach尝试了循环和递归,但无济于事。

我本质上想要发生的是按以下方式处理结果:

  • J连接到B,B连接到A,因此 $paths[0] = ['J', 'B', 'A']
  • Ĵ所连接到F,F所连接到E和d,由此继续对通过E,记住返回到F,然后创建通过d另一个路径,导致paths[1] = ['J', 'F', 'E', 'C', 'A']$paths[2] = ['J', 'F', 'D', 'C', 'A']
  • J连接到I,I连接到H,H连接到G,G连接到A,结果 $paths[3] = ['J', 'I', 'H', 'G', 'A']

任何帮助,将不胜感激!


阅读 330

收藏
2020-07-28

共1个答案

一尘不染

实际上,我命名为“枚举”的DFS修改函数解决了这个问题。为了后代的缘故,以下是我用来将多路径Dijkstra的输出转换为路径数组的代码:

/**
 * Returns all shortest paths to $dest from the origin vertex $this->start in the graph.
 *
 * @param string $dest ID of the destination vertex
 *
 * @return array An array containing the shortest path and distance
 */
public function get($dest)
{
    $this->paths = [];
    $this->enumerate($dest, $this->start);

    return array(
        'paths' => $this->paths,
        'dist' => $this->dist[$dest],
    );
}

/**
 * Enumerates the result of the multi-path Dijkstra as paths.
 *
 * @param string $source ID of the source vertex
 * @param string $dest   ID of the destination vertex
 */
private function enumerate($source, $dest)
{
    array_unshift($this->path, $source);
    $discovered[] = $source;

    if ($source === $dest) {
        $this->paths[] = $this->path;
    } else {
        if (!$this->prev[$source]) {
            return;
        }
        foreach ($this->prev[$source] as $child) {
            if (!in_array($child, $discovered)) {
                $this->enumerate($child, $dest);
            }
        }
    }

    array_shift($this->path);
    if (($key = array_search($source, $discovered)) !== false) {
        unset($discovered[$key]);
    }
}
2020-07-28