开头废话

这个问题是Donald.E.Knuth在他发表的论文Mathematical Analysis of Algorithms中提到的,这里对他的算法分析过程给出了更详细的解释。

问题描述:

给定一个数组a[1,2,...,n],用尽量少的比较次数找出数组中第t大的数。(假定这n个数两两不同)。

算法描述:

对于这个问题,可以很容易想到对应的算法。一个 \(O(n\log n)\) 的排序算法总能解决问题(然鹅今天我们并不对数组进行完全的排序)。

参照快速排序中的Partition操作,将元素a[i]放到某个位置\(k\),使得排在它前面的元素都比它大(但不一定按照从大到小的次序排列),后面的元素都比它小。再根据a[i]的位置\(k\)与\(t\)的大小关系,缩小查找范围再对子问题求解。

对于每一次Partition操作,会有这样的3种情况:

(1).若\(k=t\),算法结束。

(2).若\(k>t\),则对a[i]~a[k-1]递归地求解

(3).若\(k<t\),则对a[k+1]~a[j]递归地求解

时间复杂度分析

在这个问题的求解过程中,产生子问题的规模不断缩小。其中影响子问题的变量有\(n\)(数组的长度)和\(t\)(待查找的t)。Knuth记\(C_{n,t}\)为在\(n\)个元素的数组中选择第\(t\)大的数所需的平均比较次数,这里有一个前提,我们假设数组的排列是随机的,每一次Partition找到第1,第2,...,第n大的数概率均为\(\frac 1 n\)。

于是我们可以得到这样的式子:

\[\begin {aligned}
C_{1,1}&=0\\
C_{n,t}&=n-1+\frac 1n (A_{n,t}+B_{n,t}+0)
\end {aligned}
$$其中$A_{n,t}$和$B_{n,t}$的定义如下:\]

\begin {aligned}

A_{n,t}&=C_{n-1,t-1}+C_{n-2,t-2}+\cdots+C_{n-t+1,1}\

B_{n,t}&=C_{t,t}+C_{t+1,t}+\cdots+C_{n-1,t}

\end {aligned}

\[这里$A_{n,t}$对应的是递归过程中所有$k<t$的情况。对于这些情况,我们从数组的第$k+1$项开始向后的部分进行求解,如果把这部分看作一个新的数组,那么原始数组中第$t$大的数,在新的数组中是第$t-k$大的,也就是说这部分子问题是查找长度为$n-k$的数组中第$t-k$大的元素,其中$1\leq k \leq n.$

类似的,$B_{n,t}$对应所有$k>t$的情况,将数组第一项到第$k-1$项取出,看作一个新的数组,原始数组中第$t$大的数,在这新的数组中仍然是第$t$大,所以这部分的子问题是在长度为$k-1$的数组中选择第$t$大的数,其中$t+1\leq k \leq n.$

括号内剩下的一项$0$,对应的是$k=t$的情况,因为此时算法结束,不需要再求解子问题,所以比较次数为$0.$括号外的$n-1$是一次Partition要进行的比较次数。

这样,括号内就等于所有可能规模子问题的比较次数的总和,将它乘以$\frac 1n$,就得到子问题比较次数的数学期望,即我们所求的平均情况下的预期比较次数。

通过观察我们可以得到以下的递推公式:
\]

\begin {aligned}

A_{n+1,t+1}&=C_{n-1,t-1}+C_{n-2,t-2}+\cdots+C_{n-t+1,1}+C_{n,t}=A_{n,t}+C_{n,t}\

B_{n+1,t}&=C_{t,t}+C_{t+1,t}+\cdots+C_{n-1,t}+C_{n+1-1,t}=B_{n,t}+C_{n,t}

\end{aligned}

\[由上述等式作差消法,可以得到:
\]

(n+1)C_{n+1,t+1}-nC_{n,t+1}-nC_{n,t}+(n-1)C_{n-1,t}\

=(n+1)n-n(n-1)-n(n-1)+(n-1)(n-2)\+(A_{n+1,t+1}-A_{n,t})-(A_{n,t+1}-A_{n-1,t})+(B_{n+1,t+1}-B_{n,t+1})-(B_{n,t}-B_{n-1,t})

\

=2+C_{n,t}-C_{n-1,t}+C_{n,t+1}-C_{n-1,t}

\[合并同类项即可得到:
\]

(n+1)C_{n+1,t+1}-(n+1)C_{n,t+1}-(n+1)C_{n,t}+(n+1)C_{n-1,t}=2\\Downarrow\

C_{n+1,t+1}-C_{n,t+1}-C_{n,t}+C_{n-1,t}=\frac{2}{n+1}

