zoukankan      html  css  js  c++  java
  • 算法导论第五章:概率分析和随机算法

    注:由于输入公式困难,本篇用 C(n,k)表示组合,n选k;P(n,k)表示排列,n选k;

    k=a~b 表示求和。

    5.1 雇佣问题

    假设你要雇佣一个新的办公室助理,雇佣代理每天想你推荐一个应聘者(连续推荐n个),你面试这个人,如果这个应聘者比目前的办公室助理更优秀,你就会辞掉当前的办公室助理,然后聘用这个新的。面试一个人需付给雇佣代理一笔费用,聘用办公助理也需要费用。

    假设面试费用为Ci,雇佣的费用为Ch,假设整个过程中雇佣了m次,于是总的费用是 nCi+mCh。由于n是固定值,总费用的变化取决于m值。


    这个场景用来当做一般计算范式的模型,通常情况下我们需要检查队列中的每个成员,并且维护一个目前的获胜者,来找出序列中的最大或最小值。雇佣问题是对哪一个成员当前获胜的更新频繁程度建立模型。


    最坏情况

    最坏情况下,我们雇佣了每一个应聘者,m=n。


    概率分析

    事实上,我们既不能得知应聘者出现的顺序,也不能控制这个顺序,因此我们使用概率分析。概率分析就是在问题的分析中使用概率技术。为了使用概率分析,必须使用关于输入分布的知识或者对其做假设,然后分析算法,计算出一个期望的运行时间。这个期望值通过对所有可能的输入分布算出。

    有些问题,我们对所有可能的输入集合做某种假设。对于其他问题,可能无法描述一个合理的输入分布,此时就不能使用概率分析方法。

    在雇佣问题中,可以假设应聘者是以随机顺序出现的。假设可以对任何两个应聘者进行比较并确定哪个更优;换言之,在所有的应聘者之间存在这一个全序关系。因此可以使用从1到n的唯一号码来标志应聘者的优秀程度。用rank(i)来表示应聘者i的名次。这个有序序列<rank(1),rank(2),..., rank(n)>是序列<1,2,...,n>的一个排列。说应聘者以随机的顺序出现,就等于说这个排名列表是1到n的n!中排列中的任何一个,每种都以相等的概率出现。


    随机算法

    在许多情况下,我们对输入分布知识知之甚少;即使知道关于输入分布的某些信息,也无法对这种分布建立模型。然而通过使一个算法中的某些部分的行为随机化,就常常可以利用概率和随机性作为算法设计和分析的工具。

    比如在雇佣问题中,如果雇佣代理给我们一份应聘者的名单,每天我们随机地挑选一个应聘者进行面试,从而确保了应聘序列的随机性。

    更一般地,如果一个算法的行为不只有输入决定,同时也由随机数生成器所产生的数值决定,则称这个算法是随机的


    练习5.1.2

    假设Random(a,b)以相同概率返回a到b之间的任何一个数字,描述Random(a,b)过程的一种实现,它只调用现有实现Random(0,1)。作为a和b的函数,你的程序的期望运行时间是多少?假设Ramdom(0,1)的运行时间是常数。

    解法1:

    分析:假设k=b-a+1。那么Random(a,b)的本质就是从k个数字中随机等概率地选出一个,不妨用Random(k)来表示。 假设这样来进行一轮操作,利用Random(0,1)产生长度为k个0,1序列S = (R1,R2,...,Rk)。如果R1=R2=...=Rk,这一轮操作就作废了,重新进行;否则如果Ri=0就将第i个数淘汰,问题就转化了为对少于K个数的随机选择。这里不妨假设对于m<k,算法Random(m)已经实现。

       Ai  ->事件第i个数最终被选中
       Bim ->事件第i个数和其他m-1个数在这一轮筛选中被保留
       Cim ->事件第i个数在这m个保留下来的子集中最终被选中
     
       Pr{Ai/Bim} = Random(m) = 1/m; 
       Pr{Ai,Bim} = Pr{Bim}*1/m;
       Pr{Ai} = ∑m=1~k-1Pr{Ai,Bim}  =   Pr{Ai} = ∑Pr{Bim}*1/m;
       Pr{Bim} = C(k-1,m-1)(1/(2n-2)), 由于我们忽略了s为全1全0两种序列,所以其他序列的概率是1/(2n-2)
      
       求解后可得 Pr{Ai} = 1/k。

    由于Random(2)是现成的,于是通过数学归纳法可知Random(k)通过上述算法都可以求解。

    算法Random(k)的期望运行时间:只要不是出现s为全0全1这两种状况,算法规模就可以缩小。这两种状况的概率为 2/2k。依据伯努利试验的结论期望进行 Sk =  1/(1-2/2k)轮筛选就可以缩小问题规模。

    T(k) = 筛选轮次*k*T(2) + T(i) ;  i 以一定的概率分布于:1~k-1。

    设常量 C = T(2)

    E[T(k)] =Sk*k*C +  E[T(1)]*C(k,1)(1/(2k-2)) +...+E[T(k-1)]*c(k,k-1)(1/(2k次-2)) 
    这个递推式解不了。

    简化算法

    上面的算法给出了比较严格的推理过程,但算法过程可以简化:如果k为偶数,则将S均分成两组,通过一次R(0,1)来淘汰其中一组;如果S为奇数,则用上述方法来分组,将占多数的一组淘汰。可以证明这个算法后也是正确的。只要在某一轮的测试中R(0,1)的输出为全0或全1,问题的规模就可以缩小一半。这样算法的期望运行时间递推式可以表示为 E[T(k)] <= Sk*k*C + E[T(k/2)],  应该为Θ(k)。

    解法2:

    假设m=2k次,那么Random(m)可以很容易有Random(k)获得。假设这个m>k&&m<2k,那么可意由这种方式来设计Random(K):

    Random(K)

       value = Random(m)

       if (value<=k)

            return value

       else

            return Random(K)

    算法本质上也是一个伯努力实验。

    也可以取m大一些,比如m>Nk:

    Random(K)

       value = Random(m)

       if (value<=Nk)

            return value/N

       else

            return Random(K)

    练习5.1.3
    假设你希望以各1/2的概率输出0和1。你可以自由使用一个输出0或1的过程BIASED-RANDOM。它以概率p输出1,以概率1-p输出0,其中0<p<1,但是你并不知道p的值。给出一个利用BIASED-RANDOM作为子程序的算法,返回一个无偏向的结果。你的算法的期望运行时间是多少?

    分析:设计的思路是利用对称性。 假设有两个基于BIASED-RANDOM的伯努利试验序列A、B。每个试验序列都会产生0,1值序列;每一轮A和B各进行一次,如果该轮试验的结果是ai>bi(即ai=1,bi=0)则算法结束,结果为1;如果ai<bi则算法结束结果为0;如果ai=bi则开始下一轮迭代。

    由于每一轮试验都是独立的,所以只要能够证明每一轮在得出结果的条件下,得出1和得出0的概率相等就可以了。

    事件 Mi : 第i轮试验得出了结果,即ai!=bi;
    事件 Ai :结果为1;
    事件 Bi :结果为0;

    Pr{Ai/Mi} = Pr{Ai,Mi}/Pr{Mi};
    Pr{Ai,Mi} = Pr{ai=1,bi=0} = p*q;
    Pr{Bi,Mi} = Pr{ai=0,bi=1} = q*p;

    因此有Pr{Ai/Mi} = Pr{Bi/Mi}

    该算法的执行时间取决于进行多少轮试验后达到ai!=bi。 对任意i,Pr{ai=bi} = pp+qq Pr{ai!=bi}= 2pq。这又是一个伯努利试验。期望试验次数E[n] = 1/2pq。


    5.2指示器随机变量

    定义:

    为了分析包括包括雇佣分析在内的许多算法,我们将使用指示器随机变量,它为概率和期望之间的转换提供了一个便利的方法,给定一个样本空间S和时间A,那么事件A对应的指示器随机变量
            1 如果A发生
    Xa =  0 如果A没有发生

    E[Xa] = Pr{A}

    利用指示器随机变量分析雇佣问题:

       假设应聘者以随机的顺序出现,令X作为一个随机变量,其值等于雇佣一个新的办公室次数。那么 E[X] = ∑xPr{X=x},但这一计算会很麻烦。

       我们定义n个和每个应聘者是否被雇佣对应的变量,Xi为对应于第i个应聘者被雇佣这个事件的指示器随机变量。有X=X1+X2+...+Xn。
    E[Xi] = Pr{Xi} = 1/i,因为应聘者是随机出现的,所以第i个应聘者比前面i-1个优秀的概率是1/i。

    因此E[X] = 1+1/2+1/3+...+1/n。


    练习5.2.2:
    在雇佣问题中,假设应聘者以随机的顺序出现,正好雇佣两次的概率是多少?

    分析:第一个应聘者肯定被雇佣,最优秀的应聘者也肯定被雇佣。如果第一个就是最优秀的那么指挥雇佣一次,这种情况就可以排除。那么恰好雇佣两次就意味这在第一个应聘者和最优秀的应聘者之间的应聘者都不如第一个优秀。由于应聘者是随机出现的,那么任何一种序列都是等可能的,只要能求出满足前一要求的排列数目就能求出概率。

        假设应聘者对应于集合S={1,2,3,...,n}。应聘者的优秀程度就有数值来标记。假设我们将最大的数n暂时抽出。将剩下的1~n-1个分成两组,在由这两组形成两个序列S1、S2,要求S1的第一个数是S1中最大的数,S2完全随机排列, 那么 序列 S1nS2 就是满足需求的一种n排列。反过来,每种满足需求的排列都可以表示成这种形式。

       这种排列的数目有m = ∑k=1~n-1C(n-1,k)(k-1)!*(n-1-k)! --k=1~n-1
       概率为: m/n! = 1/n∑k=1~n-11/k

    练习5.2.4:

     帽子保管问题:有n位顾客,他们每人给餐厅负责保管帽子的服务生一顶帽子,服务生以随机的顺序将帽子归还给顾客,请问拿到自己帽子的客户的期望数目是多少?

    分析:设指示器随机变量Xi对应于事件“顾客i拿到自己的帽子”, 可知 E[Xi]= 1/n。于是拿到自己帽子客户的期望数目是 ∑E[Xi] = 1。

    练习5.2.5:

    假设A[1...n]是由n个不同数构成的数组,如果i<j且A[i]>A[j],则称(i,j)对位A的逆序对。假设A中元素形成<1,2,...,n>上的一个均匀随机排列,利用指示器随机变量来计算A中逆序对的期望。

    分析:设指示器随机变量Xij对应于事件“A[i]>[Aj]”, 那么 E[Xij]= 1/2。于是逆序对的期望数目为∑1=<i<j<=nE[Xij] = n(n-1)/4。


    5.3随机算法

    在雇佣问题中,如果应聘者是以随机顺序出现的话,雇佣一个新的办公室助理的期望次数是lnn。这个算法是随着输入的变化而变化的。对于某个特定的输入,它总是会产生固定的雇佣次数。这样就存在昂贵的输入,不贵的输入和适中的输入。如果先对应聘者进行随机排列,此时随机发生在算法上而不是发生在输入分布上。每次运行这个算法,执行依赖于随机的选择,而不是依赖于输入。这就是随机算法和概率分析的区别。


    随机排列数组

    许多随机算法通过排列给定的输入数组来使输入随机化。这里讨论两个随机化方法,假设给定一个数组A,它包含元素1到n。我们的目标是构造这个数组的一个随机排列。

    Permute-By-Sorting

     一个常用的方法是为数组A[i]赋一个随机的优先级P[i],然后根据优先级对数组中的元素进行排序。这个过程称为Permute-By-Sorting。

    Permute-By-Sorting
    1 n <- length[A]
    2 for i<- 1 to n
    3   do P[i]=RANDOM(1,n3
    4 Sort A, using P as keys
    5 return A

    第三行在范围1~n3之间选取随机值,是为了让p中的所有优先级尽可能唯一。

    现证明:如果所有的优先级是唯一的,那么Permute-By-Sorting可以产生输入的均匀随机排列。

    对Ai产生第ki大的优先级,那么k1,k2,...,kn是1~n的一个排列,该排列决定了最终的A排列。证明K1K2...Kn是1~n一个均匀随机排列即可。假设某个1~n的均匀随机排列s1...Sn,假设分别以Xi代表事件Ki等于Si。
    于是 Pr{X1∩X2∩...∩Xn} = Pr{X1}*Pr{X2|X1}*Pr{X3|X2∩X1}...{Pr{Xn|Xn-1∩Xn-2∩...∩X1} = 1/n!
    应为Pr{X1} = 1/n; Pr{X2|X1} = 1/n-1;...

    Permute-In-Place

    一个更好的方法是原地排列给定的数列。
    Permute-In-Place
    1 n <- length[A]
    2 for i <- 1 to n
    3   do swap A[i]<-->A[Random(i,n)]

    使用循环不变式来证明。证明第i次迭代后,产生的是A的一个均匀随机i排列。

    初始:第一次迭代之前,条件满足。
    保持:假设第i次迭代产生了排列 X1X2...Xi,只要证明对任意特定值x1,x2,...xi∈A, Pr{X1=x1∩X2=x2∩...∩Xi=xi}的概率相等。 Pr{X1=x1∩X2=x2∩...∩Xi=xi} = Pr{Xi=xi | X1=x1∩X2=x2∩...∩Xi-1=xi-1}*Pr{X1=x1∩X2=x2∩...∩Xi-1=xi-1} = 1/n-i+1 * Pr{X1=x1∩X2=x2∩...∩Xi-1=xi-1}。由于已知排列X1X2...Xi-1是一个均匀随机i-1排列。所以得证。 其实依次类推可知Pr{X1=x1∩X2=x2∩...∩Xi=xi}= 1/n*1/n-1*...1/n-i+1 = (n-i)!/n!。
    终止:可知任意排列的概率为 1/n!。


    练习5.3.3

    假设修改Permute-In-Place算法,不是将元素A[i]与A[i...n]中的随机一个元素进行交换,而是将它与数组任何位置上的随机元素相交换:
    Permute-With-All
    1 n <- length[A]
    2 for i <- 1 to n
    3   do swap A[i]<-->A[Random(1,n)]
    这段代码会产生均匀随机排列吗?

    分析:这问题没有找到严谨的证明方法,不过有一个投机的证明方法:每一步交换的方法有n中,那么依据乘法原理整个过程的交换序列有n的n次种,每一种交换序列产生唯一的一个最终元素序列。元素序列有n!种。由于n的n次并不能被n!整除,所以不可能等概率地产生元素序列。

    练习5.3.4

    证明程序PERMUTE-BY-SORTING的数组P中,所有元素都唯一的概率至少是1-1/n。

    分析:假设Random(1,n3)产生的n个数值为a1,a2,...,an;事件Xi表示a1~ai互不相等。Xn = {an不等于a1...an-1之一} ∩ Xn-1,于是 Pr{Xn} = Pr{an不等于a1...an-1之一 | Xn-1} *Pr{Xn-1} = (n3-n+1/n3)*Pr{Xn-1}。

    递推得 Pr{Xn} = ∏k=1~n(n3-k+1)/n3  > (1-1/n2)n >  C(n,0)*1 - C(n,1)(-1/n2) = 1-1/n。

    练习5.3.6

    解释如何实现算法Permute-By-Sorting,来处理两个或更多优先级相同的情况。亦即即使有多个优先级相同,算法也必须产生一个均匀随机序列。

    分析:对几个优先级相同的项,再进行一轮随机优先级排序;如果再有相同,再进行一次...。思想就是要确保这几个优先级相同的项得到随机的排列。
     

    5.4概率分析和指示器随机变量的进一步使用

    5.4.1 生日悖论
    一个房间里面的人数要达到多少,才能使有至少两个人生日相同的概率达到1/2。
    分析1:
       假设一年的天数为Y天,对房间里的所有人进行编号 m1,m2,...,mn。假设事件 Ai = mi与m1~mi-1的生日不同, 事件Bi=m1~mi生日互不相同;
    于是 Bk = ∩Ai [i=1~k],
    Pr{Bk} = Pr{Bk-1∩Ak} = Pr{Bk-1}*Pr{Ak|Bk-1} =Pr{Bk-1}*(Y-k+1)/Y。
    依据递推式 Pr{Bn} = ∏(1-(i-1)/Y) {i=1~n}   求使Pr{Bn} < 1/2的最小n值。

    分析2:利用指示器随机变量
    令指示器变量 Xij 对应于事件 {人i和人j生日相同}
    E[Xij] = 1/Y;
    ∑E[Xij] = 1/Y * k(k-1)/2;

    随机变量 X = ∑Xij 表示生日相同的两人对的对数;

    如果取Y=356天,则只要k>=28,就可以期望至少有两个人生日。

    注意:依据期望来反推概率只是简单而近似的分析,并不如分析1准确。不过虽然两种分析方法的结果不一致,但是都是Θ(n1/2)。


     


     

  • 相关阅读:
    CDH健康检查报DATA_NODE_BLOCK_COUNT告警
    log4net 日志不能输出
    beetsql 入门学习
    http headers详解
    python3 urllib学习
    python学习疑难1 -- 解决python3 UnicodeEncodeError: 'gbk' codec can't encode character 'xXX' in position XX
    Filter的应用
    SQL简单存储过程
    脑残问题收集
    core 标签简单使用
  • 原文地址:https://www.cnblogs.com/longhuihu/p/10423406.html
Copyright © 2011-2022 走看看