冒泡排序平均需要跑多少趟:拉马努金Q函数初探

2024-02-15 13:12

本文主要是介绍冒泡排序平均需要跑多少趟:拉马努金Q函数初探,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

摘要: 拉马努金Q函数在算法分析中的应用,初步体验

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


各位好,本文我们继续来讨论算法分析中的问题。

很多数组上的算法都与 1 ∼ n 1 \sim n 1n 的排列有关,比如各种排序算法。在分析数组上的排序算法时,排列上的各种概念对分析问题很有用,比如逆序、圈、左向右最大值、上升、下降、游程、峰、谷等等。

本文我们看一下逆序的概念及其在冒泡排序的分析中的作用。然后我们解决一个相关的算法分析问题:对于 1 ∼ N 1 \sim N 1N 的随机排列, N → ∞ N \rightarrow\infty N 时,冒泡排序平均要跑多少趟。

首先给出结论, N → ∞ N \rightarrow\infty N 时,冒泡排序平均扫描趟数为 N − π N 2 + O ( 1 ) N - \sqrt{\frac{\pi N}{2}} + O(1) N2πN +O(1)

为了推导以上结论,我们首先介绍一下排列中关于逆序、逆序数、逆序表的概念。然后介绍一下冒泡排序算法的流程,然后根据逆序表的概念以及冒泡排序的流程,我们将冒泡排序平均扫描趟数问题转化为逆序表的最大值的问题。

之后我们在逆序表上经过一些组合分析,再结合数学期望的性质,将问题的答案写成了一个和式。于是原问题最终转化为了一个和式的渐近估阶的问题。

然后引用拉马努金Q函数相关的结论,对原和式化简,使得我们可以比较容易地对简化后的和式进行渐近估阶,得到最终结果。

排列的一些基本概念

p = p 1 p 2 ⋅ ⋅ ⋅ p N p = p_{1}p_{2}\cdot\cdot\cdot p_{N} p=p1p2pN 1 ∼ N 1 \sim N 1N 的一个排列。

逆序:一个逆序 i < j i < j i<j p i > p j p_{i} > p_{j} pi>pj 的一个数对。

逆序表:记 q j q_{j} qj 表示 i ∈ [ 1.. j − 1 ] i \in [1..j-1] i[1..j1] p i > p j p_{i} > p_{j} pi>pj 的个数。 q = q 1 q 2 ⋅ ⋅ ⋅ q N q = q_{1}q_{2}\cdot\cdot\cdot q_{N} q=q1q2qN 成为排列 p p p逆序表

逆序数:记 σ ( p ) = ∑ j = 1 N q j \sigma(p) = \sum\limits_{j=1}\limits^{N}q_{j} σ(p)=j=1Nqj,表示排列 p p p逆序数

排列与逆序表的一一对应

由以上定义可以知道,对于 1 ≤ j ≤ N 1 \leq j \leq N 1jN q j q_{j} qj 需要满足约束 0 ≤ q j < j 0 \leq q_{j} < j 0qj<j

给定满足该约束的任意一个序列 q = q 1 q 2 ⋅ ⋅ ⋅ q N q = q_{1}q_{2}\cdot\cdot\cdot q_{N} q=q1q2qN,下面我们构造满足该逆序表 q q q 的排列 p p p

对于 i = N , N − 1 , ⋯ , 1 i = N, N-1, \cdots, 1 i=N,N1,,1,置 p i p_{i} pi 为未曾用过的数中第 q i q_{i} qi 大的数,即可从右到左构造 p = p 1 p 2 ⋅ ⋅ ⋅ p N p = p_{1}p_{2}\cdot\cdot\cdot p_{N} p=p1p2pN

也就是说给定任意长为 N N N 的逆序表,至少可以构造出一个满足该逆序表的 1 ∼ N 1 \sim N 1N 的排列,下面我们说明该排列是唯一的。

由于 q i q_{i} qi 的取值范围 [ 0.. i − 1 ] [0..i-1] [0..i1] i i i 种可能,因此共有 N ! N! N! 种可能的逆序表 q = q 1 q 2 ⋅ ⋅ ⋅ q N q = q_{1}q_{2}\cdot\cdot\cdot q_{N} q=q1q2qN

