SQL - postgres - 图中的最短路径 - 递归
Posted
技术标签:
【中文标题】SQL - postgres - 图中的最短路径 - 递归【英文标题】:SQL - postgres - shortest path in graph - recursion 【发布时间】:2011-10-15 23:17:25 【问题描述】:我有一个表,其中包含图中从节点 x 到节点 y 的边。
n1 | n2
-------
a | a
a | b
a | c
b | b
b | d
b | c
d | e
我想创建一个(物化)视图,它表示从 x 到节点 y 的路径包含的最短节点数/跳数:
n1 | n2 | c
-----------
a | a | 0
a | b | 1
a | c | 1
a | d | 2
a | e | 3
b | b | 0
b | d | 1
b | c | 1
b | e | 2
d | e | 1
我应该如何为我的表和视图建模以促进这一点?我想我需要某种递归,但我相信这在 SQL 中很难完成。我想避免这种情况,例如,如果路径恰好包含 10 个节点/跃点,则客户端需要触发 10 个查询。
【问题讨论】:
PostgreSQL 9 有 WITH RECURSIVE 但我不关心在数据库中寻找最短路径。 【参考方案1】:这对我有用,但有点丑:
WITH RECURSIVE paths (n1, n2, distance) AS (
SELECT
nodes.n1,
nodes.n2,
1
FROM
nodes
WHERE
nodes.n1 <> nodes.n2
UNION ALL
SELECT
paths.n1,
nodes.n2,
paths.distance + 1
FROM
paths
JOIN nodes
ON
paths.n2 = nodes.n1
WHERE
nodes.n1 <> nodes.n2
)
SELECT
paths.n1,
paths.n2,
min(distance)
FROM
paths
GROUP BY
1, 2
UNION
SELECT
nodes.n1,
nodes.n2,
0
FROM
nodes
WHERE
nodes.n1 = nodes.n2
另外,我不确定它对更大的数据集的性能有多好。正如 Mark Mann 所建议的,您可能想要使用图形库,例如pygraph
.
编辑:这是pygraph
的示例
from pygraph.algorithms.minmax import shortest_path
from pygraph.classes.digraph import digraph
g = digraph()
g.add_node('a')
g.add_node('b')
g.add_node('c')
g.add_node('d')
g.add_node('e')
g.add_edge(('a', 'a'))
g.add_edge(('a', 'b'))
g.add_edge(('a', 'c'))
g.add_edge(('b', 'b'))
g.add_edge(('b', 'd'))
g.add_edge(('b', 'c'))
g.add_edge(('d', 'e'))
for source in g.nodes():
tree, distances = shortest_path(g, source)
for target, distance in distances.iteritems():
if distance == 0 and not g.has_edge((source, target)):
continue
print source, target, distance
不包括建图时间,这需要 0.3 毫秒,而 SQL 版本需要 0.5 毫秒。
【讨论】:
【参考方案2】:扩展 Mark 的答案,还有一些非常合理的方法可以在 SQL 中探索图。事实上,它们会比 perl 或 python 中的专用库更快,因为 DB 索引将让您无需探索图表。
最有效的索引(如果图形不是不断变化的)是称为GRIPP index 的嵌套树变体。 (链接的论文提到了其他方法。)
如果您的图形在不断变化,您可能希望将nested intervals 方法应用于图形,以类似于 GRIPP 扩展嵌套集的方式,或者简单地使用浮点数而不是整数(不要忘记通过如果你这样做,转换为数字并返回浮动)。
【讨论】:
【参考方案3】:与其动态计算这些值,不如创建一个包含所有有趣对以及最短路径值的真实表。然后,无论何时在数据表中插入、删除或更新数据,您都可以重新计算所有最短路径信息。 (Perl 的Graph
模块特别适合这项任务,Perl 的DBI
接口使代码简单明了。)
通过使用外部进程,您还可以限制重新计算的次数。使用 PostgreSQL 触发器会导致在每次插入、更新和删除时发生重新计算,但如果您知道要添加 20 对点,则可以等到插入完成后再进行计算。
【讨论】:
以上是关于SQL - postgres - 图中的最短路径 - 递归的主要内容,如果未能解决你的问题,请参考以下文章
数据结构与算法图最短路径算法 ( Floyed 算法 | 图最短路径算法使用场景 | 求解图中任意两个点之间的最短路径 | 邻接矩阵存储图数据 | 弗洛伊德算法总结 )