在最坏的情况下,这种分类算法是Θ(n³)而不是Θ(n²)。


52

我刚开始学习数据结构和算法课程,我的助教为我们提供了以下用于对整数数组进行排序的伪代码:

void F3() {
    for (int i = 1; i < n; i++) {
        if (A[i-1] > A[i]) {
            swap(i-1, i)
            i = 0
        }
    }
}

可能还不清楚,但是这里是我们要排序的数组的大小。nA

无论如何,助教都会向全班解释该算法的时间是(我认为是最坏的情况),但是无论我用反向排列的数组遍历多少次,在我看来,它应该是而不是。Θ n 2Θ n 3Θ(n3)Θ(n2)Θ(n3)

有人可以向我解释为什么这是Θ(n3)而不是Θ(n2)吗?


您可能对结构化的分析方法感兴趣;尝试自己找到证明!
拉斐尔

只需实施它并采取措施说服自己。反向具有10,000个元素的数组应该花费很多时间,而反向具有20,000个元素的数组应该花费大约八倍的时间。
gnasher729

@ gnasher729你没有错,但是我的解决方案不同的是:如果你试图证明你的约束,你总是会失败,它会告诉你有些事情不对劲。(当然,两者都可以做。绘制/拟合无疑可以更快地拒绝假设,但可靠性较差。只要您进行某种形式的结构/结构分析,就不会造成危害。依靠绘图是麻烦的开始。)O(n2)
拉斐尔

1
由于该i = 0声明
njzk2

Answers:


61

这个算法可以这样重写

  1. 扫描A直到找到一个反转
  2. 如果找到一个,请交换并重新开始。
  3. 如果不存在,请终止。

现在反演中最多可以有并且您需要进行线性时间扫描才能找到它们-因此,最坏的运行时间是。一个美丽的教学示例,因为它跳入了许多模式匹配方法!(n2)Θ(n2)Θ(n3)

注意:必须稍微小心一点:有些反转出现得早,有些则出现得晚,因此,成本加起来(对于下限)本身并不容易。您还需要注意,交换从未引入新的反转。然后,使用逆序排列的数组对情况进行更详细的分析将得出类似高斯公式的二次情况。

作为@ gnasher729恰当地注释,可以很容易地看到在最坏情况下运行时间通过分析排序该输入时的运行时间(尽管该输入可能不是最坏的情况下)。Ω(n3)[1,2,,n,2n,2n1,,n+1]

注意:不要假设反向排序的数组一定是所有排序算法的最坏情况输入。这取决于算法。在某些排序算法中,反向排序的数组不是最坏的情况,甚至可能接近最好的情况。


14
如果您采用一个数组,其中前半部分由数字1到n / 2以升序组成,而后半部分是n到n / 2 + 1颠倒顺序,那么很显然您至少需要n / 2步骤来查找每个反演,其中大约(n / 2)^ 2/2。那很可能不是最坏的情况。
gnasher729

@AnthonyRossello这是标准结果(排列组合)。简而言之,计算反向排序数组中的反转次数(很明显那是最坏的情况吗?);这是高斯的总和。
拉斐尔

必须记住,无论如何,部分和总是,只是系数迅速下降:(请注意相当大的系数)。问题是,不在乎系数。Θ(nα)Θ(nα+1)k=0nkα1α+1nα+11α+1Θ
2017年

2
@yo'这与答案(或问题)如何相关?
拉斐尔

7

关于此的另一种思考方式i是重置前的最大值。事实证明,这使得人们可以更直接地推断出先前的排序顺序如何A影响算法的运行时间。

特别是,请注意,当i设置其新的最大值时,我们将其称为N,该数组[A[0], ..., A[N-1]]将按升序排序。

那么,当我们将元素添加A[N]到混合中时会发生什么呢?

数学:

好吧,可以说它适合于位置。然后,我们需要循环迭代(我将表示)将其移至,而次迭代将其移至,通常:pNNstepsN1N+(N1)N2

stepsN(pN)=N+(N1)+(N2)++(pN+1)=12(N(N+1)pN(pN+1))

对于随机排序的数组,对于每个,在上取均匀分布,其中:pN{0,1,,N}N

E(stepsN(pN))=a=1NP(pN=a)stepsN(a)=a=1N1N12(N(N+1)a(a+1))=12(N(N+1)13(N+1)(N+2))=13(N21)=Θ(N2)

可以使用Faulhaber公式或底部的Wolfram Alpha链接显示总和。

对于逆排序的数组,对于所有,,我们得到:pN=0N

stepsN(pN)=12N(N+1)

确切地讲,其任何其他值。pN

对于已经排序的数组,且,其中低阶项变得有意义。pN=NstepsN(pN)=0

总时间:

为了获得总时间,我们总结了所有上的步骤。(如果我们非常小心,我们将对交换以及循环迭代进行总结,并注意开始条件和结束条件,但是很容易看出它们在大多数情况下不会增加复杂性) 。N

再一次,使用期望的线性度和福哈伯公式:

Expected Total Steps=E(N=1nstepsN(pN))=N=1nE(stepsN(pN))=Θ(n3)