另一方面我们熟知 1 ∼ N 1 \sim N 1N 的排列数为 N ! N! N!,如果在 N ! N! N! 个长为 N N N 的逆序表中,存在某个逆序表,其对应的排列不唯一,由鸽巢原理,就会出现某个 1 ∼ N 1 \sim N 1N 的排列对应两个不同的逆序表的情况,这与逆序表的定义矛盾。

因此长度为 N N N 的排列与逆序表之间存在一一对应关系。这对于我们分析冒泡排序非常有用。

冒泡排序算法

要对一个数组 p p p 排序,可以重复扫描这个数组。在一趟扫描过程中,假设扫描到 p [ i ] p[i] p[i] 时,如果 p [ i ] > p [ i + 1 ] p[i] > p[i + 1] p[i]>p[i+1],则将 p [ i ] p[i] p[i] p [ i + 1 ] p[i+1] p[i+1] 交换,然后扫描下一个。否则直接扫描下一个。

如果在某趟扫描完成时,没有进行过任何交换,也就是每个元素都不比它后面的元素大,则排序就完成了。

由于每个交换都是两个相邻元素的交换,因此交换之后,逆序数减 1,这样总的交换次数恰好是一个排列中的逆序数。

另一方面,通过画图分析我们可以发现,一趟扫描完成后,逆序表中每个非零项的值都会减 1。当逆序表中所有项均为零时,程序结束。

这就隐含了:对一个排列的冒泡排序所需的趟数就等于逆序表中的最大元素。于是冒泡排序平均要跑多少趟,就等同于问:在 N ! N! N! 种可能的长为 N N N 的逆序表 q = q 1 q 2 ⋅ ⋅ ⋅ q N q = q_{1}q_{2}\cdot\cdot\cdot q_{N} q=q1q2qN 中,最大值 max ⁡ 1 ≤ j ≤ N q j \max\limits_{1\leq j \leq N} q_{j} 1jNmaxqj 的平均值是多少。

逆序表中的最大值

对于 1 ∼ N 1 \sim N 1N 的排列,给定 0 ≤ k ≤ N 0 \leq k \leq N 0kN,考察所有项都小于 k k k 的逆序表的个数。

考察 q i q_{i} qi。如果 i ≤ k i \leq k ik q i q_{i} qi 可以取 [ 0.. i − 1 ] [0..i-1] [0..i1] 的任意值。当 i > k i > k i>k 时, q i q_{i} qi 可以取 [ 0.. k − 1 ] [0..k-1] [0..k1] 之间的值。

于是 N ! N! N! 种长为 N N N 的逆序表中,所有项都小于 k k k,也就是最大项小于 k k k 的逆序表的个数为 k ! k N − k k!k^{N-k} k!kNk

因此 N ! N! N! 个长为 N 的逆序表中,最大项大于等于 k k k 的概率为 1 − k ! k N − k N ! 1 - \frac{k!k^{N-k}}{N!} 1N!k!kNk

Q Q Q 为随机的长为 N N N 的逆序表中的最大值,于是我们已经得到 P ( Q ≥ k ) = 1 − k ! k N − k N ! P(Q \geq k) = 1 - \frac{k!k^{N-k}}{N!} P(Qk)=1N!k!kNk

下面我们要求 E [ Q ] E[Q] E[Q]。这需要用到通过累积分布函数求数学期望的性质。

由累计分布函数求数学期望

我们知道,根据定义计算数学期望的一般方法如下。

X X X 为离散型,分布律为 P ( X = x ) P(X=x) P(X=x)

E [ X ] = ∑ x x P ( X = x ) E[X] = \sum\limits_{x}xP(X=x) E[X]=xxP(X=x)

X X X 为连续型,概率密度函数为 f ( x ) f(x) f(x)

E [ X ] = ∫ x f ( x ) d x E[X] = \int xf(x)\mathrm{d}x E[X]=xf(x)dx

