概率分析和随机化

来源:互联网 发布:招投标软件下载 编辑:程序博客网 时间:2024/06/07 19:17

概率分析和随机算法 学习笔记

 

         随机数在随机化算法设计中扮演着十分重要的角色。在现实计算机上无法产生真正的随机数,因此在随机化算法中使用的随机数都是一定程度上随机的,即伪随机数。线性同余法是产生伪随机数的最常用的方法。由线性同余法产生的随机序列a0,a1,…,an满足

                                                            a0 =d

                                             an =(ban-1 +C  )mod m                                                                                               (1)

 

其中b³0c³0d£md称为该随机序列的种子。如何选取该方法中的常数bcm直接关系到所产生的随机序列的随机性能。从直观上看,m应取得充分大,因此可取m为机器大数,另外应取gcd(m,b)=1,因此可取b为一素数

大多数程序设计语言也提供随机函数。比如C++有如下函数可实现随机化。在标准库<cstdlib>中的srand()rand()函数实现了随机化。由于计算机产生的是伪随机数,所以我们可以利用srand()设置种子。如果是在程序内循环,那么下次产生的数是以上次的数为种子的,从(1)中可以看出。一般为了得到更接近于实际意义的随机数,我们一般都是采用系统时间作为最初的种子。如下:

              #include <cstdlib>
#include<ctime>
Void main(){
              srand( time(NULL) );

              for( i = 0; i < 10; i++ )

               printf( "Random number #%d: %d/n", i, rand() );

}

注:time_t time(time_t *time) 返回当前系统时间(秒数)。如果指定参数,则将时间存入到参数中。

1、随机化算法的定义:

   随机算法是指在程序中使用随机函数,随机函数的返回值直接或间接的影响程序的执行流程和执行结果。

根据这个定义,并不是用了i¬RANDOM(N)就是一个随机算法,变量i除了在这里被赋予一个随机值之外,在其它地方从未出现过。显然,如果这个算法没有在其它地方用过随机函数,上面这条语句就无法影响执行的流程或结果,这个算法就不能称为随机化算法。

另一方面,若一个算法是随机化算法,则它执行的流程或结果就会受其中使用的随机函数的影响。我们按影响的性质和程度分三种情况:

1.随机不影响执行结果。这时,随机必然影响了执行的流程,其效应多表现为算法的时间效率的波动。比如下面要说的随机化快速排序。

2.随机影响执行结果的正确性。在这种情况中,原问题要求我们求出某个可行解,或者原问题为判定性问题[3],随机的效应表现为执行得到正确解的概率。

比如生日悖论。

3.随机影响执行结果的优劣。这时,随机的效应表现为实际执行结果与理论上的最优解或期望结果的差异。比如平时我们用骰子玩比大小的游戏、抽奖等。

23种情况中,随机的影响还可能伴随有对执行流程的影响。

2、指示器随机变量(引自<<算法导论>>)

在分析时使用指示器随机变量。指示器为概率与期望之间的转换提供了便利的方法。指示器随机变量相当于一个事件。比如:

                   确定在抛一枚均匀硬币时正面向上的期望次数,那么正面向上的事件H就相当于指示器随机变量XH,这个变量计算抛硬币正面向上的次数:

                                                       

                                                                              1        事件H发生

XH =

0         事件H不发生

 

其实相当于概率分布,事件H发生时为1,不发生时为0,用P(H)表示发生的概率,可知p(H)=1/2 。则期望为:

         E(XH)=1 * p(H) +0 *(1-p(H))=p(H)=1/2

这在概率论里和0-1分布很相似。或者可以理解为伯努利实验,也就是将一个实验重复的进行n次,其中每次事件A发生的概率恒为p

Pn(k)= C(k,n) *pK *(1-p)(n-k)

3、可以实现随机的方法

       1)、将一个文件中的数,读进数组时,随机的放在数组中。

       2)、通过将数组中的元素用随机算法,随机组合。有如下两种算法可以完成。

Void PermuteBySorting(int A[]){

       int n=a.length;

Random rand=new Random();

Int P[]=new int[n];

       for(int i=0;i<n;i++){

       P[i]=rand.nextInt(Math.pow(n,3))

       }

//在排序P时,用下标的对应关系将A也排序

return 0;

}

另一种更简单的:

Void RandomizeInPlace(int A[]){

       Int n=a.length;

       For(int i=0;i<n;i++)[

//交换A[i]A[random(I,n)]

}

}

这两种算法都可以使得出现最坏情况的概率为1/(n!)

因此,能够达到更好稳定性。

随机化算法的原理与设计

随机化算法的基本原理是:当某个决策中有多个选择,但又无法确定哪个是好的选择,或确定好的选择需要付出较大的代价时,如果大多数选择是好的,那么随机地选一个往往是一种有效的策略。通常当一个算法需要作出多个决策,或需要多次执行一个算法时,这一点表现得尤为明显。

这个原理使得随机化算法有一个共同的性质:没有一个特别的输入会使算法执行出现最坏情况。最坏情况可以表现在执行流程中(主要是时间效率),也可以表现在执行结果中。

根据这个原理,我们设计随机化算法时,通常一某个算法为母板,加入随机因素,使得算法在难以作出决策时随机地选择。在设计执行结果受随机影响的算法时,我们还可以多次执行算法,使算法不断逼近正确解或最优解[6]

以上只是设计随机化算法的基本方式。实践中,随机化算法的设计没有公式可套。我们必须具体问题具体分析,深入研究,设计出好的随机化算法。

举例

1、            生日悖论

如果一个房间里有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50%。这就意味着在一个典型的标准小学班级(30)中,存在两人生日相同的可能性更高。对于60或者更多的人,这种概率要大于99%

分析:不计特殊年月先计算房间里生日不相同的概率

P=(365/365)× (364/365) ×(363/365) ×(362/365)× ... ×(365-n+1/365)

则相同的概率为:p1=1-p

所以当n=23p1=0.507,n=100时,p1=0.99999

生日悖论可以用于检测哈希函数发生碰撞的概率,n位哈希表发生碰撞测试次数的概率为2n/2次而不是2n

快速排序的随机化算法。

一般的快速排序的效率受到输入序列的影响,平均的时间复杂度为O(nlog2n)

而,最坏的情况时间复杂度达到Q(n2)。这在某些时候是无法忍受的。因此引入了随机算法,以是算法的稳定性得到保证。由于随机化后出现最坏的情况的概率为1/(n!)

C++一次划分源代码如下:

int  Partition(int A[],int first,int end){

      int i=first;

      int j=end;

      srand(time(NULL));

      int x=rand()%(end-first);

      int tmp;

      while(i<j){

           while(i<x &&A[i]<=A[x])i++;

           if(i<x){

                 tmp=A[i];

                 A[i]=A[x];

                 A[x]=tmp;

                 i++;

           }

           while(x<j && A[x]<=A[j])j--;

           if(x<j){

                 tmp=A[x];

                 A[x]=A[j];

                 A[j]=tmp;

                 j--;

           }

      }

      return i;

}