开头废话

这个问题是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. Spring Boot 2.x基础教程:使用 ECharts 绘制各种华丽的数据图表

    上一节我们介绍了如何在Spring Boot中使用模板引擎Thymeleaf开发Web应用的基础.接下来,我们介绍一下后端开发经常会遇到的一个场景:可视化图表. 通常,这类需求在客户端应用中不太会用到 ...

  2. Angular 从入坑到挖坑 - 表单控件概览

    一.Overview angular 入坑记录的笔记第三篇,介绍 angular 中表单控件的相关概念,了解如何在 angular 中创建一个表单,以及如何针对表单控件进行数据校验. 对应官方文档地址 ...

  3. [Cts-Verifier]waiver-Camera-ITS-Test

    [问题描述] 工具:Cts-Verifier-9.0-R11.apk 测试Camera ITS Test时,点击该测试项后verifier apk闪退.重新打开后该项未pass变绿. [问题结论] A ...

  4. py基础之无序列表

    '''dic是一个可以将两个相关变量关联起来的集合,格式是dd={key1:value1,key2:value2,key3:value3}'''d = { 'adam':95, 'lisa':85, ...

  5. JZOJ 3927. 【NOIP2014模拟11.6】可见点数

    3927. [NOIP2014模拟11.6]可见点数 (Standard IO) Time Limits: 1000 ms Memory Limits: 65536 KB Description ZP ...

  6. 峰哥说技术:06-手撸Spring Boot自定义启动器,解密Spring Boot自动化配置原理

    Spring Boot深度课程系列 峰哥说技术—2020庚子年重磅推出.战胜病毒.我们在行动 06  峰哥说技术:手撸Spring Boot自定义启动器,解密Spring Boot自动化配置原理 Sp ...

  7. 如何将zTree选中节点传递给后台

    获取zTree选中节点 <body> <script type="text/javascript"> var setting = { view: { dbl ...

  8. CVPR 2020 全部论文 分类汇总和打包下载

    CVPR 2020 共收录 1470篇文章,根据当前的公布情况,人工智能学社整理了以下约100篇,分享给读者. 代码开源情况:详见每篇注释,当前共15篇开源.(持续更新中,可关注了解). 算法主要领域 ...

  9. Python自定义模块

    自定义模块 自定义模块(也就是私人订制),我们要自定义模块,首先就要知道什么是模块 一个函数封装一个功能,比如现在有一个软件,不可能将所有程序都写入一个文件,所以咱们应该分文件,组织结构要好,代码不冗 ...

  10. vue — 创建vue项目

    创建vue项目 在程序开发中,有三种方式创建vue项目,本地引入vuejs.使用cdn引入vuejs.使用vue-cli创建vue项目.其中vue-cli可以结合webpack打包工具使用,大大方便了 ...