\[接下来我们考察边界条件,当$t=1$时,由以上的式子我们可以得到下述方程组:
\]

\left{

\begin{array}{l}

C_{n,1}= n-1+\frac{1}{n}(C_{1,1}+C_{2,1}+\cdots +C_{n-1,1})\

B_{n,1}=C_{1,1}+C_{2,1}+\cdots+C_{n-1,1}\

B_{n+1,1}=B_{n,1}+C_{n,1}\

C_{n,1}=n-1+\frac{1}{n}(B_{n,1})\

C_{n+1,1}=n+\frac{1}{n+1}(B_{n+1,1})

\end{array}

\right.

\[消去方程组中包含$B$的项,可以得到:
\]

\begin{aligned}

(n+1)C_{n+1,1}-nC_{n,1} &= (n+1)n-n(n-1)+C_{n,1}\

C_{n+1,1}-C_{n,1}&=2-\frac{2}{n+1} \quad\quad(*)

\end{aligned}

\[接下来求解$C_{n,1}$:
列出方程组:
\]

\left{

\begin{array}{c}

\begin{aligned}

C_{1,1}&=0\

C_{2,1}-C_{1,1}&=2-\frac22\

C_{3,1}-C_{2,1}&=2-\frac23\

\cdots\

C_{n,1}-C_{n-1,1}&=2-\frac2n\

\end{aligned}

\end{array}

\right.\

\[将以上$n$个方程求和,最终左边只剩下$C_{n,1}$,得到如下式子:
\]

\begin{aligned}

C_{n,1}&=2(n-1)-2\sum_{k=2}^n \frac1k\

\quad\Downarrow

\

C_{n,1}&=2n-2\sum_{k=1}^n\frac1k=2n-2H_n

\end{aligned}

\[这里的$H_n$表示调和级数的前$n$项部分和。
由于问题具有的对称性(这部分可自行证明),$C_{n,n}=C_{n,1}=2n-2H_n$,将此式记作$(\Delta)$
由$(*)$式,可以列出以下方程组:
\]

\left{

\begin{array}{l}

(C_{n+1,t+1}-C_{n,t})-(C_{n,t+1}-C_{n-1,t})=\frac2{n+1}\

(C_{n,t+1}-C_{n-1,t})-(C_{n-1,t+1}-C_{n-2,t})=\frac2{n}\

\quad\quad\quad\quad\quad\quad\quad\quad\cdots\

(C_{t+2,t+1}-C_{t+1,t})-(C_{t+1,t+1}-C_{t,t})=\frac2{t+2}\

\end{array}

\right.\

\[再次对这$n-t$个方程累加,并联立$(\Delta)$式,可以得到:
\]

\begin{aligned}

C_{n+1,t+1}-C_{n,t}&=\frac{2}{n+1}+\frac{2}{n}+\cdots+\frac{2}{t+2}+C_{t+1,t+1}-C_{t,t}\

&=2(H_{n+1}-H_{t+1})+2-\frac{2}{t+1}

\end{aligned}

\[依次写出$C_{n,t}-C_{n-1,t-1}$到$C_{2,2}-C_{1,1}$的$n-1$个方程并再次累加(过程略去),可以推出:
\]

C_{n,t}=2\sum_{2\leq k\leq t}(H_{n-t+k}-H_{k}+1-\frac1k)+C_{n+1-t,1}

\[化简后:
\]

C_{n,t}=2((n+1)H_n-(n+3-t)H_{n+1-t}-(t+2)H_t+n+3),\quad(1\leq t\leq n)

\[由于$t$与$n$同阶,且平均情况下$t$的数学期望$E(t)=\frac 2n$,又 $H_n=\Theta(\log n)$ ,所以:
\]

C_{n,t}=O(n\log n)

\[至此,时间复杂度的证明结束。
\]

《Mathematical Analysis of Algorithms》中有关“选择第t大的数”的算法分析的更多相关文章

  1. "Mathematical Analysis of Algorithms" 阅读心得

    "Mathematical Analysis of Algorithms" 阅读心得 "Mathematical Analysis of Algorithms" ...

  2. 《Mathematical Analysis of Algorithms》中有关“就地排列”(In Situ Permutation)的算法分析

    问题描述 把数列\((x_1,x_2,\cdots,x_n)\)变换顺序为\((x_{p(1)},x_{p(2)},\cdots,x_{p(n)})\),其中\(p\)是\(A=\{1,2,3,\cd ...

  3. 选择第n大的数(分治法和排列实现)

    个人心得:在买的书上看到的一个经典分治题,题目意思就是给定一个数组,求第k小的数. 第一反应就是排序,然后返回第k-1位置的数就可以了,这样算法的复杂度是nlongn,在快速排序的基础下还是挺不错的. ...

  4. 从数组中找出第K大的数

    利用改进的快排方法 public class QuickFindMaxKValue { public static void main(String[] args) { int[] a = {8, 3 ...

  5. 算法分析 Analysis of Algorithms -------GeekforGeeker 翻译

    算法分析 Analysis of Algorithms 为什么要做性能分析?Why performance analysis? 在计算机领域有很多重要的因素我们要考虑 比如用户友好度,模块化, 安全性 ...

  6. 《Principles of Mathematical Analysis》-chaper1-实数系与复数系

    今天我们开始简单的介绍数学分析这门课程,参考教材是Walter Rudin著的<Principles of Mathematical Analysis> 对于一门新课你最开始可能会问的是: ...

  7. Visual Studio工具栏中无法选择调试设备

    Visual Studio工具栏中无法选择调试设备 在Visual Studio工具栏中,默认显示已经识别的设备.用户可以从中选择对应的设备,进行调试和部署App.但是由于误操作,可能导致该选项丢失. ...

  8. VIM 中鼠标选择不选中行号

    VIM 中鼠标选择不选中行号 在Vim中,我们一般会使用 :set nu 打开行号开关. 但是打开行号后,有个弊端,那就是在用鼠标进行选择的时候,会将前面的行号也一起进行拷贝了.但是在gVim中进行选 ...

  9. ArcGIS Engine中如何获取Map中已经选择的要素呢

    1.使用IEnumFeturea对象获取map中的FeatureSelection,该方法可以获取所有图层的选择要素.IMap中的FeatureSelection可不是IFeatureSelectio ...

随机推荐

  1. 7-42 jmu-python-找字符 (15 分)

    输入一个字符串及字符,输出第一次出现该字符的位置. 输入格式: 行1:输入字符串 行2:输入一个字符 输出格式: 找到,输出对应位置,格式index=X的, X表示查找到位置 找不到,输出can't ...

  2. 自定义checkbox, radio样式总结

    任务目的 深入了解html label标签 了解CSS边框.背景.伪元素.伪类(注意和伪元素区分)等属性的设置 了解CSS中常见的雪碧图,并能自己制作使用雪碧图 任务描述 参考 样例(点击查看),实现 ...

  3. 为什么要使用webpack?

    在网页中会引用到哪些常见的静态资源? js (.js  .jsx  .coffee  .ts) css (.css  .less  .sass  .scss scss是sass的plus版) imag ...

  4. 【MySQL】:事务四大特性与隔离级别

    目录 一.事务的概念 二.事务的四大特性 1.原子性 2.一致性 3.隔离性 4.持续性 三.事务语句 1.开启事务:start transaction 2.事务回滚:rollback 指定回滚点 3 ...

  5. Cake URAL - 1755

    1755. Cake Time limit: 0.5 secondMemory limit: 64 MB Karlsson and Little Boy have found a cake in th ...

  6. Android模拟器不能上网的解决方法

    我原来一直不用Android的模拟器,因为这东西的多年前的印象真的是很糟糕——启动半个小时,不支持OpenGL.即使后来有了x86镜像,在HAXM的支持下快的飞起,也不想用,因为NDK还要编译x86的 ...

  7. @常见的远程服务器连接工具:Xshell与secureCRT的比较!!!(对于刚接触的测试小白很有帮助哦)

    现在比较受欢迎的终端模拟器软件当属xshell和securecrt了. XShell绝对首选,免费版也没什么限制,随便改字体随便改颜色随便改大小随便改字符集,多窗口,也比较小巧,而SecureCRT界 ...

  8. Generator的异步编程

    对比下常用的异步处理的方案: 1,回调 我们常说的 “回调地狱”,就是多个异步操作时候,代码多重嵌套,异步之前形成强耦合,如果修改一处,其他地方也是跟着修改.(callback hell). 2,pr ...

  9. 第二篇:如何安装Linux,虚拟机安装Linux

    安装Linux的方法挺多,但是这里咱们只说一种:如何在虚拟机里安装运行Linux.     想必看此类文章的都是小白,所以下面我就写的通俗易懂点.     第一步:下载虚拟机软件.(虚拟机软件是啥?它 ...

  10. 深度学习与人类语言处理-语音识别(part1)

    语音识别 语音识别该何去何从? 1969年,J.R. PIERCE:"语音识别就像把水变成汽油.从大海中淘金.治疗癌症.人类登陆月球" 当然,这是50年前的想法,那么语音识别该如何 ...