假设图G中的边权重均匀分布在[0,1)上。哪种算法prims或Kruskals会更快? 我认为这将是kruskals,因为我们可以利用特定的排序算法,因为排序是kruskals算法的瓶颈步骤。
答案 0 :(得分:1)
边缘权重的分布无关紧要。
Prims和Kruskals之间的主要区别在于Prim的算法在时间上与顶点数的平方成比例,而Kruskal的算法与边数和边数的对数成比例地运行。因此,Prim在密集图上更快,而Kruskal在稀疏图上更快。
例如,如果你有1000个顶点3000个边(稀疏),那么Prim将是K1 * 1,000,000而Kruskal将是K2 * 24,000。但如果你有1000个顶点和250,000个边缘(密集),那么Kruskal将是K2 * 3,100,000。
答案 1 :(得分:0)
这是你必须要做的基准测试。您可以使用花哨的数据结构(van Emde Boas树)和排序算法(一些计数排序变量)将两种算法的理论预期复杂度降低到更接近线性的值。但是,目前还不清楚任何这样的技巧都可以提高任一算法的实际性能。改善记忆局部性的神龛可能会产生更大的差异。
答案 2 :(得分:0)
更新 2 正如@David Eisenstat 在下面的评论中指出的那样,在 O(E) 时间内进行排序的一种更简单的方法是使用 |E| 进行桶排序。桶。区间[0, 1)可分为|E|长度为 1/|E| 的桶每个,编号为 0, 1, 2 ... |E|-1,并且区间中的任何权重 w 都属于桶号 k = floor(|E| w)。每个桶中的预期权重数为 O(1),因此排序可以用 |E| 完成插入类型的大小为 O(1),因此这给出了 O(E alpha(V)) 预期时间 Kruskal 算法。
注意:作为@G。 Bach 指出,上面假设权重的比较和 floor(|E| w) 浮点乘法可以在 O(1) 时间内进行,这可能需要一定程度的怀疑。对于非常大的 |E| 值,这两个操作可能仍然具有 O(lg E) 贡献。
更新 正如 G. Bach 在下面指出的那样,第一轮 O(1) 位数的基数排序后的 bin 大小将始终是 Omega(E),因此从技术上讲,以下答案不是保证在 O(E) 时间内排序。但是,有可能选择小于 O(lg E) 的位数,也许是 O(lg lg E) 或 O(sqrt lg E)?所以排序花费的时间少于 O(E lg E) 预期时间。
原始答案
这是 CLRS 中的练习 23.2-6。我很确定 Kruskal 会更快(因此这里的其他答案是错误的)。权重的分布确实很重要;无关紧要的是图的密度/稀疏性。
普通版本由排序边缘权重的 O(E lg E) 时间支配。当边缘权重从均匀分布中提取时,我们可以对某个恒定数量的数字执行基数排序,然后通过进一步的基数排序来修复连续子数组中的冲突。那是 O(E) 时间。
然后,剩下的就是普通的 Kruskal:使用具有联合秩和路径压缩的不相交集(如 CLRS 的第 23 章),剩下的工作是 O(E alpha(V)),其中 alpha(V) 是逆Ackermann 函数并且对于任何合理的 V 值 <= 4(想想难以想象的比宇宙中的原子大的数字)。
因此,对于基数排序,Kruskal 是线性 O(E),概率任意接近 1。
关于基数排序的注意事项:
可以通过使用更多位数来任意减小预期的碰撞次数(即前 15 位相同的边权重),但如果位数为 O(lg E),则为 O(1)。当然,这意味着 O(E lg E) 基数排序,这将违背目的。然而,我们实际上并不需要完全避免碰撞,只需限制它们的大小,以便它们可以在线性时间内修复。
因此,我们可以考虑在一轮基数排序中对一些恒定数量的数字(如 15)进行排序,这会将权重数组分成连续的子数组(也称为“bins”),所有子数组都具有相同的 15 位数字,然后在下一轮,使用第二“轮”基数排序对每个子数组的 16-30 位数字进行排序。
一个正式的证明将涉及与生日悖论类似的计算,但由于碰撞的概率随着使用的额外数字的数量呈指数下降,所以应该可以使用 O(1) “轮次”对上述内容进行完全排序,即将导致 O(E) 总排序时间。