分而治之——为啥有效?

Posted

技术标签:

【中文标题】分而治之——为啥有效?【英文标题】:Divide and conquer - why does it work?分而治之——为什么有效? 【发布时间】:2013-03-17 18:17:55 【问题描述】:

我知道合并排序和快速排序等算法使用分而治之的范式,但我想知道为什么它可以降低时间复杂度...

为什么“分而治之”的算法通常比非分而治之的算法效果更好?

【问题讨论】:

why does it work 是什么意思? “降低时间复杂度..” 【参考方案1】:

分而治之的算法工作得更快,因为它们最终做的工作更少。

考虑一下二分搜索的经典分治算法:二分搜索不是通过查看N 项目来找到答案,而是最终只检查其中的Log2N。自然,当你做的工作越少,你就能完成得越快;这正是分治算法所发生的事情。

当然,结果在很大程度上取决于您的策略在划分工作方面的效果:如果划分在每一步都或多或少公平(即您将工作分成两半),您将获得完美的Log2N 速度。但是,如果划分不是完美的(例如快速排序的最坏情况,当它花费O(n^2) 对数组进行排序时,因为它在每次迭代中只消除了一个元素),那么分而治之的策略就没有帮助,因为你的算法不会减少工作量。

【讨论】:

【参考方案2】:

分而治之,因为数学支持它!

考虑一些分而治之的算法:

1) 二分搜索:该算法每次将您的输入空间减少一半。很明显,这比线性搜索要好,因为我们会避免查看很多元素。

但是好到什么程度呢?我们得到了重复(注意:这是最坏情况分析的重复):

T(n) = T(n/2) + O(1)

数学暗示T(n) = Theta(log n)。因此,这比线性搜索要好得多。

2) 合并排序: 这里我们分成(几乎)相等的两半,对两半进行排序然后合并。为什么这应该比二次方更好?这是重复:

T(n) = 2T(n/2) + O(n)

T(n) = Theta(n log n) 可以用数学方法证明(比如使用主定理)。因此 T(n) 渐近优于二次。

观察到 naive 快速排序最终给了我们最坏情况下的重复

T(n) = T(n-1) + O(n)

从数学上讲,它是二次的,在最坏的情况下,并不比冒泡排序好(渐近地说)。但是,我们可以证明,在平均情况下,快速排序是 O(n log n)。

3 选择算法:这是一种分治算法,用于查找第 k^th 大元素。这种算法是否比排序更好(甚至不是二次的),这一点并不明显。

但在数学上,它的重现(也是最坏的情况)结果是

T(n) = T(n/5) + T(7n/10 + 6) + O(n)

可以用数学方法证明T(n) = O(n),因此它比排序要好。

也许是一种常见的看待它们的方式:

您可以将算法视为树,其中每个子问题都变成当前问题的子树,并且可以用完成的工作量标记节点,然后可以将每个节点的总工作量相加。

对于二分查找,工作量是O(1)(只是比较),其中一个子树,工作量是0,所以总工作量是O(log n)(本质上是一条路径,就像我们在二叉搜索树中所做的那样)。

对于合并排序,对于具有 k 个子节点的节点,工作量为 O(k)(合并步骤)。每个级别完成的工作是 O(n) (n, n/2 + n/2, n/4 + n/4 + n/4 + n/4 等),并且有 O(log n) 个级别,并且所以归并排序是 O(n log n)。

对于快速排序,在最坏的情况下二叉树实际上是一个链表,所以完成的工作是 n+n-1 + ... + 1 = Omega(n^2)。

对于选择排序,我不知道如何将其可视化,但我相信将其视为具有 3 个孩子(n/5、7n/10 和其余)的树可能仍然有帮助。

【讨论】:

【参考方案3】:

分而治之的算法不会“通常效果更好”。它们只是工作,就像其他非分而治之的算法一样。它们不会降低排序复杂度,但与其他算法一样好。

【讨论】:

理论上是否有可能找到另一种算法来解决问题,该算法具有与快速排序相同的 O 复杂度,但不是分治法? @gongzhitaao 嗯,其他的,我需要先启动一个堆并运行,这需要时间 @JohnnyPauling make_heap 需要 O(n),基于比较的最佳排序是 O(nlogn) 那么可能是一个不错的选择,但我怀疑 heapsort 的性能比 quikcksort 差 @JohnnyPauling 您是否在输入中尝试过不同的算法?这是了解其中一个是否更快的唯一方法,因为它们具有相同的复杂性,并且在某些输入上快速排序可能更快,而在其他输入上使用堆排序。

以上是关于分而治之——为啥有效?的主要内容,如果未能解决你的问题,请参考以下文章

为啥二分搜索是一种分而治之的算法?

为啥分而治之在数组总和上没有表现出其性能优势?

为啥这个计数器以这种方式增加,而不是在这个分而治之的算法中一个一个增加?

什么是分而治之

C语言中的分而治之二分查找

如何使用分而治之的方法解决“固定大小的最大子数组”?