Problem 1 (教材习题 9-1)
(有序序列中的 i 个最大数) 给定一个包含 n 个元素的集合,我们希望利用基于比较的算法找出按顺序排列的前 i 个最大元素。请设计能实现下列每一项要求,并且具有最佳渐近最坏情况运行时间的算法,以 n 和 i 来表示算法的运行时间:
对输入数据排序,并找出前 i 个最大数;
对输入数据建立一个最大优先队列,并调用 EXTRACT-MAX 过程 i 次。
利用一个顺序统计量算法来找到第 i 大的元素,然后用它作为主元划分输入数组,再对前 i 大的数排序。
Solution
排序需要 O(nlogn) ,列出其中前 i 大的数需要 O(i) ,总时间为 O(nlogn+i) 。
使用最大堆来建立最大优先队列需要 O(n) 的时间,每次 EXTRACT-MAX 需要 O(logn) 的时间,共调用 i 次,总时间为 O(n+ilogn) 。
通过 SELECT 算法(详见第8讲PPT第14页)找到第 i 大的元素并依此划分数组需要 O(n) ,对前 i 大的元素进行排序需要 O(ilogi) ,总时间为 O(n+ilogi) 。
Problem 2 (教材习题 9-2)
(带权中位数) 对分别具有正权重 w1,w2,⋯,wn ,且满足 i=1∑nwi=1 的 n 个互异元素 x1,x2,⋯,xn 来说,带权中位数 xk (较小中位数)是满足如下条件的元素:
xi<xk∑wi<21
和
xi>xk∑wi≤21
例如,如果元素是 0.1,0.35,0.05,0.1,0.15,0.05,0.2 ,并且每个元素的权重等于本身(即对所有 i=1,2,⋯,7 ,都有 wi=xi),那么中位数是 0.1 ,而带权中位数是 0.2 。
证明:如果对所有的 i=1,2,⋯,n ,都有 wi=1/n ,那么 x1,x2,⋯,xn 的中位数就是 xi 的带权中位数。
利用排序,设计一个最坏情况下 O(nlogn) 时间的算法,可以得到 n 个元素的带权中位数。
说明如何利用像第8讲PPT第14页 的 SELECT 这样的线性时间中位数算法,在 Θ(n) 最坏情况时间内求出带权中位数。
邮局位置问题 的定义如下:给定权重分别为 w1,w2,⋯,wn 的 n 个点 p1,p2,⋯,pn ,我们希望找到一个点 p (不一定是输入点中的一个),使得 i=1∑nwid(p,pi) 最小,这里 d(a,b) 表示点 a 与 b 之间的距离。
证明:对一维邮局位置问题,带权中位数是最好的解决方法,其中,每个点都是一个实数,点 a 与 b 之间的距离是 d(a,b)=∣a−b∣ 。
请给出二维邮局位置问题问题的最好解决方法:其中的点是 (x,y) 的二维坐标形式,点 a=(x1,y1) 与 b=(x2,y2) 之间的距离是 Manhattan 距离 ,即 d(a,b)=∣x1−x2∣+∣y1−y2∣ 。
Solution
- 若 wi=1/n ,则 x1,x2,⋯,xn 的带权中位数 xk 满足
xi<xk∑wi=xi<xk∑n1<21⇔xi<xk∑<2n≤2n−1
xi>xk∑wi=xi>xk∑n1<21⇔xi>xk∑≤2n
于是
n−1=xi<xk∑+xi>xk∑≤2n−1+2n=n−1
所以 xi<xk∑=n/2−1 , xi>xk∑=n/2 ,因此 xi 就是中位数。
首先,使用归并排序或者堆排序根据 xi 的大小对 x1,x2,⋯,xn 进行排序,排好序后的序列记为 x1′,x2′,⋯,xn′,对应权重为 w1′,w2′,⋯,wn′ 。然后计算这个排好序的数组的权重前缀和 S1,S2,⋯ ,其中 S1=w1′,Sk=Sk−1+wk′ 。
- 直到算到 Sk 使得 Sk−1<1/2 且 Sk≥1/2 为止。此时, xk′ 就是带权中位数。
首先使用 SELECT 算法计算中位数 x ,并根据 x 划分数组。分别计算 s1=xi<x∑wi 和 s2=xi>x∑wi (递归调用的时候这两个和式也是对整个数组计算),这个过程需要 O(n) 。之后,我们会遇到三种情况:
如果 s1<1/2 且 s2≤1/2 ,则 x 就是带权中位数,直接返回即可。
如果 s1≥1/2 ,则对于 x 左侧的子数组递归寻找带权中位数。
如果 s2>1/2 ,则对于 x 右侧子数组递归寻找带权中位数。
不可能 s1≥1/2 且 s2>1/2 ,因为 s1+s2<1 。
上述算法的递归式为 T(n)=T(n/2)+O(n) ,使用主定理可得, T(n)=O(n) 。
- 反证法。记 p 是最好的解决方法,假设 p 并不是 p1,p2,⋯,pn 的带权中位数。令 ε 是绝对值足够小的数,满足 ∣ε∣<i∈{k∣pk=p}min(∣p−pi∣) 。记带权中位数为 pm ,如果 p<pm ,取 ε 为正数,否则取 ε 为负数。我们有
i=1∑nwid(p+ε,pi)=i=1∑nwid(p,pi)+ε(pi<p∑wi−pi>p∑wi)<i=1∑nwid(p,pi)
其中,第 1 个等号只是单纯的绝对值展开,其中用到了 ∣ε∣ 足够小的条件 。第 2 个小于号用到了 p 和 pm 的关系:
如果 p<pm ,则 pi<p∑wi<∑pi>pwi,ε>0 ,于是 ε(pi<p∑wi−∑pi>pwi)<0 ;
如果 p>pm ,则 pi<p∑wi>∑pi>pwi,ε<0 ,于是 ε(pi<p∑wi−∑pi>pwi)<0 ;
综上,我们找到了比 p 更优的位置 p+ε ,这与 p 是最好的解决方法是矛盾的。因此,p 不是带权中位数的假设不成立, p 只能是带权中位数。
- 观察到(其中 x 下标表示点的横坐标, y 下标表示点的纵坐标):
i=1∑nwid(p,pi)=i=1∑nwi∣px−(pi)x∣+i=1∑nwi∣py−(pi)y∣
我们只需要让拆分下来的两个和式分别最小即可。根据第 4 问的结论,取 p=(px,py) 使得 px 是所有点横坐标的带权中位数, py 是所有点纵坐标的带权中位数, p 就是最好的解决方案。
Problem 3 (教材习题 9-3)
(小顺序统计量) 要在 n 个数中选出第 i 个顺序统计量, SELECT (详见第8讲PPT第14页)在最坏情况下需要的比较次数 T(n) 满足 T(n)=Θ(n) 。但是,隐含在 Θ 记号中的常数项是非常大的。当 i 相对 n 来说很小时,我们可以实现一个不同的算法,它以 SELECT 作为子程序,但在最坏情况下所做的比较次数要更少。
设计一个能用 Ui(n) 次比较在 n 个元素中找出第 i 小元素的算法。其中,
Ui(n)={T(n)⌊n/2⌋+Ui(⌈n/2⌉)+T(2i)if i≥n/2otherwise
(提示:从 ⌊n/2⌋ 个不相交对的两两比较开始,然后对由每对中的较小元素构成的集合进行递归。)
证明:如果 i<n/2 ,则有 Ui(n)=n+O(T(2i)log(n/i)) 。
证明:如果 i 是小于 n/2 的常数,则有 Ui(n)=n+O(logn) 。
证明:如果对所有 k≥2 有 i=n/k ,则 Ui(n)=n+O(T(2n/k)logk) 。
Solution
- 如果 i≥n/2 ,直接用 SELECT 算法即可,需要 T(n) 次比较。
如果 i<n/2 ,成对地比较输入元素,需要 ⌊n/2⌋ 次比较,形成了 ⌈n/2⌉ 组元素(可能有一个落单的);
由于 i<n/2 ,递归地在 ⌈n/2⌉ 个组的较小元素中寻找第 i 小的元素,并据此划分这 ⌈n/2⌉ 个组(让组内较大数作为较小数的卫星数据跟着一起移动即可),需要 Ui(⌈n/2⌉) 次比较;
整个数组的第 i 小元素一定在 ⌈n/2⌉ 个组的较小元素的前 i 个较小元素所在的组中。对于这 i 个组, 2i 个元素调用 SELECT 算法寻找第 i 小的元素,需要 T(2i) 次比较。
- 剩余的 ⌈n/2⌉−i 个组中不可能有第 i 小的元素,里面最小也只能有第 i+1 小的元素。因为前面 i 个组的 i 个较小元素已经比这些组中的所有元素都小了。
上述算法的比较次数 Ui(n) 的递归式为:
Ui(n)={T(n)⌊n/2⌋+Ui(⌈n/2⌉)+T(2i)if i≥n/2otherwise
- 使用代入法证明。假设对于更小的 n ,有 Ui(n)≤n+cT(2i)log(n/i) ,取决于是否 i<n/4 ,有两种情况:
如果 i<n/4 ,则
Ui(n)=⌊n/2⌋+Ui(⌈n/2⌉)+T(2i)≤n/2+n/2+cT(2i)log(n/2i)+T(2i)=n+cT(2i)log(n/i)−(c−1)T(2i)
其中,最后一步需要 c≥1 。
如果 n/4≤i<n/2 (此时有 n/2≤2i),则
Ui(n)=⌊n/2⌋+Ui(⌈n/2⌉)+T(2i)≤n/2+T(n/2)+T(2i)≤n/2+2T(2i)≤n/2+cT(2i)log(n/2i)
其中,最后一步需要 c≥2 。
综上,取 c=2 即可, Ui(n)≤n+cT(2i)log(n/i) ,即 Ui(n)=n+O(T(2i)log(n/i)) 。
基于第 2 问结论,如果 i 是常数, O(T(2i)log(n/i)) 就变成了 O(logn) ,于是 Ui(n)=n+O(logn) 。
基于第 2 问结论,将 i=n/k 代入,有 Ui(n)=n+O(T(2i)log(n/i))=n+O(T(2n/k)logk) 。
Problem 4 (教材习题 9-4)
(随机化选择的另一种分析方法) 在这个问题中,我们用指示器随机变量来分析 RANDOMIZED-SELECT ,这一方法类似于第6讲PPT第27页 中所用的对 RANDOMIZED-QUICKSORT 的分析方法。
与快速排序中的分析一样,我们假设所有的元素都是互异的,输入数组 A 的元素被重命名为 z1,z2,⋯,zn ,其中 zi 是第 i 小的元素。因此,调用 RANDOMIZED-SELECT(A,1,n,k) 返回 zk 。
对所有 1≤i<j≤n ,设
Xijk=I{在执行算法查找 zk 期间,zi 与 zj 进行过比较}
给出 E[Xijk] 的准确表达式。(提示:你的表达式可能有不同的值,依赖于 i,j,k 的值。)
设 Xk 表示在找到 zk 时 A 中元素的总比较次数,证明:
E[Xk]≤2(i=1∑kj=k∑nj−i+11+j=k+1∑nj−k+1j−k−1+i=1∑k−2k−i+1k−i−1)
证明: E[Xk]≤4n 。
假设 A 中的元素都是互异的,证明: RANDOMIZED-SELECT 的期望运行时间是 O(n) 。
Solution
- 我们只需要关心当我们选择一个在 min(zi,zj,zk) 和 max(zi,zj,zk) 之间的主元的情况。因为此时我们
要么选择 zi 或者 zj 作为主元,并比较它们两个;
要么选择 zi 和 zj 之间的元素作为主元,并永远不比较它们两个;
要么选择 zk 和 [zi,zj] 区间之间的元素作为主元,并永远不会再选择包含 zi 和 zj 的范围中的元素。
为了让 zi 和 zj 进行比较,应该选择 zi 或者 zj 作为主元,考虑主元选取是随机的,我们有
E(Xijk)=⎩⎪⎪⎨⎪⎪⎧j−k+12j−i+12k−i+12zk≤zi<zjzi≤zk<zjzi<zj≤zk
- E[Xk] 最多就是所有可能的元素对比较的期望之和:
E[Xk]≤1≤i<j≤n∑E[Xijk]=i=1∑kj=k∑nj−i+12+i=k+1∑n−1j=i+1∑nj−k+12+i=1∑k−2j=i+1∑k−1k−i+12=2(i=1∑kj=k∑nj−i+11+j=k+1∑nj−k+1j−k−1+i=1∑k−2k−i+1k−i−1)
其中,最后一步是一个简单的和式变换,将行优先求和转化成列优先求和,类似于第6讲PPT第29页中涉及的和式变换。
- 由 j−k+1j−k−1<1 以及 k−i+1k−i−1<1 有
j=k+1∑nj−k+1j−k−1+i=1∑k−2k−i+1k−i−1≤n−k+k−2=n−2
考虑使得 j−i+11=c1 的项,其中 c 是常数。这相当于 j−i=c−1 ,因此这样的项最多有 c 项,c 的范围是 [1,n] ,于是
i=1∑kj=k∑nj−i+12≤c=1∑nc⋅c1=n
综上, E[Xk]≤2(n+n−2)≤4n 。
- RANDOMIZED-SELECT 算法中执行次数最多的关键操作就是输入数组中元素的比较操作,根据第 3 问结果, RANDOMIZED-SELECT 过程中输入数组元素间的期望比较次数不超过 4n ,因此 RANDOMIZED-SELECT 的期望运行时间为 O(n) 。