当然,如果由于某种原因不是(例如,我们正在查看的数组的分布已经非常接近排序),则不必总是这样做就是这样。但是要达到此目的,需要在上进行非常具体的分配!stepsN(pN)Θ(N2)pN

相关阅读:


@Raphael-感谢您提出的改进建议,我添加了更多细节。好了,随机变量是(来自,从的有序集),因此期望从技术上对进行了预期piΩAΩ
David E

不同的 ; 我的意思是兰道。Ω
拉斐尔

3

免责声明:

这不是证据(似乎有些人认为我将其发布为已发布)。OP可以执行这只是一个小实验,以解决他或她对作业的怀疑:

无论我用反向排序的数组遍历它多少次,在我看来,它都应该是而不是。Θ(n2)Θ(n3)

使用这样一个简单的代码,和之间的差异就不难发现,在许多实际情况下,这是检查预感或调整期望值的有用方法。Θ(n2)Θ(n3)


@Raphael已经回答了您的问题,但是仅是踢球,使用此gnuplot脚本将该程序的输出拟合为报告了和指数值,并生成了以下图(第一个是正常比例,第二个是对数-对数比例):f(x)=axb+cx2.997961668332222.99223727692339

正常 日志

希望这对帮助¨


2
您可以使任何函数适合这些值。另请参阅此处
拉斐尔

3
@Raphael如果您不想用这种方式来伪装,那就不行,您将无法安装任何函数(例如,您将无法以任何合理的精度安装常数函数)。这不是证明,但是已经有一个提供草图的答案。至于有用性,我可以引用您自己链接的帖子:“我必须同意这是一种非常有用的方法,有时甚至没有充分利用”。而且,OP说他认为应该是而不是,那么为什么不做实验,看看他的直觉是否正确?Θ(n2)Θ(n3)
dtldarek '17

2
这提供了算法为证据,但问题是为什么。它要求对此现象做出解释,而不是对其进行确认。Θ(n3)
David Richerby

2
@DavidRicherby这是否意味着这个答案没有用?
dtldarek

3
@Magicsowon这是一个问答网站,而不是论坛。我们正在寻找问题的答案,而不是围绕它的讨论。
David Richerby

3

假设您有一个数组。

array a[10] = {10,8,9,6,7,4,5,2,3,0,1}

您的算法执行以下操作

Scan(1) - Swap (10,8) => {8,10,9,6,7,4,5,2,3,0,1}  //keep looking at "10"
Scan(2) - Swap (10,9) => {8,9,10,6,7,4,5,2,3,0,1}
...
Scan(10) - Swap(10,1) => {8,9,6,7,4,5,2,3,0,1,10}

基本上,它会移动到数组最高元素的末尾,并且这样做会在每次扫描时重新开始,从而有效地O(n^2)移动了该元素。但是,有n个元素,因此我们必须重复n一次。这不是正式的证明,但可以“非正式”的方式帮助您理解运行时间为何O(n^3)


4
与其他答案相比,这还有什么呢?已经给出了该算法的功能的解释,并且您对运行时的推理充其量不过是粗略的。(最坏的情况不是线性的!)
拉斐尔

2
有时,以多种方式(用形式主义;用一个简单的例子来“直觉化”)解释同一个想法很有用,特别是当有人问这个问题对本领域来说是新事物时。因此,在我看来,这增加了以有助于直觉的方式呈现。
DW

因为我收到了对我的评论的答复(请不要这样做!):“最坏情况不会线性表现!” -我的意思是最差情况算子的代数性质。粗略地说,您使用的是WorstCase(1 + ... + n)“ =” WorstCase(1)+ ... + WorstCase(n),但此标识不成立。
拉斐尔

1
我是该领域的新手,并提供一个带有具体说明的具体示例的说明,绝对可以帮助我获得对该问题的直觉。现在,接受的解决方案对我来说更有意义。
vaer-k

0

逻辑似乎是按升序对数组中的元素进行排序。

假设最小数字在数组(a [n])的末尾。为了使其到达正确的位置-需要(n +(n-1)+(n-2)+ ... 3 + 2 + 1)个操作。= O(n2)。

对于数组中的单个元素,需要O(n2)ops。因此,对于元素,它是O(n3)。


5
与其他答案相比,这又是什么呢?已经给出了该算法的功能的解释,并且您对运行时的推理充其量不过是粗略的。(最坏的情况不是线性的!)
拉斐尔

很好的解释。这为问题提供了一个不同的,更直观的观点,其他答案未对此进行解释。(更不用说简短和易于理解了。)
2501年

1
@ 2501不,这是错误的。尝试对Dijkstra的算法使用这种“直觉”,将会得到二次运行时间(以节点数为单位),这是错误的。
拉斐尔

@Raphael不,是的,如答案中所述。此说明适用于此算法,不适用于其他算法。尽管对他们来说可能是错误的,但这一主张并不能证明这对他们是错误的。
2501年

@Raphael我不理解接受的答案中的解释。因此,我解决了这个问题,并尝试以简单的术语来解释它,而没有任何技术术语。.所以,这是针对像我这样无法理解公认答案的成员。
mk..17.18.17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.