若|S1|≥k则调用Select(k,S1);
若(|S1|+|S2|)≥k,则第k小元素就是x;
否则就有(|S1|+|S2|)<k,此时调用Select(k-|S1|-|S2|,S3)。
定理:
若以等概率方法在n个数中随机取数,
则该算法用到的比较数的期望值不超过4n。
(假定n个数互不相同,如果有相同的数的话,
落在S2中的可能性会更大,比较数的期望值会更小一些。
)
证:
设C(n)是输入规模为n时,算法中所用到的比较次数的期望值。
并设取到任一个数的概率相同。
假定用上述算法取到的数x是第j小的数(j=1,2,…,n)。
若j>k,则需要调用Select(k,S1)。
∵|S1|=j-1(因为取到的x是第j小的数),
∴调用Select(k,S1)的比较次数的期望值为C(j-1)。
若j<k,则需要调用Select(k-j,S3)(j=|S1|+|S2|)。
∵|S3|=n-j,∴本次调用的比较次数的期望值是C(n-j)。
若j=k,则直接返回第j个元素,无需继续进行比较。
由于取j为1,2,…,n的概率相同,故有
C(n)=n+
(其中的n是:
获得S1,S2,S3所需比较次数)
=n+
=n+
由于C(i)是非减函数(在i<j时,总有C(i)≤C(j))
∴(
)在k=n/2时取得最大值。
(证明留作作业)
归纳法基础:
n’=1时无需比较,显然有C(n’)≤4n’。
归纳法假设:
设n’<n时,有C(n’)≤4n’。
由于C(n)是在k=n/2时取得最大值,于是有
C(n)≤n+
(注意式中的
均应为n/2,下同)
≤n+
(由归纳法假设,n’<n时,有C(n’)≤4n’)
≤n+
(n为奇数时n-n/2+1=n/2,故此时两和式相等)
=n+
=n+
(n*(n-1)/2-n/2*(n/2-1)/2)
≤n+
(本式中的
不向上取整)=4n-2
由于每个元素至少被比较一次,所以C(n)≥n,
∴随机化Select算法的比较次数期望值有:
n≤C(n)<4n。
Sherwood随机化方法(属LasVegas算法)
如果某个问题已经有了一个平均性质较好的确定性算法,
但是该算法在最坏情况下效率不高,此时引入一个随机数发生器
(通常是服从均匀分布,根据问题需要也可以产生其他的分布),将一个确定性算法改成一个随机算法,使得对于任何输入实例,
该算法在概率意义下都有很好的性能(e.g.Select,Quicksort等)。
如果算法(所给的确定性算法)无法直接使用Sherwood方法,
则可以采用随机预处理的方法,使得输入对象服从均匀分布
(或其他分布),然后再用确定性算法对其进行处理。
所得效果在概率意义下与Sherwood型算法相同。
TestingStringEquality(MonteCarlo算法)
设A处有一个长字符串x(e.g.长度为106),
B处也有一个长字符串y,A将x发给B,由B判断是否有x=y。
算法:
首先由A发一个x的长度给B,若长度不等,则x≠y。
若长度相等,则采用“取指纹”的方法:
A对x进行处理,取出x的“指纹”,然后将x的“指纹”发给B,
由B检查x的“指纹”是否等于y的“指纹”。
若取k次“指纹”(每次取法不同),每次两者结果均相同,
则认为x与y是相等的。
随着k的增大,误判率可趋于0。
常用的指纹:
令I(x)是x的编码,取Ip(x)I(x)(modp)作为x的指纹。
这里的p是一个小于M的素数,M可根据具体需要调整,
本例中取M=2n2。
由于0≤Ip(x)<p,
∴Ip(x)的二进制传输长度不超过log2p位。
设n是I(x)的二进制表示的长度,则有I(x)<2n。
由于p<M=2n2,故有log2p≤log(2n2)+1=O(log2n)。
∴传输的长度可以大大的缩短。
例:
设I(x)是106位二进制的数,即n=106,则M=2×1012≈240.8631
∴Ip(x)的位数不超过41位,传输一次可节省约2.5万倍。
根据下面所做的分析,可知错判率小于
。
如果取5种不同的p(即k=5)求出“指纹”再传输、检查5次,
则传输量可节省5,000倍。
此时错判率小于
。
错判率分析:
B接到指纹Ip(x)后与Ip(y)比较,如果Ip(x)≠Ip(y),当然有x≠y。
如果Ip(x)=Ip(y)而xy,则称此种情况为一个误匹配。
问题是:
误匹配的概率有多大?
若我们总是随机地去取一个小于M的素数p,则对于给定的x和y,
Pr[failure]=(使得Ip(x)=Ip(y)但xy的素数p(p<M)的个数)/
(小于M的素数的总个数)。
数论定理1:
设(n)是小于n的素数个数,则(n)≈
,
误差率不超过6%。
例:
n=5001000104105106107108109
(n)=951681229959278498664579576144550847478
∵M=2n2,Pr[failure]的分母
(M)≈
=
=
≈
=
Pr[failure]的分子≤使得Ip(x)=Ip(y)的素数p(p<M)的个数
=能够整除︱I(x)-I(y)︱的素数p(p<M)的个数
(∵a≡b(modp)iffp整除︱a-b︱)
数论定理2:
如果a<2n,则能够整除a的素数个数不超过(n)个。
(只要n不是太小)
∵︱I(x)-I(y)︱<max{I(x),I(y)}≤2n-1,
∴能够整除︱I(x)-I(y)︱的素数个数不超过(n)。
而满足p<M的素数则更少,∵当n≥7时,有2n2<2n。
∴Pr[failure]≤(n)/(M)≈
=
。
即误匹配的概率小于
。
∴当n很大时,误匹配的概率很小。
设xy,如果取k个不同的小于2n2的素数来求Ip(x)和Ip(y),
由于事件的独立性,因此事件均发生的概率满足乘法规则,
即k次试验均有Ip(x)=Ip(y)但xy(误匹配)的概率小于
。
∴当n较大、且重复了k次试验时,误匹配的概率趋于0。
PatternMatching(MonteCarlo算法)
给定两个字符串:
X=x1,…,xn,Y=y1,…,ym,看Y是否为X的子串?
(即Y是否为X中的一段。
)
该问题可用KMP算法在O(m+n)时间里获得正确结果,
但算法比较繁琐,函数计算比较复杂。
(此问题还可用Rabin-Karp算法、Boyer-Moore算法等)
考虑随机算法(用brute-force思想)
记X(j)=xjxj+1…xj+m-1(从X的第j位开始、长度与Y一样的子串),
从起始位置j=1开始到j=n-m+1,我们不去逐一比较X(j)与Y,
而仅逐一比较X(j)的指纹Ip(X(j))与Y的指纹Ip(Y)。
由于Ip(X(j+1))可以很方便地根据Ip(X(j))计算出来,
故算法可以很快完成。
不失一般性,设xi(1≤i≤n)和yj(1≤j≤m)∈{0,1},即X,Y都是0-1串。
Ip(X(j+1))=(xj+1…xj+m)(modp)
=(2(xj+1…xj+m-1)+xj+m)(modp)
=(2(xj+1…xj+m-1)+2mxj-2mxj+xj+m)(modp)
=(2(xjxj+1…xj+m-1)-2mxj+xj+m)(modp)
(∵(xy+z)(modp)=(x(ymodp)+z)(modp))
=(2((xjxj+1…xj+m-1)modp)-2mxj+xj+m)(modp)
=(2*Ip(X(j))-
xj+xj+m)(modp)(﹡)
∴Ip(X(j+1))可以利用Ip(X(j))及(﹡)式计算出来。
算法
1、随机取一个小于M的素数p,置j←1;
2、计算Ip(Y)、Ip(X
(1))及Wp(=2mmodp);
3、Whilej≤n-m+1do
{ifIp(X(j))=Ip(Y)thenreturnj/﹡X(j)极有可能等于Y﹡/
else{使用(﹡)式计算出Ip(X(j+1));j增1}
}
4、return0;/﹡X肯定没有子串等于Y﹡/
时间复杂度:
Y、X
(1)、2m均只有m位(二进制数),
故计算Ip(Y)、Ip(X
(1))及2mmodp的时间不超过O(m)次运算。
Ip(X(j+1))的计算:
由于2*Ip(X(j))只需要在Ip(X(j))后加个0;
当xj为1时,第二部分Wp*xj就是Wp,当xj为0时该部分为0;
xj+m或为0或为1,然后进行加减法(O
(1)时间)就可得到
2*Ip(X(j))-
xj+xj+m。
但此式还要对p取模。
由于0≤2*Ip(X(j))≤2p-2,0≤
xj≤p-1,0≤xj+m≤1,
因此2*Ip(X(j))-
xj+xj+m的值在[-(p-1),2p-1]之间。
故实际计算时,若上式是负值,则加上p后即得Ip(X(j+1));
若为非负,则看其是否小于p,小于p则已得Ip(X(j+1));
若大于等于p,则减去p后即得Ip(X(j+1))。
故Ip(X(j+1))的计算只需用O
(1)时间。
由于循环最多执行n-m+1次,故这部分的时间复杂度为O(n)。
于是,总的时间复杂性为O(m+n)。
失败的概率:
当Y≠X(j),但Ip(Y)=Ip(X(j))时产生失败。
Ip(Y)=Ip(X(j))当且仅当p能整除|Y-X(j)|。
当p能整除|Y-X(j)|时,p当然也能整除
|Y-X
(1)||Y-X
(2)|…|Y-X(j)|…|Y-X(n-m+1)|(∵p素数,反之也成立),
由于|Y-X(j)|不超过m个二进制位,∴|Y-X(j)|<2m。
∴|Y-X
(1)||Y-X
(2)|…|Y-X(n-m+1)|<(2m)n-m+1≤2mn。
由数论定理2(如果a<2n,则能够整除a的素数个数不超过(n)个),
能整除|Y-X
(1)||Y-X
(2)|…|Y-X(n-m+1)|的素数个数不超过(mn)个。
于是
Pr[failure]=(Y不含在X中、但p(p<M)能够整除|Y-X
(1)||Y-X
(2)|…|Y-X(j)|…|Y-X(n-m+1)|的素数的个数)/小于M的素数的个数
≤(mn)/(M)=(mn)/(2mn2)(取M=2mn2)
≈(mn/loge(mn))/(2mn2/loge(2mn2))=loge(2mn2)/2nloge(mn)
(m≥2时有)≤loge((mn)2)/2nloge(mn)=1/n
即失败的概率只与X的长度有关,与Y的长度无关。
当m=n时,问题退化为判定两个字符串是否相等的问题。
本算法可以转成LasVegas算法:
当Ip(Y)=Ip(X(j))时,不直接returnj,而去比较Y和X(j),
即在returnj之前加一个判断看Y和X(j)是否相等,
相等则returnj,否则继续执行循环。
这样,
如果有子串X(j)与Y相匹配,该算法总能给出正确的位置
(即算法出错的概率为0)。
∵在最坏情况下算法执行O(mn)时间,
而p能整除|I(Y)-I(X(j))|概率的不超过
,故
算法的时间复杂性的期望值不超过
。
作业:
分别用KMP、MonteCarlo和LasVegas算法编制3个程序,
随机生成不小于5000对的、长度不等的01串X和Y(三个程序生成相同的串),然后统计算法的执行时间、MonteCarlo算法出错的比率,并根据运行结果对三种算法进行深入的比较。
注意,
先利用本题下方所给素数实现上述算法,学完素数判定算法之后,
将该算法编程,产生少量的大素数并用数组保存起来,
以供上述随机算法使用(素数判定算法写在上述随机算法之外)。
素数(每种情况给7个数)
(250以内)211223227229233239241
(500以内)461463467479487491499
(1000以内)953967971977983991997
(5000以内)4957496749694973498749934999
(10000以内)9923992999319941994999679973
(100000以上)100003100019100043100049100057100069100103