《Mathematical Analysis of Algorithms》中有关“选择第t大的数”的算法分析
开头废话
这个问题是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\)。
于是我们可以得到这样的式子:
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}
类似的,$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}
\]
\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.
\]
\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}
列出方程组:
\]
\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.\
\]
\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}
由于问题具有的对称性(这部分可自行证明),$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.\
\]
\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}=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)
\]
C_{n,t}=O(n\log n)
\]
《Mathematical Analysis of Algorithms》中有关“选择第t大的数”的算法分析的更多相关文章
- "Mathematical Analysis of Algorithms" 阅读心得
"Mathematical Analysis of Algorithms" 阅读心得 "Mathematical Analysis of Algorithms" ...
- 《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 ...
- 选择第n大的数(分治法和排列实现)
个人心得:在买的书上看到的一个经典分治题,题目意思就是给定一个数组,求第k小的数. 第一反应就是排序,然后返回第k-1位置的数就可以了,这样算法的复杂度是nlongn,在快速排序的基础下还是挺不错的. ...
- 从数组中找出第K大的数
利用改进的快排方法 public class QuickFindMaxKValue { public static void main(String[] args) { int[] a = {8, 3 ...
- 算法分析 Analysis of Algorithms -------GeekforGeeker 翻译
算法分析 Analysis of Algorithms 为什么要做性能分析?Why performance analysis? 在计算机领域有很多重要的因素我们要考虑 比如用户友好度,模块化, 安全性 ...
- 《Principles of Mathematical Analysis》-chaper1-实数系与复数系
今天我们开始简单的介绍数学分析这门课程,参考教材是Walter Rudin著的<Principles of Mathematical Analysis> 对于一门新课你最开始可能会问的是: ...
- Visual Studio工具栏中无法选择调试设备
Visual Studio工具栏中无法选择调试设备 在Visual Studio工具栏中,默认显示已经识别的设备.用户可以从中选择对应的设备,进行调试和部署App.但是由于误操作,可能导致该选项丢失. ...
- VIM 中鼠标选择不选中行号
VIM 中鼠标选择不选中行号 在Vim中,我们一般会使用 :set nu 打开行号开关. 但是打开行号后,有个弊端,那就是在用鼠标进行选择的时候,会将前面的行号也一起进行拷贝了.但是在gVim中进行选 ...
- ArcGIS Engine中如何获取Map中已经选择的要素呢
1.使用IEnumFeturea对象获取map中的FeatureSelection,该方法可以获取所有图层的选择要素.IMap中的FeatureSelection可不是IFeatureSelectio ...
随机推荐
- java反序列化-ysoserial-调试分析总结篇(4)
1.前言 这篇文章继续分析commoncollections4利用链,这篇文章是对cc2的改造,和cc3一样,cc3是对cc1的改造,cc4则是对cc2的改造,里面chained的invoke变成了i ...
- Java中如何更优雅的处理空值
经常看到项目中存在到处空值判断的情况,这些判断,会让人觉得摸不着头绪,它的出现很有可能和当前的业务逻辑并没有关系.但它会让你很头疼.有时候,更可怕的是系统因为这些空值的情况,会抛出空指针异常,导致业务 ...
- [面试专题]Web缓存详解
Web缓存详解 标签(空格分隔): 缓存 缓存之于性能优化 请求更快:通过将内容缓存在本地浏览器或距离最近的缓存服务器(如CDN),在不影响网站交互的前提下可以大大加快网站加载速度. 降低服务器压力: ...
- web前端 关于浏览器兼容的一些知识和问题解决
浏览器兼容 为什么产生浏览器兼容,浏览器兼容问题什么是浏览器兼容: 所谓的浏览器兼容性问题,是指因为不同的浏览器对同一段代码有不同的解析,造成页面显示效果不统一的情况. 浏览器兼容产生的原因: 因为不 ...
- Sequence to Sequence Learning with Neural Networks论文阅读
论文下载 作者(三位Google大佬)一开始提出DNN的缺点,DNN不能用于将序列映射到序列.此论文以机器翻译为例,核心模型是长短期记忆神经网络(LSTM),首先通过一个多层的LSTM将输入的语言序列 ...
- 7种你应该知道的JavaScript常见的错误
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://blog.bitsrc.io/types-of-native-errors-in- ...
- C语言程序设计(十二) 结构体和共用体
第十二章 结构体和共用体 当需要表示复杂对象时,仅使用几个基本数据类型显然是不够的 根本的解决方法是允许用户自定义数据类型 构造数据类型(复合数据类型)允许用户根据实际需要利用已有的基本数据类型来构造 ...
- Object.keys()方法
一.定义和用法 返回对象的可枚举属性和方法的名称.二.参数 obj:要返回器枚举自身属性的对象.三.返回值 返回一个所有元素为字符串的数组,其元素来自于从给定的obj里可直接枚举的属性.这些属性的顺序 ...
- Jquery 系列化表单
大家知道Jquery中有serialize方法,可以将表单序列化为一个“&”连接的字符串,但却没有提供序列化为Json的方法.不过,我们可以写一个插件实现. 我在网上看到有人用替换的方法,先用 ...
- Elasticsearch系列---多字段搜索
概要 本篇介绍一下multi_match的best_fields.most_fields和cross_fields三种语法的场景和简单示例. 最佳字段 bool查询采取"more-match ...