【问题标题】:Optimized dijkstra’s algorithm for an extremely dense graph with matrix优化了具有矩阵的极其密集图的 dijkstra 算法
【发布时间】:2021-02-11 08:52:02
【问题描述】:

您有 N 个节点,标记为 1…N (N

还给你一个矩阵 M x M,以确定不同 ID 的节点之间是否存在有向边。如果 ID 为 i 的节点有一条到 ID 为 j 的节点的有向边,则 M_ij = 'Y',否则为'N'。请注意,M_ij 可能不等于 M_ji,而 M_ii 可能 = 0。

如果两个节点之间有一条边,那么它们之间的有向边的权重就是它们的标签之间的差异。

例子:

有 5 个节点。

节点 1:ID 1

节点 2:ID 4

节点 3:ID 2

节点 4:ID 3

节点 5:ID 4

矩阵:

YNYN

NNNY

NYYN

NYNN

所以在这个矩阵中,我们知道 ID 1 的节点有指向 ID 1 和 ID 3 的节点的边。ID 2 的节点有指向 ID 4 的节点的有向边。ID 3 的节点有指向 ID 3 的节点的有向边ID 2 和 ID 3。ID 4 的节点有指向 ID 2 的节点。

答案 = 6。

节点 1 和 5 之间的最短路径是通过从节点 1 到节点 4 的有向边,权重为 4 - 1 = 3。请注意,这条边是可能的,因为节点 1 的 ID 为 1,而节点 4 的 ID 为ID 3,并且 ID 1 的节点具有指向 ID 3 的所有节点的有向边。接下来,您将使用从节点 4 到节点 3 的有向边,权重为 4 - 3 = 1;那么从节点 3 到节点 5 的有向边,权重为 5 - 3 = 2. 3 + 1 + 2 = 6,因此总最短路径为 6。

我的解决方案:

当我第一次阅读这个问题时,我认为 Dijkstra 的算法可以轻松解决它。但是,它的时间复杂度为 N^2logN,边数最多为 50000^2,太高了。如何解决密集图中的最短路径问题?有没有办法通过某种方式利用矩阵来优化 Dijkstra?

【问题讨论】:

  • 所以ID——尽管它的名字——不一定是唯一的?该属性的另一个名称将是合适的......
  • “我们知道 ID 1 的节点有指向 ID 1 和 ID 3 节点的边”:这个陈述可以更明确吗?这是否意味着 ID 为 1 的所有节点都指向 ID 为 1 的所有节点和 ID 为 3 的所有节点?或者这是否意味着 ID 为 1 的一些节点有指向 ID 为 1 的一些节点和 ID 为 3 的一些节点的边?
  • 如果我正确理解了这个问题,那么更好的术语是position 1 到 N,group 1 到 M。距离是 positions 之间的绝对差。该矩阵表示groups 之间的边。问题中的路径使用positions 1,4,3,5。行进的净距离是 5-1=4,但由于从位置 4 到 3 的回溯,增加了 2 的额外距离,使总路径长度为 6。换句话说,目标是最小化回溯。 IMO,Dijkstra 根本不会提供帮助。

标签: algorithm optimization graph graph-algorithm dijkstra


【解决方案1】:

假设有 3 个具有不同标签的节点共享相同的 id 1:
ID 1 <==> Labels {1, 7, 13}

同理,有5个节点共享同一个id2:
ID 2 <==> Labels {2, 8, 12, 15}

共享相同id的3个节点3:
ID 3 <==> Labels {5, 10, 11}

和 2 个共享相同 id 的节点4:
ID 4 <==> Labels {6, 9}

现在假设我们有 4 条边:

  • ID1 -> ID2
  • ID1 -> ID3
  • ID2 -> ID4
  • ID3 -> ID4

如果我们想找出标签“7”和“9”之间的最小距离,我们会怎么做?

井标签 7 表示 ID1,标签 9 表示 ID4。这意味着我们需要找到 ID1 和 ID4 之间的最短距离。

但是由于这 2 个 id 之间没有直接的边缘,我们必须先跳转到 ID 2 或 3。

让我们计算到 ID 2 的距离:
我们应该从 ID1 跳转到 ID2 内的哪个标签?
由于距离被定义为 2 个标签值之间的绝对差,而源标签是“7”,我们应该跳到最接近 7 值的标签处。
在 {1, 2, 8, 12, 15} 中,这将是 '8'(距离 = 8-7 = 1)

让我们计算到 ID 3 的距离:
我们应该从 ID1 跳转到 ID3 内的哪个标签?
在 {5, 10, 11} 中,最接近 7 的数是 '5'(距离 = 7-5 = 2)

由于到 ID2 的距离小于 ID3,我们应该跳转到 ID2,标签 8。

现在我们在标签8,我们可以直接跳转到ID4中的标签9(距离= 9-8 = 1)

总距离 = 1 + min(1, 2) + 1 = 3

一般算法是:

  • 在 ID 和共享该 ID 的标签的排序列表之间创建一个哈希映射。
  • 如果源标签为 L1,目标标签为 L2,假设它们属于 ids I1 和 I2,执行 dijkstra 以找到 ids I1 和 I2 之间的最短距离。
  • 在执行 dijkstra 时,通过在共享该 id 的标签列表中选择值最接近 L1 的标签来计算当前标签“L1”和邻居 id 之间的“距离”(该列表可以从哈希图中访问) )。
  • 对于从 L1 到邻居 ID 的所有此类距离,将所有这些距离(连同所选标签)推送到优先级队列(与普通 dijkstra 相同)。

