作业 8 解答

Problem 1 (教材习题 9-1)

(有序序列中的 ii 个最大数) 给定一个包含 nn 个元素的集合,我们希望利用基于比较的算法找出按顺序排列的前 ii 个最大元素。请设计能实现下列每一项要求,并且具有最佳渐近最坏情况运行时间的算法,以 nnii 来表示算法的运行时间:

  1. 对输入数据排序,并找出前 ii 个最大数;

  2. 对输入数据建立一个最大优先队列,并调用 EXTRACT-MAX 过程 ii 次。

  3. 利用一个顺序统计量算法来找到第 ii 大的元素,然后用它作为主元划分输入数组,再对前 ii 大的数排序。

Solution
  1. 排序需要 O(nlogn)O(n\log n) ,列出其中前 ii 大的数需要 O(i)O(i) ,总时间为 O(nlogn+i)O(n\log n + i)

  2. 使用最大堆来建立最大优先队列需要 O(n)O(n) 的时间,每次 EXTRACT-MAX 需要 O(logn)O(\log n) 的时间,共调用 ii 次,总时间为 O(n+ilogn)O(n + i\log n)

  3. 通过 SELECT 算法(详见第8讲PPT第14页)找到第 ii 大的元素并依此划分数组需要 O(n)O(n) ,对前 ii 大的元素进行排序需要 O(ilogi)O(i\log i) ,总时间为 O(n+ilogi)O(n + i\log i)

Problem 2 (教材习题 9-2)

(带权中位数) 对分别具有正权重 w1,w2,,wnw_1, w_2, \cdots, w_n ,且满足 i=1nwi=1\sum\limits_{i=1}^{n} w_i = 1nn 个互异元素 x1,x2,,xnx_1, x_2, \cdots , x_n 来说,带权中位数 xkx_k (较小中位数)是满足如下条件的元素:

xi<xkwi<12\sum_{x_i < x_k} w_i < \frac{1}{2}

xi>xkwi12\sum_{x_i > x_k} w_i \le \frac{1}{2}

例如,如果元素是 0.1,0.35,0.05,0.1,0.15,0.05,0.20.1, 0.35, 0.05, 0.1, 0.15, 0.05, 0.2 ,并且每个元素的权重等于本身(即对所有 i=1,2,,7i = 1, 2, \cdots, 7 ,都有 wi=xiw_i = x_i),那么中位数是 0.10.1 ,而带权中位数是 0.20.2

  1. 证明:如果对所有的 i=1,2,,ni = 1, 2, \cdots, n ,都有 wi=1/nw_i = 1 / n ,那么 x1,x2,,xnx_1, x_2, \cdots, x_n 的中位数就是 xix_i 的带权中位数。

  2. 利用排序,设计一个最坏情况下 O(nlogn)O(n\log n) 时间的算法,可以得到 nn 个元素的带权中位数。

  3. 说明如何利用像第8讲PPT第14页 的 SELECT 这样的线性时间中位数算法,在 Θ(n)\Theta(n) 最坏情况时间内求出带权中位数。

邮局位置问题 的定义如下:给定权重分别为 w1,w2,,wnw_1, w_2, \cdots, w_nnn 个点 p1,p2,,pnp_1, p_2, \cdots, p_n ,我们希望找到一个点 pp (不一定是输入点中的一个),使得 i=1nwid(p,pi)\sum\limits_{i=1}^{n}w_i d(p, p_i) 最小,这里 d(a,b)d(a, b) 表示点 aabb 之间的距离。

  1. 证明:对一维邮局位置问题,带权中位数是最好的解决方法,其中,每个点都是一个实数,点 aabb 之间的距离是 d(a,b)=abd(a, b) = |a-b|

  2. 请给出二维邮局位置问题问题的最好解决方法:其中的点是 (x,y)(x, y) 的二维坐标形式,点 a=(x1,y1)a=(x_1, y_1)b=(x2,y2)b = (x_2, y_2) 之间的距离是 Manhattan 距离 ,即 d(a,b)=x1x2+y1y2d(a, b) = |x_1 - x_2| + |y_1 - y_2|

Solution
  1. wi=1/nw_i = 1 / n ,则 x1,x2,,xnx_1, x_2, \cdots , x_n 的带权中位数 xkx_k 满足