但如果随机变量 X X X 非负,还可以有不经过分布律或概率密度函数的算法。

如果非负 X X X 为离散型,且已知 P ( X ≥ n ) P(X \geq n) P(Xn),那么有:

E [ X ] = ∑ n = 0 ∞ P ( X ≥ n ) E[X] = \sum\limits_{n=0}\limits^{\infty}P(X\geq n) E[X]=n=0P(Xn)

证明

E [ X ] = ∑ x = 0 ∞ x P ( X = x ) = ∑ x = 0 ∞ ∑ n = 0 x P ( X = x ) = ∑ n = 0 ∞ ∑ x = n ∞ P ( X = x ) = ∑ n = 0 ∞ P ( X ≥ x ) \begin{aligned} E[X] &= \sum\limits_{x=0}\limits^{\infty}xP(X=x) \\ &= \sum\limits_{x=0}\limits^{\infty}\sum\limits_{n=0}\limits^{x}P(X=x) \\ &= \sum\limits_{n=0}\limits^{\infty}\sum\limits_{x=n}\limits^{\infty}P(X=x) \\ &= \sum\limits_{n=0}\limits^{\infty}P(X\geq x) \\ \end{aligned} E[X]=x=0xP(X=x)=x=0n=0xP(X=x)=n=0x=nP(X=x)=n=0P(Xx)

□ \Box

类似地,如果非负 X X X 为连续型,且其累积分布函数为 F ( x ) F(x) F(x),那么有:

E [ X ] = ∫ 0 ∞ ( 1 − F ( x ) ) d x E[X] = \int_{0}^{\infty}(1 - F(x))\mathrm{d}x E[X]=0(1F(x))dx

证明

E [ X ] = ∫ 0 ∞ y f ( y ) d y = ∫ 0 ∞ ∫ 0 y f ( y ) d x d y = ∫ 0 ∞ ∫ x ∞ f ( y ) d y d x = ∫ 0 ∞ ( 1 − F ( x ) ) d x \begin{aligned} E[X] &= \int_{0}^{\infty}yf(y)\mathrm{d}y \\ &= \int_{0}^{\infty}\int_{0}^{y}f(y)\mathrm{d}x\mathrm{d}y \\ &= \int_{0}^{\infty}\int_{x}^{\infty}f(y)\mathrm{d}y\mathrm{d}x \\ &= \int_{0}^{\infty}(1 - F(x))\mathrm{d}x \\ \end{aligned} E[X]=0yf(y)dy=00yf(y)dxdy=0xf(y)dydx=0(1F(x))dx

□ \Box

问题规约:拉马努金Q函数

根据前面对逆序表的分析,以及上述数学期望的性质,长为 N N N 的逆序表的最大值的期望如下:

∑ k = 0 N ( 1 − k ! k N − k N ! ) = N + 1 − ∑ k = 0 N k ! k N − k N ! \sum\limits_{k=0}\limits^{N}(1 - \frac{k!k^{N-k}}{N!}) = N + 1 - \sum\limits_{k=0}\limits^{N}\frac{k!k^{N-k}}{N!} k=0N(1N!k!kNk)=N+1k=0NN!k!kNk

于是最初的问题最终归结到了对 ∑ k = 0 N k ! k N − k N ! \sum\limits_{k=0}\limits^{N}\frac{k!k^{N-k}}{N!} k=0NN!k!kNk 的渐近估阶。

对以上和式做下标变换:

∑ k = 0 N k ! k N − k N ! = ∑ k = 0 N ( N − k ) ! ( N − k ) k N ! \sum\limits_{k=0}\limits^{N}\frac{k!k^{N-k}}{N!} = \sum\limits_{k=0}\limits^{N}\frac{(N-k)!(N-k)^{k}}{N!} \\ k=0NN!k!kNk=k=0NN!(Nk)!(Nk)k

f N ( k ) = ( N − k ) ! ( N − k ) k N ! f_{N}(k) = \frac{(N-k)!(N-k)^{k}}{N!} fN(k)=N!(Nk)!(Nk)k,下面推导 f N ( k ) f_{N}(k) fN(k)

