Randomized algorithms(随机算法)

随机算法是由一个雇佣问题引出的:

假如你要雇佣一名新的办公室助理,但是你先前的雇佣尝试都失败了,你打算找一个雇佣代理。雇佣代理每天给你推荐一个应聘者。你面试这个人,然后决定是否雇佣他,同时你需要付给雇佣代理一定的费用,以便面试应聘者。除此之外,雇佣一个人也需要花费一大笔钱,因为你必须辞掉目前的办公室助理,同时付给雇佣代理一大笔中介费。

你承诺:任何时候都找最合适的人来担任这项职务,因此你决定在面试完应聘者之后,如果该应聘者比目前的办公室助理更合适,就会辞掉当前的办公室助理,然后聘用新的。

同时你愿意为该策略付费,但希望能够估算出该费用。

下面先给出HIRE-ASSISTANT过程的伪代码,该伪代码表示雇佣策略。

假设应聘候选人编号为1…n,该过程中,假设你能在面试完应聘者i之后,可以决定应聘者i是否为你目前见过最佳的人选。

HIRE-ASSISTANT
best = 0
for i = 1 to n
	interview candidate i
	if candidate i is better than best
		best = i
		hire candidate i

不管是分析费用,还是分析运行时间,采用的方法都是相同的。在任何情形中,我们都是在计算特定基本操作的执行次数。

例如,面试和雇佣都会产生一定的费用,面试费用较低,比如 c i c_i ci,然后雇佣的费用很高,比如 c h c_h ch。假设以用有n个应聘者,m个被雇佣的人,那么这个算法的总费用为 O ( c i n + c h m ) O(c_in+c_hm) O(cin+chm)​​​​​。由于面试产生的费用总是 c i n c_in cin,因此我们只关注分析 c h m c_hm chm就可以了,也就是雇佣的费用。

我们很容易就知道,上述的策略是极其依赖于输入序列的,不同的输入序列,会产生不同的情况,运行时间也就不同。如果应聘者的质量严格递增的话,你就需要雇佣n次,总的费用就是 O ( c h n ) O(c_hn) O(chn)​​​​​​​​​​​。这个时候我们就需要讨论平均情况下的运行时间了,我们可以假设每个应聘者是随机出现的。随机出现就意味着所有应聘者之间存在着一个全序关系,那么一共就有 n ! n! n!种排列的可能,每一种序列等概率出现。

但是大多数情况下,我们并不知道应聘者是不是随机出现的,因此这个时候可以在输入和算法之间加入一个随机数生成器,那么这个时候我们可以称这个算法是随机的。(在实践中,大多数编程环境会提供一个伪随机数生成器,它是一个确定性算法,返回值在统计上看起来是随机的。)

当分析一个随机算法的运行时间时,我们以运行时间的期望值来衡量,一个随机算法的运行时间称为期望运行时间;当概率分布发生在算法的输入的时候,我们讨论的是平均情况运行时间

指示器随机变量

我们经常采用指示器随机变量来进行分析,它为概率和期望之间的转换提供了一个便利的方法。