xi<xkwi=xi<xk1n<12xi<xk<n2n21\sum_{x_i < x_k} w_i = \sum_{x_i < x_k} \frac{1}{n} < \frac{1}{2} \Leftrightarrow \sum_{x_i < x_k} < \frac{n}{2} \le \frac{n}{2} - 1

xi>xkwi=xi>xk1n<12xi>xkn2\sum_{x_i > x_k} w_i = \sum_{x_i > x_k} \frac{1}{n} < \frac{1}{2} \Leftrightarrow \sum_{x_i > x_k} \le \frac{n}{2}

于是

n1=xi<xk+xi>xkn21+n2=n1n - 1 = \sum_{x_i < x_k} + \sum_{x_i > x_k} \le \frac{n}{2} - 1 + \frac{n}{2} = n - 1

所以 xi<xk=n/21\sum\limits_{x_i < x_k} = n/2 - 1xi>xk=n/2\sum\limits_{x_i > x_k} = n/2 ,因此 xix_i 就是中位数。

  1. 首先,使用归并排序或者堆排序根据 xix_i 的大小对 x1,x2,,xnx_1, x_2, \cdots ,x_n 进行排序,排好序后的序列记为 x1,x2,,xnx&#x27;_1, x&#x27;_2, \cdots ,x&#x27;_n,对应权重为 w1,w2,,wnw&#x27;_1, w&#x27;_2, \cdots ,w&#x27;_n 。然后计算这个排好序的数组的权重前缀和 S1,S2,S_1, S_2, \cdots ,其中 S1=w1,Sk=Sk1+wkS_1 = w&#x27;1, S_k = S{k-1} + w&#x27;_k

    • 直到算到 SkS_k 使得 Sk1<1/2S_{k-1} < 1/2Sk1/2S_k \ge 1/2 为止。此时, xkx&#x27;_k 就是带权中位数。
  2. 首先使用 SELECT 算法计算中位数 xx ,并根据 xx 划分数组。分别计算 s1=xi<xwis_1 = \sum\limits_{x_i < x}w_is2=xi>xwis_2 = \sum\limits_{x_i > x}w_i (递归调用的时候这两个和式也是对整个数组计算),这个过程需要 O(n)O(n) 。之后,我们会遇到三种情况:

  • 如果 s1<1/2s_1 < 1/2s21/2s_2 \le 1/2 ,则 xx 就是带权中位数,直接返回即可。

  • 如果 s11/2s_1 \ge 1/2 ,则对于 xx 左侧的子数组递归寻找带权中位数。

  • 如果 s2>1/2s_2 > 1 / 2 ,则对于 xx 右侧子数组递归寻找带权中位数。

  • 不可能 s11/2s_1 \ge 1/2s2>1/2s_2 > 1/2 ,因为 s1+s2<1s_1 + s_2 < 1

上述算法的递归式为 T(n)=T(n/2)+O(n)T(n) = T(n/2) + O(n) ,使用主定理可得, T(n)=O(n)T(n) = O(n)

  1. 反证法。记 pp 是最好的解决方法,假设 pp 并不是 p1,p2,,pnp_1, p_2, \cdots, p_n 的带权中位数。令 ε\varepsilon 是绝对值足够小的数,满足 ε<mini{kpkp}(ppi)|\varepsilon| < \min\limits_{i \in {k | p_k\ne p}}(|p - p_i|) 。记带权中位数为 pmp_m ,如果 p<pmp < p_m ,取 ε\varepsilon 为正数,否则取 ε\varepsilon 为负数。我们有

i=1nwid(p+ε,pi)=i=1nwid(p,pi)+ε(pi<pwipi>pwi)<i=1nwid(p,pi)\sum_{i=1}^{n}w_id(p+\varepsilon,p_i) = \sum_{i=1}^{n}w_id(p,p_i) + \varepsilon(\sum_{p_i < p}w_i-\sum_{p_i>p}w_i) < \sum_{i=1}^{n}w_id(p,p_i)