f N ( k ) = ( N − k ) ! ( N − k ) k N ! = N − k N ⋅ N − k N − 1 ⋅ ⋅ ⋅ N − k N − k + 1 = ( 1 − k N ) ⋅ ( 1 − k − 1 N − 1 ) ⋅ ⋅ ⋅ ( 1 − 1 N − k + 1 ) = ∏ i = 1 k ( 1 − i N − k + i ) \begin{aligned} f_{N}(k) &= \frac{(N-k)!(N-k)^{k}}{N!} \\ &= \frac{N-k}{N}\cdot \frac{N-k}{N-1} \cdot\cdot\cdot \frac{N-k}{N-k+1} \\ &= (1 - \frac{k}{N})\cdot (1 - \frac{k-1}{N-1}) \cdot\cdot\cdot (1 - \frac{1}{N-k+1}) \\ &= \prod\limits_{i=1}\limits^{k}(1 - \frac{i}{N-k+i}) \\ \end{aligned} fN(k)=N!(Nk)!(Nk)k=NNkN1NkNk+1Nk=(1Nk)(1N1k1)(1Nk+11)=i=1k(1Nk+ii)

后续的处理非常复杂,需要对 k 较小和 k 很大的情况分别讨论,比较冗长,其完整推导过程与拉马努金 Q 函数的推导过程类似,这里直接引用结论,如下:

∑ k = 0 N k ! k N − k N ! = ∑ k = 0 N f N ( k ) = ∑ k = 0 N e − k 2 2 N + O ( 1 ) N → ∞ \sum\limits_{k=0}\limits^{N}\frac{k!k^{N-k}}{N!} = \sum\limits_{k=0}\limits^{N}f_{N}(k) = \sum\limits_{k=0}\limits^{N}e^{-\frac{k^{2}}{2N}} + O(1) \quad\quad N\rightarrow\infty k=0NN!k!kNk=k=0NfN(k)=k=0Ne2Nk2+O(1)N

以后有时间可以再回来看一下上式的推导过程,感兴趣的可以看《算法分析导论》或《计算机程序设计艺术》中关于拉马努金Q函数的内容。

积分逼近求和

下面对 ∑ k = 0 ∞ e − k 2 2 N \sum\limits_{k=0}\limits^{\infty}e^{-\frac{k^{2}}{2N}} k=0e2Nk2 进行估阶。记 h ( x ) = e − x 2 2 N h(x) = e^{-\frac{x^{2}}{2N}} h(x)=e2Nx2。可以用积分逼近求和:

∑ k = 0 ∞ e − k 2 2 N = ∫ 0 ∞ e − x 2 2 N d x + Δ = 2 N ∫ 0 ∞ e − ( x 2 N ) 2 d x 2 N + Δ = 2 N ∫ 0 ∞ e − t 2 d t + Δ = 2 N π 2 + Δ \begin{aligned} \sum\limits_{k=0}\limits^{\infty}e^{-\frac{k^{2}}{2N}} &= \int_{0}^{\infty}e^{-\frac{x^{2}}{2N}}\mathrm{d}x + \Delta \\ &= \sqrt{2N}\int_{0}^{\infty}e^{-(\frac{x}{\sqrt{2N}})^{2}}\mathrm{d}\frac{x}{\sqrt{2N}} + \Delta \\ &= \sqrt{2N}\int_{0}^{\infty}e^{-t^{2}}\mathrm{d}t + \Delta \\ &= \sqrt{2N}\frac{\sqrt{\pi}}{2} + \Delta \\ \end{aligned} k=0e2Nk2=0e2Nx2dx+Δ=2N 0e(2N x)2d2N x+Δ=2N 0et2dt+Δ=2N 2π +Δ

由于 h ( x ) h(x) h(x) x ≥ 0 x \geq 0 x0 上是单调递减函数, Δ ≤ ∣ h ( 0 ) − h ( ∞ ) ∣ = 1 \Delta \leq |h(0) - h(\infty)| = 1 Δh(0)h()=1,于是有:

