[算法]由random(0,1)生成random(a,b)
问题描述:
描述RANDOM(a,b)过程的一种实现,它只调用RANDOM(0,1)。作为a和b的函数,你的程序期望运行时间是多少?
这是算法导论上的一道题。
这个没有固定答案,看了很多网上的解法之后,总结一下我的想法。首先,random(0,1)是随机的产生0,1两个整数,random(a,b)是随机产生[a,b]之间的整数。随机就是等概率的意思。生成random(a,b)等价于随机生成[0,b-a]内的一个数,然后在加上a就是[a,b]之间的数了。
解法一:
设k=b-a,假设2的c次方(以下用2^c表示)是第一个大于b-a的2的正整数次方,那么调用random(0,1)c次,产生一个c位的二进制数,对应一个[0,2^c-1]的十进制数,如果这个数在[0,b-a]之内,输出,否则继续生成c位二进制数,直到得到一个[0,b-a]之内的数为止。
下面是java代码的表示:
public int randomAB2(int a,int b){ int k=b-a; //找到第一个2^n大于k的n int k1=k; int c=0; while(k1!=0){ c++; k1=(int)Math.floor(k1/2); } int s=0; //生成0到k的数 do{ s=0; for(int i=0;i<c;i++){ s+=random01()*Math.pow(2, i); } } while(s>k); return s+a; }
期望运行时间:
第二个while循环的运行次数呈几何分布,每一次成功(也就是得到了[0,b-a]之内的数)的概率是---p=(b-a+1)/(2^c),这里的c应该是lg(b-a)向上取整,所以运行次数的期望是Tavg=1/p。
期望运行时间T(a,b)=Tavg*lg(b-a)*D。其中lg(b-a)是以2为底b-a的对数,D是第二个while循环内代码运行的时间,是个常数。这个貌似是个O(lg(b-a))级别的运行时间。
解法二:
这个方法来自http://blog.csdn.net/longhuihu/article/details/5864442这篇博文的解法一。这个方法也是先生成一个长度为k=b-a+1二进制序列---R1,R2,..,然后把二进制序列中值为0对应的[0,b-a]中的数删除,从而减小要生成数的范围。
下面是模拟这个过程的java代码:
public int randomAB(int a,int b){ int k=b-a+1; ArrayList<Integer> S=new ArrayList<Integer>(); ArrayList<Integer> temS; for(int i=0;i<k;i++){ S.add(i); } temS=copy(S); while(S.size()>1){ int n=S.size(); for(int j=n-1;j>=0;j--){ int r=random01(); if(r==0){ temS.remove(j); } } if(temS.isEmpty()){ temS=copy(S); } else{ S=temS; temS=copy(S); } } return S.get(0)+a; }
这个理解起来比较容易,但是分析期望运行时间比较复杂。
首先,要生成几次b-a+1长度的二进制序列才能达到缩减范围的目的?这也是个几何分布,每次试验成功的概率是p=1-2/2^k(因为只要不生成全0或全1就会成功)。那么试验次数的期望是Tavg=1/p。那么运行时间T(a,b)可以表示为T(a,b)=Tavg*k*D+T(x)。这个T(x)表示任何可能的缩小范围后的范围,x取值在1-(k-1)之间,D是循环内的常数运行时间。
这篇文章证明了这种做法最后输出的a,b之间的数肯定是等概率的,应用了数学归纳法,证明方法如下:
(1)能够等概率生成0,1
(2)假设有random(m),m<k等概率生成0-m之间的数。
(3)证明random(k)生成[0,k-1]之间的数也是等概率的:
定义事件:
Ai--输出第i个数
Bim--一轮筛选后m个数留下,其中包含第i个数
P(Ai|Bim)=random(m)=1/m,这是(2)的假设。
P(Ai,Bim)=P(Bim)*P(Ai|Bim)=P(Bim)*(1/m)。
P(Bim)=C((k-1),(m-1))/(2k-2)。C((k-1),(m-1))是个组合公式。
P(Ai)=∑P(Ai,Bim) for m=1:(k-1)。这是个求和公式,对m从1到k-1取值求和。
求解后的P(Ai)=1/k。