其中,第 1 个等号只是单纯的绝对值展开,其中用到了 ε|\varepsilon| 足够小的条件 。第 2 个小于号用到了 pppmp_m 的关系:

  • 如果 p<pmp < p_m ,则 pi<pwi<pi>pwi,ε>0\sum\limits_{p_i < p}w_i < \sum_{p_i>p}w_i, \varepsilon > 0 ,于是 ε(pi<pwipi>pwi)<0\varepsilon(\sum\limits_{p_i < p}w_i - \sum_{p_i>p}w_i) < 0

  • 如果 p>pmp > p_m ,则 pi<pwi>pi>pwi,ε<0\sum\limits_{p_i < p}w_i > \sum_{p_i>p}w_i, \varepsilon < 0 ,于是 ε(pi<pwipi>pwi)<0\varepsilon(\sum\limits_{p_i < p}w_i - \sum_{p_i>p}w_i) < 0

综上,我们找到了比 pp 更优的位置 p+εp + \varepsilon ,这与 pp 是最好的解决方法是矛盾的。因此,pp 不是带权中位数的假设不成立, pp 只能是带权中位数。

  1. 观察到(其中 xx 下标表示点的横坐标, yy 下标表示点的纵坐标):

i=1nwid(p,pi)=i=1nwipx(pi)x+i=1nwipy(pi)y\sum_{i=1}^{n}w_id(p,p_i) = \sum_{i=1}^{n}w_i|p_x - (p_i)x| + \sum{i=1}^{n}w_i|p_y - (p_i)_y|

我们只需要让拆分下来的两个和式分别最小即可。根据第 4 问的结论,取 p=(px,py)p = (p_x, p_y) 使得 pxp_x 是所有点横坐标的带权中位数, pyp_y 是所有点纵坐标的带权中位数, pp 就是最好的解决方案。

Problem 3 (教材习题 9-3)