∑ k = 0 ∞ e − k 2 2 N = π N 2 + O ( 1 ) \sum\limits_{k=0}\limits^{\infty}e^{-\frac{k^{2}}{2N}} = \sqrt{\frac{\pi N}{2}} + O(1) k=0e2Nk2=2πN +O(1)

上式通过欧拉-麦克劳林公式也可以导出,但由于 h ( x ) h(x) h(x) 的单调性,直接用积分逼近求和更简单一些。最终结果为:

N + 1 − ∑ k = 0 N k ! k N − k N ! = N − π N 2 + O ( 1 ) N → ∞ N + 1 - \sum\limits_{k=0}\limits^{N}\frac{k!k^{N-k}}{N!} = N - \sqrt{\frac{\pi N}{2}} + O(1) \quad\quad N\rightarrow\infty N+1k=0NN!k!kNk=N2πN +O(1)N

也就是, N → ∞ N \rightarrow\infty N 时,冒泡排序平均扫描趟数为 N − π N 2 + O ( 1 ) N - \sqrt{\frac{\pi N}{2}} + O(1) N2πN +O(1)

总结

本文我们讨论了排序算法的分析中的一个问题:冒泡排序平均需要跑多少趟。

首先引入了排列中的一些概念定义,包括逆序、逆序表,然后基于冒泡排序的算法流程,发现冒泡排序扫描的趟数就是逆序表中的最大值。

再结合排列的逆序表自身的性质,以及通过累积分布函数求数学期望的性质,最终我们将问题归结到了 ∑ k = 0 N k ! k N − k N ! \sum\limits_{k=0}\limits^{N}\frac{k!k^{N-k}}{N!} k=0NN!k!kNk 的渐近估阶。

上式的渐近估阶非常麻烦冗长,我们参考了《计算机程序设计艺术》、《算法分析导论》等名著中关于拉马努金Q函数的相关论述,直接引用结果,将问题转化为 ∑ k = 0 ∞ e − k 2 2 N \sum\limits_{k=0}\limits^{\infty}e^{-\frac{k^{2}}{2N}} k=0e2Nk2 的进行估阶。而后者通过积分逼近求和的方式或者欧拉-麦克劳林公式你可以方便解决。

最终我们得出结论, N → ∞ N \rightarrow\infty N 时,冒泡排序平均扫描趟数为 N − π N 2 + O ( 1 ) N - \sqrt{\frac{\pi N}{2}} + O(1) N2πN +O(1)。通过这个例子我们看到,使用拉马努金 Q 函数可以将某些难解的和式简化。

算法分析中使用拉马努金 Q 函数的例子非常多,关于拉马努金 Q 函数的前因后果,以及更多的应用,后续再跟大家探讨。

这篇关于冒泡排序平均需要跑多少趟:拉马努金Q函数初探的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/711484

相关文章

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据

Unity3D 运动之Move函数和translate

CharacterController.Move 移动 function Move (motion : Vector3) : CollisionFlags Description描述 A more complex move function taking absolute movement deltas. 一个更加复杂的运动函数,每次都绝对运动。 Attempts to

✨机器学习笔记(二)—— 线性回归、代价函数、梯度下降

1️⃣线性回归(linear regression) f w , b ( x ) = w x + b f_{w,b}(x) = wx + b fw,b​(x)=wx+b 🎈A linear regression model predicting house prices: 如图是机器学习通过监督学习运用线性回归模型来预测房价的例子,当房屋大小为1250 f e e t 2 feet^

JavaSE(十三)——函数式编程(Lambda表达式、方法引用、Stream流)

函数式编程 函数式编程 是 Java 8 引入的一个重要特性,它允许开发者以函数作为一等公民(first-class citizens)的方式编程,即函数可以作为参数传递给其他函数,也可以作为返回值。 这极大地提高了代码的可读性、可维护性和复用性。函数式编程的核心概念包括高阶函数、Lambda 表达式、函数式接口、流(Streams)和 Optional 类等。 函数式编程的核心是Lambda