给定一个样本空间 S S S和一个事件 A A A,那么事件 A A A对应的指示器随机变量 I ( A ) I(A) I(A)定义为:
I { A } = { 1 如果A发生 0 如果A不发生 I\{A\}= \left \{ \begin{aligned} &1 \quad \text{如果A发生}\\ &0 \quad \text{如果A不发生} \end{aligned} \right. I{A}={1如果A发生0如果A不发生
举个例子,假设我们来确定抛一枚标准硬币时 正面朝上的期望次数。样本空间 S = { T   , F } S=\{T \,, F\} S={T,F}​​​, T T T​​表示正面朝上, F F F​​表示反面朝上,其中 P r { T } = P r { F } = 1 / 2 Pr\{T\}=Pr\{F\}=1/2 Pr{T}=Pr{F}=1/2​​。接下来定义一个指示器随机变量 X T X_T XT​,对应硬币朝上的事件 T T T​​​。​这个变量是计数抛硬币时正面朝上的次数,如果正面朝上则值为1,否则为0。
X T = I { T } = { 1 如果T发生 0 如果F发生 X_T=I\{T\}= \left \{ \begin{aligned} &1 \quad \text{如果T发生} \\ &0 \quad \text{如果F发生} \end{aligned} \right. XT=I{T}={1如果T发生0如果F发生
所以在一次抛硬币的时候,正面朝上的期望次数就是指示器变量 X T X_T XT的期望值:
E [ X T ] = E [ I { T } ] = 1 ∗ P r { T } + 0 ∗ P r { F } = 1 / 2 E[X_T]=E[I\{T\}]=1*Pr\{T\}+0*Pr\{F\}=1/2 E[XT]=E[I{T}]=1Pr{T}+0Pr{F}=1/2
因此,抛一枚硬币的时候,正面朝上的期望次数为 1 / 2 1/2 1/2

一个事件A对应的指示器随机变量的期望值等于事件A发生的概率。

给定一个样本空间S和事件A,设 X A = I { A } X_A=I\{A\} XA=I{A}​,那么 E [ X A ] = P r { A } E[X_A]=Pr\{A\} E[XA]=Pr{A}​。(这里就不证明了,证明过程可以去看算法导论)

指示器随机变量看起来很麻烦,但是在分析重复随机试验的时候是非常有用的。

接下来我们使用指示器随机变量来分析这个雇佣问题。

X X X对应我们雇佣新的办公室助理的次数, X i X_i Xi对应第 i i i​个应聘者被雇佣 这个指示器随机变量:
X i = I { i 被聘用 } = { 1 应聘者 i 被聘用 0 应聘者 i 未被聘用 X_i=I\{i\text{被聘用} \}= \left \{ \begin{aligned} &1 \quad \text{应聘者}i\text{被聘用} \\ &0 \quad \text{应聘者}i\text{未被聘用} \end{aligned} \right. Xi=I{i被聘用}={1应聘者i被聘用0应聘者i未被聘用

X = X 1 + X 2 + . . . + X n X=X_1+X_2+...+X_n X=X1+X2+...+Xn

现在我们分析一下HIRE-ASSISTANT执行的效率:

我们在上文已经假设每个应聘者是随机出现的,那么前 i i i​个应聘者中的任意一个都等可能的是目前最有资格的,那么应聘者 i i i​比其他应聘者更有资格的概率是 1 / i 1/i 1/i,也就是应聘者 i i i会以 1 / i 1/i 1/i​​的概率被雇佣。
E [ X i ] = P r { 应聘者 i 被雇佣 } = 1 / i E[X_i]=Pr\{\text{应聘者}i\text{被雇佣}\}=1/i E[Xi]=Pr{应聘者i被雇佣}=1/i

E [ X ] = E [ ∑ i = 1 n X i ] = ∑ i = 1 n E [ X i ] (期望的线性性质) = ∑ i = 1 n 1 / i = l n ( n ) + O ( 1 ) (调和级数) \begin{aligned} E[X] &= E[\sum_{i=1}^{n}X_i] \\ &=\sum_{i=1}^n E[X_i] \quad \text{(期望的线性性质)} \\ &=\sum_{i=1}^n 1/i \\ &=ln(n)+O(1) \quad \text{(调和级数)} \end{aligned} E[X]=E[i=1nXi]=i=1nE[Xi](期望的线性性质)=i=1n1/i=ln(n)+O(1)(调和级数)

根据上述分析的结果,我们可以知道,尽管我们需要面试 n n n个人,但平均起来,实际上大约只雇佣了他们之中的 l n ( n ) ln(n) ln(n)个人。

假设应聘者是随机次序的,那么算法HIRE-ASSISTANT总的雇佣费用平均情况下为 O ( c h l n n ) O(c_hlnn) O(chlnn)

我们之前也说过,我们不能限制输入的随机性,但是我们可以加一个中间层,不管输入的序列是什么,都产生一个均匀随机的序列,这样执行就不依赖于输入了,而是依赖于随机选择。

RANDOMZED-HIRE-ASSISTANT
randomly permute the list of candidates//随机输入 
best = 0
for i = 1 to n
	interview candidate i
	if candidate i is better than best
		best = i
		hire candidate i

我们只是做了一个简单的改变,很明显看出,这个随机算法的性能,与假设应聘者以随即次数出现所得结果是一样的。因为和之前相比,只是去掉了假设,换成了使用随机算法使输入随机化。

很多随机算法通过给定输入 变换排列以使输入随机化。接下来讨论两种随机方法,并给予证明。(我用我贫瘠的数学知识,我太废了)

第一种:为数组的每个元素 A [ i ] A[i] A[i]赋予一个随机的优先级 P [ i ] P[i] P[i],然后根据优先级对数组 A A A​中的元素进行排序。就是我们常说的置换策略。

例如: A = { 1 , 2 , 3 , 4 } A=\{1,2,3,4\} A={1,2,3,4}​​​​,随机的优先级 P = { 45 , 23 , 96 , 12 } P=\{45, 23, 96, 12\} P={45,23,96,12}​​​​(数值越小,优先级越高),那么就会产生一个数组 B = { 4 , 2 , 1 , 3 } B=\{4,2,1,3\} B={4,2,1,3}​​​​​。这个过程称为PERMUTE-BY-SORTING:

PERMUTE-BY-SORTING
n = A.length
let P[1...n] be a new array
for i = 0 to n
	P[i]=RANDOM(1,n^3)
sort A, using P as sort keys

这里使用 n 3 n_3 n3是为了让 P P P中所有的优先级尽可能唯一,现在假设所有的优先级都唯一

那接下来我们证明这个过程能产生一个均匀随机排列,即该过程可以等可能地产生数字1~n的每一种排列。换种说法就是,需要证明每一种排列发生的概率是 1 n ! \frac{1}{n!} n!1

先考虑每个元素 A [ i ] A[i] A[i]​分配到第 i i i​个的特殊排列开始,其实就是 A = B A=B A=B​。假设 E i E_i Ei​代表元素 A [ i ] A[i] A[i]​分配到第 i i i​个这个事件,则对所有的 i i i​,该事件发生的概率是:
P r { E 1 ∩ E 2 ∩ ⋅ ⋅ ⋅ ∩ E n − 1 ∩ E n } = P r { E 1 } ∗ P r { E 2 ∣ E 1 } ∗ P r { E 3 ∣ E 2 ∩ E 1 } ⋅ ⋅ ⋅ P r { E n ∣ E n − 1 ∩ ⋅ ⋅ ⋅ ∩ E 1 } \begin{aligned} &Pr\{E_1 \cap E_2 \cap···\cap E_{n-1} \cap E_n\} \\ \\ &=Pr\{E_1\}*Pr\{E_2|E_1\}*Pr\{E_3|E_2 \cap E_1\}···Pr\{E_n|E_{n-1}\cap···\cap E_1\} \end{aligned} Pr{E1E2En1En}=Pr{E1}Pr{E2E1}Pr{E3E2E1}Pr{EnEn1E1}
其中 P r { E 1 } Pr\{E_1\} Pr{E1}是从一个 n n n个元素的集合中选取一个优先级最高(即数值最小)的概率,那么 P r { E 1 } = 1 / n Pr\{E_1\}=1/n Pr{E1}=1/n P r { E 2 ∣ E 1 } Pr\{E_2|E_1\} Pr{E2E1}​就是从剩下的 n − 1 n-1 n1个元素中选取一个优先级最高的概率,那么 P r { E 2 ∣ E 1 } = 1 / ( n − 1 ) Pr\{E_2|E_1\}=1/(n-1) Pr{E2E1}=1/(n1)。以此类推, P r { E i ∣ E i − 1 ∩ . . . ∩ E 1 } = 1 / ( n − i + 1 ) Pr\{E_i|E_{i-1}\cap...\cap E_1\}=1/(n-i+1) Pr{EiEi1...E1}=1/(ni+1)。因此,
P r { E 1 ∩ E 2 ∩ ⋅ ⋅ ⋅ ∩ E n − 1 ∩ E n } = ( 1 n ) ( 1 n − 1 ) . . . ( 1 n − i + 1 ) . . . ( 1 2 ) ( 1 1 ) = 1 n ! Pr\{E_1 \cap E_2 \cap···\cap E_{n-1} \cap E_n\}=\bigg( \frac{1}{n} \bigg) \bigg( \frac{1}{n-1} \bigg)...\bigg( \frac{1}{n-i+1} \bigg)...\bigg( \frac{1}{2} \bigg)\bigg( \frac{1}{1} \bigg)=\frac{1}{n!} Pr{E1E2En1En}=(n1)(n11)...(ni+11)...(21)(11)=n!1
现在已经证明这个特殊的排列的发生的概率是 1 n ! \frac{1}{n!} n!1,现在需要扩展到其他的排列。如果 E i E_i Ei代表元素 A [ i ] A[i] A[i]分配到第 ξ ( i ) \xi(i) ξ(i)小的事件, A [ i ] A[i] A[i]被分配到了 j j j位置上,上述的证明同样适用。因此,如果要计算得到任何特定排列的概率,该计算与前面的计算完全相同,于是得到此排列的概率为 1 n ! \frac{1}{n!} n!1

这样我们就证明了PERMUTE-BY-SORTING过程可以产生输入的均匀随机的排列

第二种方法,就是原地排列给定数组。在进行第 i i i次迭代的时候,元素 A [ i ] A[i] A[i]是从元素 A [ i ] A[i] A[i] A [ n ] A[n] A[n]中随机选取的,第 i i i次迭代之后, A [ i ] A[i] A[i]不再改变。 RANDOMIZE-IN-PLACE过程的时间复杂度为 O ( n ) O(n) O(n)​。

RANDOMIZE-IN-PLACE
n = A.length
for i = 1 to n
	swap A[i] with A[RANDOM(i,n)]

接下来我们我们来证明这个过程可以产生均匀随机排列,和之前一样,证明每个排列生成的概率为 1 n ! \frac{1}{n!} n!1,证明方位使用的是循环不变式。

循环不变式和数学中的数学归纳法一样,都是一种演绎推理法,用于理解和证明算法的正确性。

循环不变式不是一个狭义上的一个式子,而是一个命题,在算法的起始状态、运行过程中和算法而技术时始终保持为真的一个命题。

循环不变式的三条性质:

  • 初始化:循环的第一次迭代之前,它为真。

  • 保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真。

  • 终止:在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法时正确的。

注意:循环不变式的前两条性质类似于数学归纳法,证明了一个基本情况和一个归纳步。此外,终止性不同于数学归纳法的做法,在归纳法中,归纳步是无限地使用的,这里当循环终止的时候,归纳随之终止。

对于RANDOMIZE-IN-PLACE,在进行第 i i i迭代之前,对每个可能的 ( i − 1 ) (i-1) (i1)排列(有 ( n − i + 1 ) ! (n-i+1)! (ni+1)!种),那么在子数组 A [ 1... i − 1 ] A[1...i-1] A[1...i1]​​包含某个 ( i − 1 ) (i-1) (i1)排列的概率为 ( n − i + 1 ) ! / n ! (n-i+1)!/n! (ni+1)!/n!

我们只需要证明,该循环不变式在第1次迭代之前为真,同时循环的每次迭代能够维持此不变式,并且当循环终止时,这个不变式能够得到证明算法正确的有用性质或结果。

初始化: 在第1次迭代之前,此时 i = 1 i=1 i=1,对于循环不变式来说,子数组 A [ 1...0 ] A[1...0] A[1...0]包含0排列的概率为 n ! / n ! = 1 n!/n!=1 n!/n!=1。我们很容易知道,子数组A是一个空数组,0排列也是空的,没有元素,那么该子数组包含0排列的概率必然为1。因此,该循环不变式在第一次迭代之前是为真的。

保持: 我们假设在进行第 i i i次迭代之前,每种可能的 ( i − 1 ) (i-1) (i1)排列出现在子数组 A [ 1... i − 1 ] A[1...i-1] A[1...i1]中的概率为 ( n − i + 1 ) ! / n ! (n-i+1)!/n! (ni+1)!/n!。我们要证明在第 i i i次迭代之后,每种可能的 i i i排列出现在子数组 A [ 1... i ] A[1...i] A[1...i]中的概率为 ( n − i ) ! / n ! (n-i)!/n! (ni)!/n!,依然保持循环不变式。

我们先考虑一个特殊的 i i i​排列, { x 1 , x 2 , . . . , x i } \{x_1,x_2,...,x_i\} {x1,x2,...,xi}​,这个排列中包含一个 ( i − 1 ) (i-1) (i1)​​排列 { x 1 , x 2 , . . . , x i − 1 } \{x_1,x_2,...,x_{i-1}\} {x1,x2,...,xi1},接着算法会在 A [ i ] A[i] A[i]中放入 x i x_i xi

E 1 E_1 E1表示前 i − 1 i-1 i1次迭代已经在 A [ 1... i − 1 ] A[1...i-1] A[1...i1]中构造了 ( i − 1 ) (i-1) (i1)排列的事件,根据循环不变式, P r { E 1 } = ( n − i + 1 ) ! / n ! Pr\{E_1\}=(n-i+1)!/n! Pr{E1}=(ni+1)!/n!

E 2 E_2 E2表示第 i i i次迭代在位置 A [ i ] A[i] A[i]放置 x i x_i xi的事件。当 E 1 E_1 E1 E 2 E_2 E2都恰好发生的时候, { x 1 , x 2 , . . . , x i } \{x_1,x_2,...,x_i\} {x1,x2,...,xi}排列会出现在 A [ 1... i ] A[1...i] A[1...i]中,因此:
P r { E 2 ∩ E 1 } = P r { E 2 ∣ E 1 } ∗ P r { E 1 } = 1 n − i + 1 ∗ ( n − i + 1 ) ! n ! = ( n − i ) ! n ! \begin{aligned} Pr\{E_2\cap E_1\}=Pr\{E_2|E_1\}*Pr\{E_1\}=\frac{1}{n-i+1}*\frac{(n-i+1)!}{n!}=\frac{(n-i)!}{n!} \end{aligned} Pr{E2E1}=Pr{E2E1}Pr{E1}=ni+11n!(ni+1)!=n!(ni)!
这符合循环不变式。

终止: 终止的时候, i = n + 1 i=n+1 i=n+1,子数组 A [ 1... n ] A[1...n] A[1...n]是一个给定n排列的概率为 ( n − ( n + 1 ) + 1 ) / n ! = 1 ! / n ! = 1 (n-(n+1)+1)/n!=1!/n!=1 (n(n+1)+1)/n!=1!/n!=1

综上所述,RANDOMIZE-IN-PLACE可以产生一个均匀随机排列。

从上面一大堆我们可以知道,随机算法使得算法不再依赖于输入,通常是解决一个问题最简单、最有效的方法。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值