(小顺序统计量) 要在 nn 个数中选出第 ii 个顺序统计量, SELECT (详见第8讲PPT第14页)在最坏情况下需要的比较次数 T(n)T(n) 满足 T(n)=Θ(n)T(n) = \Theta(n) 。但是,隐含在 Θ\Theta 记号中的常数项是非常大的。当 ii 相对 nn 来说很小时,我们可以实现一个不同的算法,它以 SELECT 作为子程序,但在最坏情况下所做的比较次数要更少。

  1. 设计一个能用 Ui(n)U_i(n) 次比较在 nn 个元素中找出第 ii 小元素的算法。其中,
    Ui(n)={T(n)if in/2n/2+Ui(n/2)+T(2i)otherwiseU_i(n) = \begin{cases}
    T(n) & \text{if } i \ge n / 2\
    \lfloor n/2\rfloor + U_i(\lceil n/2 \rceil) + T(2i) & \text{otherwise}
    \end{cases}

    (提示:从 n/2\lfloor n/2 \rfloor 个不相交对的两两比较开始,然后对由每对中的较小元素构成的集合进行递归。)

  2. 证明:如果 i<n/2i < n/2 ,则有 Ui(n)=n+O(T(2i)log(n/i))U_i(n) = n + O(T(2i)\log(n/i))

  3. 证明:如果 ii 是小于 n/2n / 2 的常数,则有 Ui(n)=n+O(logn)U_i(n) = n + O(\log n)

  4. 证明:如果对所有 k2k \ge 2i=n/ki = n/k ,则 Ui(n)=n+O(T(2n/k)logk)U_i(n) = n + O(T(2n/k)\log k)

Solution
  1. 如果 in/2i \ge n/2 ,直接用 SELECT 算法即可,需要 T(n)T(n) 次比较。
  • 如果 i<n/2i < n/2 ,成对地比较输入元素,需要 n/2\lfloor n / 2\rfloor 次比较,形成了 n/2\lceil n/2 \rceil 组元素(可能有一个落单的);

  • 由于 i<n/2i < n/2 ,递归地在 n/2\lceil n/2 \rceil 个组的较小元素中寻找第 ii 小的元素,并据此划分这 n/2\lceil n/2 \rceil 个组(让组内较大数作为较小数的卫星数据跟着一起移动即可),需要 Ui(n/2)U_i(\lceil n/2 \rceil) 次比较;

  • 整个数组的第 ii 小元素一定在 n/2\lceil n/2 \rceil 个组的较小元素的前 ii 个较小元素所在的组中。对于这 ii 个组, 2i2i 个元素调用 SELECT 算法寻找第 ii 小的元素,需要 T(2i)T(2i) 次比较。

    • 剩余的 n/2i\lceil n/2\rceil - i 个组中不可能有第 ii 小的元素,里面最小也只能有第 i+1i + 1 小的元素。因为前面 ii 个组的 ii 个较小元素已经比这些组中的所有元素都小了。

上述算法的比较次数 Ui(n)U_i(n) 的递归式为:

Ui(n)={T(n)if in/2n/2+Ui(n/2)+T(2i)otherwiseU_i(n) = \begin{cases}
T(n) & \text{if } i \ge n / 2\
\lfloor n/2\rfloor + U_i(\lceil n/2 \rceil) + T(2i) & \text{otherwise}
\end{cases}

  1. 使用代入法证明。假设对于更小的 nn ,有 Ui(n)n+cT(2i)log(n/i)U_i(n) \le n + cT(2i)\log(n/i) ,取决于是否 i<n/4i < n/4 ,有两种情况:
  • 如果 i<n/4i < 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)(c1)T(2i)\begin{aligned}
    U_i(n) &= \lfloor n/2\rfloor + U_i(\lceil n/2 \rceil) + T(2i)\
    &\le n/2 + n/2 + cT(2i)\log(n/2i) + T(2i)\
    &= n + cT(2i)\log(n/i) - (c-1)T(2i)
    \end{aligned}

    其中,最后一步需要 c1c \ge 1

  • 如果 n/4i<n/2n/4 \le i < n/2 (此时有 n/22in/2 \le 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)\begin{aligned}
    U_i(n) &= \lfloor n/2\rfloor + U_i(\lceil n/2 \rceil) + T(2i)\
    &\le n/2 + T(n/2) + T(2i)\
    &\le n/2 + 2T(2i)\
    &\le n/2 + cT(2i)\log(n/2i)
    \end{aligned}

    其中,最后一步需要 c2c \ge 2

综上,取 c=2c = 2 即可, Ui(n)n+cT(2i)log(n/i)U_i(n) \le n + cT(2i)\log(n/i) ,即 Ui(n)=n+O(T(2i)log(n/i))U_i(n) = n + O(T(2i)\log(n/i))

  1. 基于第 2 问结论,如果 ii 是常数, O(T(2i)log(n/i))O(T(2i)\log(n/i)) 就变成了 O(logn)O(\log n) ,于是 Ui(n)=n+O(logn)U_i(n) = n + O(\log n)

  2. 基于第 2 问结论,将 i=n/ki = n / k 代入,有 Ui(n)=n+O(T(2i)log(n/i))=n+O(T(2n/k)logk)U_i(n) = n + O(T(2i)\log(n/i)) = n + O(T(2n/k)\log k)

Problem 4 (教材习题 9-4)

(随机化选择的另一种分析方法) 在这个问题中,我们用指示器随机变量来分析 RANDOMIZED-SELECT ,这一方法类似于第6讲PPT第27页 中所用的对 RANDOMIZED-QUICKSORT 的分析方法。

与快速排序中的分析一样,我们假设所有的元素都是互异的,输入数组 AA 的元素被重命名为 z1,z2,,znz_1, z_2, \cdots , z_n ,其中 ziz_i 是第 ii 小的元素。因此,调用 RANDOMIZED-SELECT(A,1,n,kA, 1, n, k) 返回 zkz_k

对所有 1i<jn1 \le i < j \le n ,设

Xijk=I{在执行算法查找 zk 期间,zi 与 zj 进行过比较}X_{ijk} = I{\text{在执行算法查找 }z_k\text{ 期间,} z_i\text{ 与 }z_j\text{ 进行过比较}}

  1. 给出 E[Xijk]E[X_{ijk}] 的准确表达式。(提示:你的表达式可能有不同的值,依赖于 i,j,ki, j, k 的值。)

  2. XkX_k 表示在找到 zkz_kAA 中元素的总比较次数,证明:

E[Xk]2(i=1kj=kn1ji+1+j=k+1njk1jk+1+i=1k2ki1ki+1)E[X_k] \le 2(\sum_{i=1}^{k}\sum_{j=k}^{n}\frac{1}{j-i+1} + \sum_{j=k+1}^{n}\frac{j-k-1}{j-k+1} + \sum_{i=1}^{k-2}\frac{k-i-1}{k-i+1})

  1. 证明: E[Xk]4nE[X_k]\le 4n

  2. 假设 AA 中的元素都是互异的,证明: RANDOMIZED-SELECT 的期望运行时间是 O(n)O(n)

Solution
  1. 我们只需要关心当我们选择一个在 min(zi,zj,zk)\min(z_i, z_j, z_k)max(zi,zj,zk)\max(z_i, z_j, z_k) 之间的主元的情况。因为此时我们
  • 要么选择 ziz_i 或者 zjz_j 作为主元,并比较它们两个;

  • 要么选择 ziz_izjz_j 之间的元素作为主元,并永远不比较它们两个;

  • 要么选择 zkz_k[zi,zj][z_i, z_j] 区间之间的元素作为主元,并永远不会再选择包含 ziz_izjz_j 的范围中的元素。

为了让 ziz_izjz_j 进行比较,应该选择 ziz_i 或者 zjz_j 作为主元,考虑主元选取是随机的,我们有

E(Xijk)={2jk+1zkzi<zj2ji+1zizk<zj2ki+1zi<zjzkE(X_{ijk}) = \begin{cases}
\frac{2}{j-k+1} & z_k \le z_i < z_j\
\frac{2}{j-i+1} & z_i \le z_k < z_j\
\frac{2}{k-i+1} & z_i < z_j \le z_k
\end{cases}

  1. E[Xk]E[X_k] 最多就是所有可能的元素对比较的期望之和:

E[Xk]1i<jnE[Xijk]=i=1kj=kn2ji+1+i=k+1n1j=i+1n2jk+1+i=1k2j=i+1k12ki+1=2(i=1kj=kn1ji+1+j=k+1njk1jk+1+i=1k2ki1ki+1)\begin{aligned}
E[X_k] &\le \sum_{1\le i < j \le n}E[X_{ijk}]\
&=\sum_{i=1}^{k}\sum_{j=k}^{n}\frac{2}{j-i+1} + \sum_{i=k+1}^{n-1}\sum_{j=i+1}^n\frac{2}{j-k+1} + \sum_{i=1}^{k-2}\sum_{j=i+1}^{k-1}\frac{2}{k-i+1}\
&=2(\sum_{i=1}^{k}\sum_{j=k}^{n}\frac{1}{j-i+1} + \sum_{j=k+1}^{n}\frac{j-k-1}{j-k+1} + \sum_{i=1}^{k-2}\frac{k-i-1}{k-i+1})
\end{aligned}

其中,最后一步是一个简单的和式变换,将行优先求和转化成列优先求和,类似于第6讲PPT第29页中涉及的和式变换。

  1. jk1jk+1<1\frac{j-k-1}{j-k+1} < 1 以及 ki1ki+1<1\frac{k-i-1}{k-i+1} < 1

j=k+1njk1jk+1+i=1k2ki1ki+1nk+k2=n2\sum_{j=k+1}^{n}\frac{j-k-1}{j-k+1} + \sum_{i=1}^{k-2}\frac{k-i-1}{k-i+1} \le n - k + k - 2 = n - 2

考虑使得 1ji+1=1c\frac{1}{j-i+1} = \frac{1}{c} 的项,其中 cc 是常数。这相当于 ji=c1j - i = c - 1 ,因此这样的项最多有 cc 项,cc 的范围是 [1,n][1, n] ,于是

i=1kj=kn2ji+1c=1nc1c=n\sum_{i=1}^{k}\sum_{j=k}^{n}\frac{2}{j-i+1} \le \sum_{c=1}^{n}c\cdot\frac{1}{c} = n

综上, E[Xk]2(n+n2)4nE[X_k] \le 2(n + n - 2) \le 4n

  1. RANDOMIZED-SELECT 算法中执行次数最多的关键操作就是输入数组中元素的比较操作,根据第 3 问结果, RANDOMIZED-SELECT 过程中输入数组元素间的期望比较次数不超过 4n4n ,因此 RANDOMIZED-SELECT 的期望运行时间为 O(n)O(n)