Поиск путей в неориентированном графе


Рассмотрим следующий график:

dummy graph

Представлен следующей структурой массива:

$graph = array
(
    'a' => array(),
    'b' => array('a'),
    'c' => array('a', 'b'),
    'd' => array('a'),
    'e' => array('d'),
    'f' => array('a', 'b', 'c', 'd'),
    'g' => array('d'),
    'h' => array('c'),
    'i' => array('c', 'g'),
    'j' => array(),
);

Каков наиболее эффективный алгоритм для поиска всех путей (а не только кратчайшего) от узла X к узлу Y в любом направлении без повторяющихся узлов ? Например, пути, ведущие от узла C к узлу A, следующие:

C --> A
C --> B --> A
C --> F --> A
C --> F --> B --> A
C --> F --> D --> A
C --> I --> G --> D --> A

Поиск всех путей с использованием родительских узлов узла X (A и B, в пример для узла C) тривиален, но мне действительно трудно перемещаться по узлам в направлении потомка/гибрида.

Кто-нибудь может мне помочь?


ОБНОВЛЕНИЕ: Следуя совету @Jackmaney, я попытался перенести IDDFS (Итеративный поиск по глубине углубления) на основе псевдокода Википедии, и это более или менее похоже на мой код:

$graph = directed2Undirected($graph);

function IDDFS($root, $goal) {
    $depth = 0;

    while ($depth <= 2) { // 2 is hard-coded for now
        $result = DLS($root, $goal, $depth);

        if ($result !== false) {
            return $result;
        }

        $depth++;
    }
}

function DLS($node, $goal, $depth) {
    global $graph;

    if (($depth >= 0) && ($node == $goal)) {
        return $node;
    }

    else if ($depth > 0) {
        foreach (expand($node, $graph) as $child) {
            return DLS($child, $goal, $depth - 1);
        }
    }

    else {
        return false;
    }
}

И вот вспомогательные функции, используемые им:

function directed2Undirected($data) {
    foreach ($data as $key => $values) {
        foreach ($values as $value) {
            $data[$value][] = $key;
        }
    }

    return $data;
}

function expand($id, $data, $depth = 0) {
    while (--$depth >= 0) {
        $id = flatten(array_intersect_key($data, array_flip((array) $id)));
    }

    return array_unique(flatten(array_intersect_key($data, array_flip((array) $id))));
}

function flatten($data) {
    $result = array();

    if (is_array($data) === true) {
        foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($data)) as $value) {
            $result[] = $value;
        }
    }

    return $result;
}

Вызов вышеуказанного дает странные или неполные результаты:

var_dump(IDDFS('c', 'a')); // a -- only 1 path?
var_dump(IDDFS('c', 'd')); // NULL -- can't find this path?!

Я думаю, что упускаю что-то из псевдокода, но я не уверен, что это такое.


Я также попробовал этот класс DFS, который был рекомендован в другом вопросе, хотя, похоже, он всегда находит один путь от узла X до узла Y, я не могу заставить его возвращать все пути (демонстрация для C -> A и C -> D).


Поскольку мне не нужно знать, какой путь на самом деле пройден, только сколько существует путей, для которых требуется n количество шагов , чтобы добраться от узла X до узла Y, я придумал эту функцию (использует directed2Undirected выше):

$graph = directed2Undirected($graph);

function Distance($node, $graph, $depth = 0) {
    $result = array();

    if (array_key_exists($node, $graph) === true) {
        $result = array_fill_keys(array_keys($graph), 0);

        foreach (expand($node, $graph, $depth - 1) as $child) {
            if (strcmp($node, $child) !== 0) {
                $result[$child] += $depth;
            }
        }

        $result[$node] = -1;
    }

    return $result;
}

function expand($id, $data, $depth = 0) {
    while (--$depth >= 0) {
        $id = flatten(array_intersect_key($data, array_flip((array) $id)));
    }

    // no array_unique() now!
    return flatten(array_intersect_key($data, array_flip((array) $id)));
}

Для Distance('c', $graph, 0), Distance('c', $graph, 1) и Distance('c', $graph, 2) это правильно возвращает сумму расстояния между C и любым другим узлом. Проблема в том, что с Distance('c', $graph, 3) ( и выше) он начинает повторять узлы и возвращает неправильные результаты:

Array
(
    [a] => 12
    [b] => 9
    [c] => -1
    [d] => 9
    [e] => 3
    [f] => 12
    [g] => 3
    [h] => 3
    [i] => 6
    [j] => 0
)

Индекс a должен только 6 (3+3), так как единственными способами, которыми я могу перейти от C к A, используя 3 шага, являются:

C --> F --> B --> A
C --> F --> D --> A

Тем не менее, похоже, что он рассматривает два ( только?) дополнительные пути, которые повторяют узлы:

C --> A --> C --> A
C --> B --> C --> A
C --> F --> C --> A
C --> H --> C --> A
C --> I --> C --> A

Конечно, индекс a не единственный неправильный. Проблема, похоже, в expand(), но я не уверен, как это исправить, array_diff(expand('c', $graph, $i), expand('c', $graph, $i - 2)), похоже, исправляет эту конкретную ошибку, но это неправильное исправление... Помочь?


dummy graph again опять же, чтобы вам не пришлось прокрутка

Author: Alix Axel, 2012-05-29

1 answers

В общем случае вы можете выполнить поиск по глубине или поиск по ширине , хотя ни один из них не превосходит другой (так как легко придумать примеры, для которых один превосходит другой).

Редактировать: Перечитав вопрос и немного подумав, поскольку вам нужны все пути от C до A, DFS, начинающийся с C, вероятно, будет иметь наибольший смысл. По пути вам придется хранить последовательности ребер и выбрасывать последовательности если они не окажутся в A.

 2
Author: , 2012-05-29 18:16:24