Найти все возможные пути из одной вершины в ориентированном циклическом графе в Эрланге - PullRequest
6 голосов
/ 20 октября 2011

Я хотел бы реализовать функцию, которая находит все возможные пути ко всем возможным вершинам из исходной вершины V в ориентированном циклическом графе G.

Производительность теперь не имеет значения, я просто хотел быпонять алгоритм.Я прочитал определение алгоритма поиска в глубину, но у меня нет полного понимания того, что делать.

У меня нет готового фрагмента кода, чтобы предоставить здесь, потому что я неубедитесь, что:

  • сохраните результаты (наряду с A-> B-> C-> мы также должны хранить A-> B и A-> B-> C);
  • представляют график (digraph? Список кортежей?);
  • сколько рекурсий использовать (работать с каждой смежной вершиной?).

Как найтивсе возможные пути образуют одну заданную исходную вершину в ориентированном циклическом графе в Erlang?

UPD: Основываясь на ответах, которые я до сих пор должен был переопределить, определение графа: это неациклический граф.Я знаю, что если моя рекурсивная функция попадает в цикл, это неопределенный цикл.Чтобы избежать этого, я могу просто проверить, есть ли текущая вершина в списке полученного пути - если да, я прекращаю обход и возвращаю путь.


UPD2: Спасибодля размышлений, вызывающих комментарии!Да, мне нужно найти все простые пути, у которых нет петель из одной исходной вершины во все остальные.

В графе, подобном этому:

Non-acyclic graph

с исходной вершиной A алгоритм должен найти следующие пути:

  • A, B
  • A, B, C
  • A, B, C, D
  • A, D
  • A, D, C
  • A, D, C, B

Следующий код выполняет работу, но оннепригоден для графов, которые имеют более 20 вершин (я думаю, что это неправильно с рекурсией - занимает слишком много памяти, никогда не заканчивается):

dfs(Graph,Source) ->
    ?DBG("Started to traverse graph~n", []),
            Neighbours = digraph:out_neighbours(Graph,Source),
    ?DBG("Entering recursion for source vertex ~w~n", [Source]),
            dfs(Neighbours,[Source],[],Graph,Source),
ok.


dfs([],Paths,Result,_Graph,Source) ->
    ?DBG("There are no more neighbours left for vertex ~w~n", [Source]),
    Result;

dfs([Neighbour|Other_neighbours],Paths,Result,Graph,Source) ->
    ?DBG("///The neighbour to check is ~w, other neighbours are: ~w~n",[Neighbour,Other_neighbours]),
    ?DBG("***Current result: ~w~n",[Result]),
    New_result = relax_neighbours(Neighbour,Paths,Result,Graph,Source),

        dfs(Other_neighbours,Paths,New_result,Graph,Source).


relax_neighbours(Neighbour,Paths,Result,Graph,Source) ->
     case lists:member(Neighbour,Paths) of 
        false ->
            ?DBG("Found an unvisited neighbour ~w, path is: ~w~n",[Neighbour,Paths]),
            Neighbours = digraph:out_neighbours(Graph,Neighbour),
            ?DBG("The neighbours of the unvisited vertex ~w are ~w, path is:
                ~w~n",[Neighbour,Neighbours,[Neighbour|Paths]]),
                dfs(Neighbours,[Neighbour|Paths],Result,Graph,Source);
            true ->
                [Paths|Result]

        end.

UPD3:

Проблема в том, что обычный алгоритм поиска в глубину сначала идет по одному из путей: (A, B, C, D) или (A, D, C, B) и никогда не идет по второму.path.

В любом случае это будет единственный путь - например, когда обычные возвратные дорожки DFS из (A, B, C, D) возвращаются к A и проверяет, является ли D (вторым соседом)а) посещается.А поскольку обычная DFS поддерживает глобальное состояние для каждой вершины, D будет иметь «посещенное» состояние.

Итак, мы должны ввести рекурсивно-зависимое состояние - если мы возвращаемся из (A, B, C,D) до A у нас должно быть (A, B, C, D) в списке результатов, и мы должны иметь D, помеченный как не посещенный, как в самом начале алгоритма.

Я пыталсяоптимизировать решение для хвостовой рекурсии, но время выполнения алгоритма неосуществимо - требуется около 4 секунд, чтобы пройти крошечный граф из 16 вершин с 3 ребрами на вершину:

dfs(Graph,Source) ->
    ?DBG("Started to traverse graph~n", []),
            Neighbours = digraph:out_neighbours(Graph,Source),
    ?DBG("Entering recursion for source vertex ~w~n", [Source]),
    Result = ets:new(resulting_paths, [bag]),
Root = Source,
            dfs(Neighbours,[Source],Result,Graph,Source,[],Root).


dfs([],Paths,Result,_Graph,Source,_,_) ->
    ?DBG("There are no more neighbours left for vertex ~w, paths are ~w, result is ~w~n", [Source,Paths,Result]),
    Result;

dfs([Neighbour|Other_neighbours],Paths,Result,Graph,Source,Recursion_list,Root) ->
    ?DBG("~w *Current source is ~w~n",[Recursion_list,Source]),
    ?DBG("~w Checking neighbour _~w_ of _~w_, other neighbours are: ~w~n",[Recursion_list,Neighbour,Source,Other_neighbours]),



