笔记
7.2节提到,快速排序的平均情况时间复杂度为Θ(nlgn)Θ(n{\rm lg}n)Θ(nlgn)。然而实际应用中,输入数据有可能并不是完全随机排列的。比如,练习7.2-4描述的应用,需要排序的绝大多数输入数组都已经是基本有序的,在这种应用中,快速排序的效率总是十分低下。
为了避免在某些应用中,由于输入数据并不是完全随机排列的,它们可能具备或大致上具备某种规律性,从而导致快速排序总是效率低下,可以通过在算法中引入随机性,使得快速排序算法在这些应用中也能得到较好的期望性能。即每次调用PARTITION(A, p, r)时,随机选择一个元素作为划分主元,而不是固定选择A[r]A[r]A[r]。
对于上文提到的输入数组总是基本有序的情况,随机化版本的快速排序算法也可以得到较好的平均时间复杂度。
练习
7.3-1 为什么我们分析随机化算法的期望运行时间,而不是其最坏运行时间呢?
解
对于相同的输入,随机化算法每次运行的时间都各不相同。因此即使对于相同的输入,我们也可以认为算法的运行时间是一个随机变量。而对于一个随机变量,我们通常关注它的期望值。
我们根本无法预测随机化算法在什么时候会出现最坏情况运行时间,因此分析最坏运时间意义不大。
7.3-2 在RANDOMIZED-QUICKSORT的运行过程中,在最坏情况下,随机数生成器RANDOM被调用了多少次?在最好情况下呢?以ΘΘΘ符号的形式给出你的答案。
解
在RANDOMIZED-QUICKSORT的最坏情况下,对于一个包含nnn个元素的数组,划分得到的两个子数组的大小分别为n−1n-1n−1和000。其中,大小为n−1n-1n−1的子数组会进一步划分。因此,划分过程一共被调用了n−1n-1n−1次,那么随机数生成器也一共被调用了n−1n-1n−1次,即Θ(n)Θ(n)Θ(n)。
在RANDOMIZED-QUICKSORT的最好情况下,每次划分都是完全平衡的划分,即划分得到的222个子数组的规模分别为⌊(n−1)/2⌋⌊(n-1)/2⌋⌊(n−1)/2⌋和⌈(n−1)/2⌉⌈(n-1)/2⌉⌈(n−1)/2⌉。于是可以得到调用划分过程的次数,也即调用随机数生成器的次数的递归式为
有经验的读者应该一眼能看出这个递归不等式的解为T(n)=Ω(n)T(n) = Ω(n)T(n)=Ω(n)。如果要严格证明,我们需要用代入法去验证这个递归不等式。应用代入法时应当假设T(n)≥cn−dT(n) ≥ cn-dT(n)≥cn−d,其中c>0c > 0c>0并且d>0d > 0d>0。
我们先考察初始情况,初始情况为n=0n = 0n=0和n=1n = 1n=1。当n=0n = 0n=0时,有T(0)=0T(0) = 0T(0)=0,即空的子数组是不需要进行划分的。根据我们对T(n)T(n)T(n)的假设,有T(0)=0≥−dT(0) = 0 ≥ -dT(0)=0≥−d,这个不等式显而易见是成立的 (因为我们已经假设了d>0d > 0d>0)。
当n=1n = 1n=1时,有T(1)=0T(1) = 0T(1)=0,即单个元素的子数组也是不需要进行划分的。根据我们对T(n)T(n)T(n)的假设,有T(1)=0≥c−dT(1) = 0 ≥ c-dT(1)=0≥c−d,显然当d≥cd ≥ cd≥c时,这个不等式是成立的。即只要满足不等式d≥c{\color {red} d ≥ c}d≥c,那么T(n)≥cn−dT(n) ≥ cn-dT(n)≥cn−d对n=1n = 1n=1一定成立。
下面进入数学归纳过程。假设T(n)≥cn−dT(n) ≥ cn-dT(n)≥cn−d对0,1,…,n−10, 1, …, n-10,1,…,n−1都成立。于是有
为了让T(n)≥cn−dT(n) ≥ cn-dT(n)≥cn−d成立,我们令cn−2c−2d+1≥cn−dcn-2c-2d+1≥cn-dcn−2c−2d+1≥cn−d,可以得到d≤1−2cd ≤ 1-2cd≤1−2c。这说明只要满足不等式d≤1−2c{\color {red} d ≤ 1-2c}d≤1−2c,就可以使得T(n)≥cn−dT(n) ≥ cn-dT(n)≥cn−d成立。
综合初始情况和归纳过程,可以得到只要满足不等式0<c≤d≤1−2c{\color {red} 0 < c ≤ d ≤ 1-2c}0<c≤d≤1−2c,就可以使得T(n)≥cn−dT(n) ≥ cn-dT(n)≥cn−d对所有nnn的取值都成立。比如我们可以取c=1/3c = 1/3c=1/3并且d=1/3d = 1/3d=1/3,此时不等式0<c≤d≤1−2c0 < c ≤ d ≤ 1-2c0<c≤d≤1−2c是成立的,于是可以断言
以上只得到了T(n)T(n)T(n)的下限。而T(n)T(n)T(n)的上限是显而易见的,即T(n)≤nT(n) ≤ nT(n)≤n,因为数组一共有nnn个元素,每个元素至多被选为划分主元一次。综上所述,T(n)=Θ(n)T(n) = Θ(n)T(n)=Θ(n)。即在RANDOMIZED-QUICKSORT的最好情况下,随机数生成器被调用的次数也为Θ(n)Θ(n)Θ(n)。
这里再多提一下,为什么初始情况要考察n=0n = 0n=0和n=1n = 1n=1两项?首先我们要证明的不等式T(n)≥cn−dT(n) ≥ cn-dT(n)≥cn−d,nnn的取值范围是n≥0n ≥ 0n≥0,因此初始情况必须包含n=0n = 0n=0。而递归不等式T(n)≥2T(⌊(n−1)/2⌋)+1T(n)≥2T(⌊(n-1)/2⌋)+1T(n)≥2T(⌊(n−1)/2⌋)+1,nnn的取值范围是n≥2n ≥ 2n≥2,因为将n=1n = 1n=1代入这个不等式得到T(1)≥2T(0)+1=1T(1) ≥ 2T(0) + 1 = 1T(1)≥2T(0)+1=1,这显然是不成立的 (因为T(1)T(1)T(1)等于000)。所以进入数学归纳过程必须将n=1n = 1n=1排除在外,而应当从n=2n = 2n=2开始归纳,故n=1n = 1n=1就被列入初始情况。