Эффективно найти кратчайший путь в больших графах - PullRequest
15 голосов
/ 14 июня 2010

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

Ответы [ 7 ]

17 голосов
/ 14 июня 2010

python-graph

добавлено:

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

$ 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.Важно отметить, что способ вычисления Дейкстры с помощью pygraph дает словарь всех связующих деревьев для каждого узла относительно одной цели (который был произвольно узлом 0 и не имеет привилегированной позиции в графе).Следовательно, решение, которое заняло 3,75 минуты для вычисления, фактически дало ответ на вопрос «какой кратчайший путь от всех узлов до цели?».Действительно, как только shortest_path было сделано, поиск ответа был простым поиском по словарю и, по сути, не занял много времени.Стоит также отметить, что добавление предварительно вычисленных ребер в график было довольно дорогостоящим: ~ 1,5 минуты.Эти временные интервалы одинаковы при нескольких запусках.

Я бы хотел сказать, что процесс хорошо масштабируется, но я все еще жду biggraph 5 6 на компьютере, который в противном случае не работает на процессор, все в ядре), который работал более четверти часа.По крайней мере, использование памяти стабильно около 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
10 голосов
/ 15 июня 2010

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

Что касается "реалистичности", я также провел несколько быстрых тестов:

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, в результате чего получается граф с примерно 1М вершин и 6,5М ребер.В этом случае вычисление кратчайшего пути занимает больше времени (поскольку сами пути длиннее), но оно все еще довольно близко к реальному времени: в среднем 0,41357 секунды.

Отказ от ответственности: я один изавторы igraph .

3 голосов
/ 15 июня 2010

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

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

3 голосов
/ 14 июня 2010

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

2 голосов
/ 14 июня 2010

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

0 голосов
/ 15 февраля 2013

магазин в neo4j

Это включает Дейкстру, A *, алгоритмы "кратчайшего пути".

0 голосов
/ 15 июня 2010

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

...