?    DBG("~w Ready to check for visited: ~w~n",[Recursion_list,Neighbour]),

 case lists:member(Neighbour,Paths) of 
        false ->
            ?DBG("~w Found an unvisited neighbour ~w, path is: ~w~n",[Recursion_list,Neighbour,Paths]),
New_paths = [Neighbour|Paths],
?DBG("~w Added neighbour to paths: ~w~n",[Recursion_list,New_paths]),
ets:insert(Result,{Root,Paths}),

            Neighbours = digraph:out_neighbours(Graph,Neighbour),
            ?DBG("~w The neighbours of the unvisited vertex ~w are ~w, path is: ~w, recursion:~n",[Recursion_list,Neighbour,Neighbours,[Neighbour|Paths]]),
                dfs(Neighbours,New_paths,Result,Graph,Neighbour,[[[]]|Recursion_list],Root);
            true -> 
            ?DBG("~w The neighbour ~w is: already visited, paths: ~w, backtracking to other neighbours:~n",[Recursion_list,Neighbour,Paths]),
ets:insert(Result,{Root,Paths})

end,

        dfs(Other_neighbours,Paths,Result,Graph,Source,Recursion_list,Root).

Любойидеи запустить это в приемлемое время?

Ответы [ 3 ]

2 голосов
/ 20 октября 2011

Редактировать: Хорошо, теперь я понимаю, вы хотите найти все простые пути из вершины в ориентированном графе.Таким образом, поиск в глубину с возвратом назад, как вы уже поняли, подойдет.Основная идея состоит в том, чтобы пойти к соседу, затем перейти к другому (не тот, который вы посетили), и продолжайте идти, пока не зашли в тупик.Затем вернитесь к последней вершине, в которой вы находились, и выберите другого соседа и т. Д. Вам нужно правильно разобраться с битыми битами, но это не должно быть слишком сложно.Например, на каждом этапе вам нужно пометить вершины как "исследованные" или "неизученные" в зависимости от того, посещали ли вы их ранее.Производительность не должна быть проблемой, правильно реализованный алгоритм должен занимать, возможно, O (n ^ 2) время.Так что я не знаю, что вы делаете неправильно, возможно, вы посещаете слишком много соседей?Например, возможно, вы посещаете соседей, которых вы уже посещали, и зацикливаетесь или что-то в этом роде.

Я на самом деле не читал вашу программу, но страница Wiki по поиску в глубину имеет короткий, простойПрограмма псевдокода, которую вы можете попробовать скопировать на вашем языке.Сохраните графики в виде списков смежности, чтобы упростить их.

Редактировать: Да, извините, вы правы, стандартный поиск DFS не будет работать как есть, вам нужно настроить егонемного так, что он вернется к вершинам, которые он посещал раньше.Таким образом, вам разрешено посещать любые вершины, кроме тех, которые вы уже сохранили в своем текущем пути.Это, конечно, означает, что мое время выполнения было совершенно неверным, сложность вашего алгоритма будет выше.Если средняя сложность вашего графа равна d + 1, то будет приблизительно d * d * d * ... * d = d ^ n возможных путей.Таким образом, даже если у каждой вершины есть только 3 соседа, есть еще довольно много путей, когда вы получаете более 20 вершин.На самом деле нет никакого способа обойти это, потому что если вы хотите, чтобы ваша программа выводила все возможные пути, тогда вам действительно нужно будет вывести все d ^ n из них.

Мне интересно знать, нужно ли вам это дляконкретная задача, или просто пытаются запрограммировать это из интереса.Если последнее, вы просто должны быть довольны маленькими, разреженными графиками.

2 голосов
/ 20 октября 2011

Я не понимаю вопроса. Если у меня есть граф G = (V, E) = ({A, B}, {(A, B), (B, A)}), существует бесконечный путь от A до B {[A, B], [ A, B, A, B], [A, B, A, B, A, B], ...}. Как мне найти все возможные пути к любой вершине в циклическом графе?

Edit:

Вы даже пытались вычислить или угадать рост возможных путей для некоторых графиков? Если у вас есть полностью подключенный график, вы получите

  • 2 - 1
  • 3 - 4
  • 4 - 15
  • 5 - 64
  • 6 - 325
  • 7 - 1956
  • 8 - 13699
  • 9 - 109600
  • 10 - 986409
  • 11 - 9864100
  • 12 - 108505111
  • 13 - 1302061344
  • 14 - 16926797485
  • 15 - 236975164804
  • 16 - 3554627472075
  • 17 - 56874039553216
  • 18 - 966858672404689
  • 19 - 17403456103284420
  • 20 - 330665665962403999

Вы уверены, что хотите найти все пути для всех узлов? Это означает, что если вы вычисляете один миллион путей в одну секунду, потребуется 10750 лет, чтобы вычислить все пути ко всем узлам в полностью связанном графе с 20 узлами. Это верхняя граница для вашей задачи, поэтому я думаю, что вы не хотели бы делать это. Я думаю, вы хотите что-то еще.

1 голос
/ 22 октября 2011

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

Вы можете увидеть пример здесь: Некоторые функции матрицы Эрланга , в функции maximise_assignment (комментарии начинаются со строки 191 на сегодняшний день). Опять же, в основе алгоритма лежит довольно наивная и грубая сила, но распараллеливание довольно быстро ускоряет его для многих форм матриц.

Я использовал аналогичный подход в прошлом, чтобы найти число гамильтоновых путей в графе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...