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 算法 | 图最短路径算法使用场景 | 求解图中任意两个点之间的最短路径 | 邻接矩阵存储图数据 | 弗洛伊德算法总结 )

图中的最短路径查询

彩色边图中的最短路径

求图中任意两点之间最短路径有啥算法?

访问k个顶点的无向图中的最短路径

图中所有节点到特定节点的最短路径成本