笔记
7.2节提到,快速排序的平均情况时间复杂度为
Θ
(
n
l
g
n
)
Θ(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的最坏情况下,对于一个包含
n
n
n个元素的数组,划分得到的两个子数组的大小分别为
n
−
1
n-1
n−1和
0
0
0。其中,大小为
n
−
1
n-1
n−1的子数组会进一步划分。因此,划分过程一共被调用了
n
−
1
n-1
n−1次,那么随机数生成器也一共被调用了
n
−
1
n-1
n−1次,即
Θ
(
n
)
Θ(n)
Θ(n)。
在RANDOMIZED-QUICKSORT的最好情况下,每次划分都是完全平衡的划分,即划分得到的
2
2
2个子数组的规模分别为
⌊
(
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
)
≥
c
n
−
d
T(n) ≥ cn-d
T(n)≥cn−d,其中
c
>
0
c > 0
c>0并且
d
>
0
d > 0
d>0。
我们先考察初始情况,初始情况为
n
=
0
n = 0
n=0和
n
=
1
n = 1
n=1。当
n
=
0
n = 0
n=0时,有
T
(
0
)
=
0
T(0) = 0
T(0)=0,即空的子数组是不需要进行划分的。根据我们对
T
(
n
)
T(n)
T(n)的假设,有
T
(
0
)
=
0
≥
−
d
T(0) = 0 ≥ -d
T(0)=0≥−d,这个不等式显而易见是成立的 (因为我们已经假设了
d
>
0
d > 0
d>0)。
当
n
=
1
n = 1
n=1时,有
T
(
1
)
=
0
T(1) = 0
T(1)=0,即单个元素的子数组也是不需要进行划分的。根据我们对
T
(
n
)
T(n)
T(n)的假设,有
T
(
1
)
=
0
≥
c
−
d
T(1) = 0 ≥ c-d
T(1)=0≥c−d,显然当
d
≥
c
d ≥ c
d≥c时,这个不等式是成立的。即只要满足不等式
d
≥
c
{\color {red} d ≥ c}
d≥c,那么
T
(
n
)
≥
c
n
−
d
T(n) ≥ cn-d
T(n)≥cn−d对
n
=
1
n = 1
n=1一定成立。
下面进入数学归纳过程。假设
T
(
n
)
≥
c
n
−
d
T(n) ≥ cn-d
T(n)≥cn−d对
0
,
1
,
…
,
n
−
1
0, 1, …, n-1
0,1,…,n−1都成立。于是有
为了让
T
(
n
)
≥
c
n
−
d
T(n) ≥ cn-d
T(n)≥cn−d成立,我们令
c
n
−
2
c
−
2
d
+
1
≥
c
n
−
d
cn-2c-2d+1≥cn-d
cn−2c−2d+1≥cn−d,可以得到
d
≤
1
−
2
c
d ≤ 1-2c
d≤1−2c。这说明只要满足不等式
d
≤
1
−
2
c
{\color {red} d ≤ 1-2c}
d≤1−2c,就可以使得
T
(
n
)
≥
c
n
−
d
T(n) ≥ cn-d
T(n)≥cn−d成立。
综合初始情况和归纳过程,可以得到只要满足不等式
0
<
c
≤
d
≤
1
−
2
c
{\color {red} 0 < c ≤ d ≤ 1-2c}
0<c≤d≤1−2c,就可以使得
T
(
n
)
≥
c
n
−
d
T(n) ≥ cn-d
T(n)≥cn−d对所有
n
n
n的取值都成立。比如我们可以取
c
=
1
/
3
c = 1/3
c=1/3并且
d
=
1
/
3
d = 1/3
d=1/3,此时不等式
0
<
c
≤
d
≤
1
−
2
c
0 < c ≤ d ≤ 1-2c
0<c≤d≤1−2c是成立的,于是可以断言
以上只得到了
T
(
n
)
T(n)
T(n)的下限。而
T
(
n
)
T(n)
T(n)的上限是显而易见的,即
T
(
n
)
≤
n
T(n) ≤ n
T(n)≤n,因为数组一共有
n
n
n个元素,每个元素至多被选为划分主元一次。综上所述,
T
(
n
)
=
Θ
(
n
)
T(n) = Θ(n)
T(n)=Θ(n)。即在RANDOMIZED-QUICKSORT的最好情况下,随机数生成器被调用的次数也为
Θ
(
n
)
Θ(n)
Θ(n)。
这里再多提一下,为什么初始情况要考察
n
=
0
n = 0
n=0和
n
=
1
n = 1
n=1两项?首先我们要证明的不等式
T
(
n
)
≥
c
n
−
d
T(n) ≥ cn-d
T(n)≥cn−d,
n
n
n的取值范围是
n
≥
0
n ≥ 0
n≥0,因此初始情况必须包含
n
=
0
n = 0
n=0。而递归不等式
T
(
n
)
≥
2
T
(
⌊
(
n
−
1
)
/
2
⌋
)
+
1
T(n)≥2T(⌊(n-1)/2⌋)+1
T(n)≥2T(⌊(n−1)/2⌋)+1,
n
n
n的取值范围是
n
≥
2
n ≥ 2
n≥2,因为将
n
=
1
n = 1
n=1代入这个不等式得到
T
(
1
)
≥
2
T
(
0
)
+
1
=
1
T(1) ≥ 2T(0) + 1 = 1
T(1)≥2T(0)+1=1,这显然是不成立的 (因为
T
(
1
)
T(1)
T(1)等于
0
0
0)。所以进入数学归纳过程必须将
n
=
1
n = 1
n=1排除在外,而应当从
n
=
2
n = 2
n=2开始归纳,故
n
=
1
n = 1
n=1就被列入初始情况。