在已排序的数组can be done in O(logn) 中查找最接近某个值的数字,其中 n 是 排序数组的大小。

dijkstra 的算法复杂度为O(M^2 * logM * logX),其中 X = 共享单个 ID 的平均标签数,可以安全地假设为 N / M。

使用排序列表创建 hashmap 需要 O(M*X*logX) = O(N * logN/M) 复杂度。

所以总复杂度 = O((N * log(N/M)) + (M^2 * logM * log(N/M)) ~O(log(N/M) * (N + M^2)

【讨论】:

  • 创建 hashmap 需要 O(N) 的时间,但这会杀死其余的有效实现 - 您需要至少查看每个节点一次才能将其添加到 hashmap 中。
  • @Shamis 感谢您的指出。修复了复杂度计算。
【解决方案2】:

免责声明:
据我了解您的公式,边 ID1 -> ID2 意味着从 ID1 的节点到 ID2 的每个其他节点都有一条边。如果这不正确,只需忽略其余部分。现在我会假设这是事实。

观察:
由于边 ID1->ID2 意味着 ID1 的每个节点都与 ID2 的每个其他节点都有边,因此我们使用具有特定 ID 的节点并不重要 - 它们对于最短路径的目的都是等效的。
这意味着要计算从节点 Label_X, ID_I 到节点 Label_Y, ID_J 的最短路径的长度,我们可以简单地计算图上的 Djikstra,该图考虑每个标签恰好出现一次,以找到 labels。这将需要O(M * log(M^2)) 操作。

现在,由于我们有了标签的路径,我们可以简单地选择我们遇到的具有所需标签的第一个节点。由于无论如何我们都必须读取输入,我们可以简单地创建一个数组,其中包含我们遇到的每个 ID 的第一个节点。读取输入将采用~ N + M^2 操作
在这种情况下,总复杂度为O(N + M^2 + M * log(M^2))。在您的情况下,总数为50000 + 2500 + 50 * log(2500) = ~ 53100。

【讨论】:

  • 不确定最后一句话是如何毁掉任何东西的。根据 OP 提供的信息,这是您在最坏情况下将采取的步骤的近似计数。
【解决方案3】:

在具有M 节点的较小图上运行 Dijkstra 算法,而不是在具有N 节点的较大图上运行,即将每个指定的 ID 视为一个节点,并解决该图上的问题。

例如,如果节点1 指定ID 为A,而节点N 指定ID 为B,则使用下式给出的边信息求解从A 到B 的最短路径M by M 矩阵。构建路径很容易 - 只需从路径中每个“元”指定的 ID 节点中选择任何“真实”节点。

Dijkstra 算法的时间复杂度取决于用于边界集的数据结构。由于您声称它是一个“密集”图(这通常意味着边数与节点数的平方成正比),因此如果您使用二进制平均堆,则使用此方法将为您提供 O(M^2logM)。如果您使用未排序的数组,它将是O(M^2),这对于密集图会更好。

请注意,此方法仅适用于当M_ij = Y 时,指定ID 为i 的所有节点都具有到指定ID 为j 的所有节点的出边。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-20
    • 2022-01-04
    • 2013-08-08
    • 2011-06-29
    • 2016-01-24
    • 2021-04-18
    • 1970-01-01
    相关资源
    最近更新 更多