Эффективное обнаружение кратчайшего пути в больших графах

Я ищу, чтобы найти способ в реальном времени найти кратчайший путь между узлами в огромном графе. Он содержит сотни тысяч вершин и миллионы ребер. Я знаю, что этот вопрос задан раньше, и я думаю, что ответ заключается в использовании поиска по ширине, но мне больше интересно узнать, какое программное обеспечение вы можете использовать для его реализации. Например, было бы совершенно идеально, если бы уже существовала библиотека (с привязками python!) Для выполнения bfs в неориентированных графах.

Ответы

Ответ 1

python-graph

добавлено:

В комментариях мне было любопытно узнать, как производительность пигграфа была связана с проблемой порядка OP, поэтому я сделал игрушку, чтобы узнать. Здесь вывод для немного меньшей версии проблемы:

$ python2.6 biggraph.py 4 6
biggraph generate 10000 nodes     00:00:00
biggraph generate 1000000 edges   00:00:00
biggraph add edges                00:00:05
biggraph Dijkstra                 00:01:32
biggraph shortest_path done       00:04:15
step: 1915 2
step: 0 1
biggraph walk done                00:04:15
path: [9999, 1915, 0]

Неплохо для узлов 10k и краев 1M. Важно отметить, что метод Dijkstra, вычисляемый pygraph, дает словарь всех связующих деревьев для каждого node относительно одной цели (которая была произвольно node 0 и не имеет привилегированного положения на графике). Таким образом, решение, которое потребовалось 3,75 минуты для вычисления, действительно дало ответ на вопрос: "Каков самый короткий путь от всех узлов к цели?". Действительно, раз shortest_path было сделано, ходящий ответ был простым поиском словарей и практически не занимал времени. Также стоит отметить, что добавление предварительно вычисленных ребер к графику было довольно дорогим в течение ~ 1,5 минут. Эти тайминги согласованы между несколькими запусками.

Я хотел бы сказать, что процесс масштабируется хорошо, но я все еще жду на biggraph 5 6 на другом компьютере без работы (Athlon 64, 4800 BogoMIPS на процессор, все в ядре), который работает более четверти часа. По крайней мере, использование памяти стабильно на уровне около 0,5 ГБ. И результаты:

biggraph generate 100000 nodes    00:00:00
biggraph generate 1000000 edges   00:00:00
biggraph add edges                00:00:07
biggraph Dijkstra                 00:01:27
biggraph shortest_path done       00:23:44
step: 48437 4
step: 66200 3
step: 83824 2
step: 0 1
biggraph walk done                00:23:44
path: [99999, 48437, 66200, 83824, 0]

Это долгое время, но это было также тяжелое вычисление (и я действительно хочу, чтобы я маринул результат). Здесь код для любопытных:

#!/usr/bin/python

import pygraph.classes.graph
import pygraph.algorithms
import pygraph.algorithms.minmax
import time
import random
import sys

if len(sys.argv) != 3:
    print ('usage %s: node_exponent edge_exponent' % sys.argv[0])
    sys.exit(1)

nnodes = 10**int(sys.argv[1])
nedges = 10**int(sys.argv[2])

start_time = time.clock()
def timestamp(s):
    t = time.gmtime(time.clock() - start_time)
    print 'biggraph', s.ljust(24), time.strftime('%H:%M:%S', t)

timestamp('generate %d nodes' % nnodes)
bg = pygraph.classes.graph.graph()
bg.add_nodes(xrange(nnodes))

timestamp('generate %d edges' % nedges)
edges = set()
while len(edges) < nedges:
    left, right = random.randrange(nnodes), random.randrange(nnodes)
    if left == right:
        continue
    elif left > right:
        left, right = right, left
    edges.add((left, right))

timestamp('add edges')
for edge in edges:
    bg.add_edge(edge)

timestamp("Dijkstra")
target = 0
span, dist = pygraph.algorithms.minmax.shortest_path(bg, target)
timestamp('shortest_path done')

# the paths from any node to target is in dict span, let's
# pick any arbitrary node (the last one) and walk to the
# target from there, the associated distance will decrease
# monotonically
lastnode = nnodes - 1
path = []
while lastnode != target:
    nextnode = span[lastnode]
    print 'step:', nextnode, dist[lastnode]
    assert nextnode in bg.neighbors(lastnode)
    path.append(lastnode)
    lastnode = nextnode
path.append(target)
timestamp('walk done')
print 'path:', path

Ответ 2

Для больших графов попробуйте интерфейс Python igraph. Его ядро ​​реализовано на C, поэтому оно может легко справляться с графиками с миллионами вершин и ребер. Он содержит реализацию BFS (среди других алгоритмов), а также алгоритм Дейкстры и алгоритм Беллмана-Форда для взвешенных графиков.

Что касается "realtimeness", я сделал несколько быстрых тестов:

from igraph import *
from random import randint
import time

def test_shortest_path(graph, tries=1000):
    t1 = time.time()
    for _ in xrange(tries):
        v1 = randint(0, graph.vcount()-1)
        v2 = randint(0, graph.vcount()-1)
        sp = graph.get_shortest_paths(v1, v2)
    t2 = time.time()
    return (t2-t1)/tries

>>> print test_shortest_path(Graph.Barabasi(100000, 100))     
0.010035698396
>>> print test_shortest_path(Graph.GRG(1000000, 0.002))
0.413572219742

В соответствии с вышеприведенным фрагментом кода поиск кратчайшего пути между двумя заданными вершинами в графе малого мира, имеющем вершины 100K и ребра 10M (10M = 100K * 100), составляет в среднем 0,01003 секунды (в среднем от 1000 попыток). Это был первый тестовый пример, и это разумная оценка, если вы работаете с данными социальной сети или какой-либо другой сетью, где диаметр известен как небольшой по сравнению с размером сети. Второй тест представляет собой геометрический случайный график, в котором на двумерной плоскости случайным образом удаляются 1 миллион точек, а две точки связаны, если их расстояние меньше 0,002, в результате получается граф с примерно 1M вершинами и 6,5 М ребрами. В этом случае вычисление кратчайшего пути занимает больше времени (поскольку сами пути больше), но он все еще довольно близок к реальному времени: в среднем 0,41357 секунд.

Отказ от ответственности: я являюсь одним из авторов igraph.

Ответ 3

Для большого (и с вашими ограничениями производительности) графика вы, вероятно, захотите Boost Graph Library, поскольку он написан на С++. Он имеет привязки Python, который вы ищете.

Ответ 4

Ну, это зависит от того, сколько метаданных вы привязали к своим узлам и краям. Если относительно мало, то размер графика будет вписываться в память, и поэтому я рекомендую отличный пакет NetworkX (особенно, http://networkx.lanl.gov/reference/generated/networkx.shortest_path.html), который является чистым Python.

Для более надежного решения, которое может обрабатывать многие миллионы узлов, больших метаданных, транзакций, дискового хранилища и т.д., мне повезло с neo4j (http://www.neo4j.org/). Он написан на Java, но имеет привязки Python или может быть запущен как сервер REST. Обход с ним немного сложнее, но неплохо.

Ответ 5

BFS в ненаправленном графе составляет всего около 25 строк кода. Вам не нужна библиотека. Посмотрите пример кода в Статья в Википедии.

Ответ 6

В зависимости от того, какая дополнительная информация у вас есть, A * может быть чрезвычайно эффективным. В частности, если задано node, вы можете вычислить оценку стоимости с этого node цели, A * оптимально эффективна.

Ответ 7

сохранить в neo4j

Он включает в себя алгоритмы Dijkstra, A *, "кратчайшего пути".