Решение проблемы с Python

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

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

  • могут быть пройдены только узлы определенного типа, т.е. если начальные узлы имеют тип x, любой node в пути должен быть из другого набора путей, y или z
  • если node имеет тип y, его можно передать только один раз
  • если node имеет тип z, его можно передать дважды
  • в случае посещения a node типа z, выход должен быть из другого класса соседа, то есть если его посетили сверху, выход должен быть от нисходящего

Итак, я попробовал несколько экспериментов, но я, как сказал, боролся. Во-первых, я не уверен, какой тип графика действительно представляет? Он не направлен, так как не имеет значения, если вы переходите от node 1 до node 2 или от node 2 до node 1 ( кроме в этом последнем сценарии, поэтому что немного усложняет ситуацию...). Это означает, что я не могу просто создать граф, который просто многонаправлен, поскольку я должен иметь это ограничение. Во-вторых, мне нужно пройти через эти узлы, но указать, что для пути должны быть доступны только узлы определенного типа. Кроме того, в случае возникновения последнего сценария, я должен иметь в виду класс и направление ввода и выхода, что ставит его в несколько направленное состояние.

Вот пример кода макета:

import networkx as nx

G=nx.DiGraph()
G.add_node(1, type=1)
G.add_node(2, type=2)
G.add_node(3, type=3)
G.add_edge(1,2, side="up")
G.add_edge(1,3, side="up")
G.add_edge(2,1, side="down")
G.add_edge(2,3, side="down")
for path in nx.all_simple_paths(G,1,3):
    print path

Результат довольно приятный, но мне нужны эти ограничения. Итак, есть ли у вас предложения о том, как я могу их реализовать, или дать мне несколько рекомендаций относительно понимания этого типа проблем или предложить другой подход или библиотеку для этой проблемы? Может быть, простой алгоритм на основе словаря соответствовал бы этой потребности?

Спасибо!

Ответ 1

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

  • могут быть пройдены только узлы определенного типа, т.е. если начальные узлы имеют тип x, любой node в пути должен быть из другого набора путей, y или z

Учитывая начальный node n, удалите все остальные узлы с этим типом, прежде чем находите пути.

  • если node имеет тип y, его можно передать только один раз

Это определение простых путей, поэтому оно автоматически выполняется.

  • если node имеет тип z, его можно передать дважды

Для каждого node n типа z добавьте новый node n2 с теми же ребрами, что и те, которые указывают на и из n.

  • в случае посещения a node типа z, выход должен быть из другого класса соседа, то есть если его посетили сверху, выход должен быть от нисходящего

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

Ответ 2

Лучший способ сделать это, я думаю, - вычислить все допустимые пути длины не более k между исходным S и любым другим node, а затем использовать эту информацию для вычисления всех допустимых путей длины не более k + 1. Затем вы просто повторяете это, пока не получите фиксированную точку, где никакие пути не будут изменены.

На практике это означает, что вы должны настроить список путей на каждом node. На каждом шаге вы поочередно берете node U и смотрите пути, которые на предыдущем шаге заканчивались на некотором соседнем V из U. Если какой-либо из этих путей может быть расширен, чтобы быть новым, различным путем к U, расширьте его и добавьте в список U.

Если при выполнении шага вы не найдете новых путей, это состояние завершения. Затем вы можете проверить список путей в целевом node T.

Псевдокод (в очень свободном формализме С#):

var paths = graph.nodes.ToDictionary(node => node, node => new List<List<node>>())
paths[S].Add(new List<node> {S}) // The trivial path that'll start us off.


bool notAFixedPoint = true;

while (notAFixedPoint)
{
    notAFixedPoint = false // Assume we're not gonna find any new paths.

    foreach (var node in graph)
    {
        var pathsToNode = paths[node]

        foreach (var neighbour in node.Neighbours)
        {
            var pathsToNeighbour = paths[neighbour]

            // ExtendPaths is where all the logic about how to recognise a valid path goes.
            var newPathsToNode = ExtendPaths(pathsToNeighbour, node)

            // The use of "Except" here is for expository purposes. It wouldn't actually work,
            // because collections in most languages are compared by reference rather than by value.
            if (newPathsToNode.Except(pathsToNode).IsNotEmpty())
            {
                // We've found some new paths, so we can't terminate yet.
                notAFixedPoint = true 

                pathsToNode.AddMany(newPathsToNode)
            }
        }
    }
}

return paths[T] 

Ответ 3

Для меня это похоже на проблему оптимизации - посмотрите "Traveling Salesman" на классический пример, который немного близок к тому, что вы хотите сделать.

Мне удалась использовать "имитированный отжиг" для задач оптимизации, но вы также можете взглянуть на "генетические алгоритмы".