算法:分治法和时间复杂度 O(nlogn) 有啥关系?
Posted
技术标签:
【中文标题】算法:分治法和时间复杂度 O(nlogn) 有啥关系?【英文标题】:algorithms: how do divide-and-conquer and time complexity O(nlogn) relate?算法:分治法和时间复杂度 O(nlogn) 有什么关系? 【发布时间】:2015-07-07 18:39:07 【问题描述】:在我的算法和数据结构课程中,引入了第一个 divide-and-conquer algorithm
即 merge sort
。
在为作业实施算法时,我想到了几个问题。
使用分而治之范式实现的算法是否具有 O(nlogn) 的时间复杂度?
是不是该方法中的递归部分有能力将运行在 O(n^2) 的算法浓缩为 O(nlogn)?
首先是什么让这样的算法在 O(nlogn) 中运行?
对于 (3),我假设这与递归树和可能的递归数有关。有人可能会用一个在 O(nlogn) 中运行的简单分治算法来展示,实际上是如何计算复杂性的?
干杯, 安德鲁
【问题讨论】:
你可能想看看bigocheatsheet.com 【参考方案1】:我认为您的问题的所有答案都可能来自Master Theorem 它有点告诉您对于您拥有的几乎所有分而治之的解决方案,您的复杂性是什么,是的,它必须使用递归树来做所有事情,通过玩参数你会发现一些分而治之的解决方案不会有 O(nlogn) 复杂度,实际上有divide and conquer algorithms that have O(n) complexity。
就问题2而言,并非总是如此,事实上,有些问题被认为不可能比O(n^2)更快地解决,这取决于问题的性质。
以 O(nlogn) 运行并且我认为具有非常简单、清晰和有教育意义的运行时间分析的算法示例是 MergeSort。可以从下图把握:
所以每个递归步骤将输入分成两部分,然后征服部分花费 O(n),因此树的每一层花费 O(n),棘手的部分可能是递归级别(树高)为 logn。这或多或少很简单。所以在每一步中,我们将输入分成两部分,每部分 n/2 个元素,并递归地重复,直到我们有一些恒定大小的输入。因此,在第一级,我们除以 n/2,在下一个 n/4,然后是 n/8,直到我们达到一个恒定大小的输入,这将是树的一个叶子,以及最后一个递归步骤。
所以在第 i 个递归步骤,我们将 n/2^i 相除,所以让我们在最后一步找到 i 的值。我们需要 n/2^i = O(1),这是在 2^i = cn 时实现的,对于某个常数 c,我们从两边取以 2 为底的对数,得到 i = clogn。所以最后一个递归步骤将是第 clogn 步,因此树具有 clogn 高度。
因此,对于每个 clogn 递归(树)级别,MergeSort 的总成本将是 cn,这给出了 O(nlogn) 复杂度。
一般来说,只要递归步骤具有 O(n) 复杂度,您就可以确信您的算法将具有 O(nlogn) 复杂度,并且您可以划分为 b 个大小为 n/b 的问题,甚至更一般,如果这些部分是 n 的线性分数,加起来为 n。在不同的情况下,您很可能会有不同的运行时。
回到问题 2,在 QuickSort 的情况下,一个可能从 O(n^2) 到 \Theta(nlogn) 正是因为平均随机情况实现了一个很好的分区,尽管运行时分析比这更复杂.
【讨论】:
谢谢哈维尔。你做得非常好,为我节省了数小时的思考时间。 :-) 您能解释一下“n/2^i = O(1), 这是在 2^i = cn” 时实现的吗?对我来说, O(1)=k 。所以我们有 n/(2^i)=k 所以 k*(2^i)=n。这是否意味着您的 c 等于(对于我的 k) 1/k ?【参考方案2】:不,分而治之不能保证 O(nlogn) 性能。这完全取决于每次递归如何简化问题。
在归并排序算法中,原问题分为两半。然后对结果执行 O(n) 操作。这就是 O(n...) 的来源。
这两个子操作中的每一个现在都有自己的n
,它的大小是原来的一半。每次你递归时,你又把问题一分为二。这意味着递归数将为 log2(n)。这就是 O(...logn) 的来源。
【讨论】:
你刚刚是通过Master Theorem挥手吗? @Degustaf 我只是想用最简单的术语解释我自己对这个过程的理解,以提供一些见解。【参考方案3】:使用分而治之范式实现的算法是否具有 O(nlogn) 的时间复杂度?
平均而言,Quicksort 和 Mergesort 的时间复杂度为 O(n log(n)),但不一定总是这样。 Big O Cheat Sheet
是不是该方法中的递归部分有能力将运行在 O(n^2) 到 O(nlogn) 的算法浓缩?
不仅仅是表面上看到的,它还取决于其他因素,例如每个递归调用与输入相关的操作数。
我强烈推荐这个video,在那里你可以看到为什么 MergeSort 是 O(n log(n))。
是什么让这样的算法首先在 O(nlogn) 中运行。
同样,这只是一个算法消耗多少时间与输入大小相关的指标,因此说算法的时间复杂度为 O (n log (n)) 并没有提供任何信息关于算法是如何实现的,它只是说当输入开始增加很多时,使用的时间不会成正比增加,而是需要更多的时间和更多。
【讨论】:
【参考方案4】:使用分而治之范式实现的算法是否具有 O(nlogn) 的时间复杂度?
不,分而治之的一般公式是:
2是每个递归调用内部的操作数,是除以子问题的递归调用,是征服的线性操作数
是什么让这样的算法首先在 O(nlogn) 中运行?
对数线性时间的一个很好的例子是合并排序算法m:
方法中的递归部分是否有能力将运行在 O(n^2) 到 O(nlogn) 的算法浓缩?
主定理用于确定分治算法的运行时间
如果重复是这种形式
那么
示例
让
a = 2
b = 4
d = 1/2
因为 2 = 4 ^ 1/2 情况 2 适用
【讨论】:
以上是关于算法:分治法和时间复杂度 O(nlogn) 有啥关系?的主要内容,如果未能解决你的问题,请参考以下文章