九阴真经万寿任务:随机数生成算法调查和思考

来源:百度文库 编辑:中财网 时间:2024/04/28 08:17:33

1、线性同余算法:

      现在用得最广泛的伪随机数产生算法就是所谓的线性同余算法。其随机数序列{Xn}由方程:Xn+1 = ( aXn + c ) mod m得到,其中m>0称为模数,0≤ a 当m、a、c、X0都是整数时,通过这个方程就能产生一系列[0,m)范围内的整数了。

      很明显,对一个线性同余随机产生算法来说,最重要的是m、a、c的选择。我们希望产生的序列足够长,能包含[0,m)内所有的数,并且产生的数是随机的,最好能用32bit算术高效实现。于是乎为了有足够的空间产生足够长的队列,我们会使m足够大;为了使序列足够完整,足够象随机序列,我们会发现当m是素数,c为0时就可以使序列的周期(即序列不重复的长度)对于一定的a达到m-1;要能用32bit算术高效实现,我们的m当然不能超过32bit,幸好刚好有一个满足上述条件的很方便的素数2^31-1给我们用,于是产生函数就变成了Xn+1 = ( aXn) mod ( 2^31 – 1 )。在超过20亿个a的可选取值中,只有很少的几个可以满足上述所有条件,其中之一就是a = 7^5 = 16807。于是我们可以实现如下:
     unsigned myrand()
     {
         return (seed = (seed * 10807L) & 0x7fffffffL);
     }
      这种产生器得到了广泛的使用和比其它产生器都彻底的测验,其产生的随机数有在分布上有很好的特性,但是却不具备良好的不可预测性。所以我们在使用时往往会以系统时钟(模m)或系统时钟与随机数的和(模m)作为种子来重新启动序列,这也是现在绝大多数随机数产生器的做法。
       另一种产生伪随机数序列的方法就是使用加密逻辑,通过加密算法的雪崩效应,输入的每一位改变就会造成输出的巨大改变,并且假如密钥被妥善保存的话从序列中前面的数推出后面的数在计算上是不可行的,于是我们可以使用一个简单的累加器来作为输入,得到的随机序列可以直接使用(当然也可以把系统时间等等因素考虑进去作为输入来产生更随机的序列,不过这通常是没有必要的),这种方法常常用来产生会话密钥或是现时。具体采用的加密逻辑和运算方式有很多,这里就不一一介绍了,大家感兴趣可以找本密码学的书来看看。
        在密码学领域里比较流行的一种产生安全的伪随机数的方法是BBS产生器,它用其研制者的名字命名(Blum Blum Shub,不是我们经常灌水的那个BBS),它的密码编码强度据说具有最强的公开证明。首先,我们找两个大素数p和q,要求它们被4除都余3,令n = p×q,再选择一个随机数s,使s与n互素,然后我们通过如下算法产生一系列比特Bi:
X0 = (s^2)mod n,
for i = 1 to ∞
Xi = (Xi-1^2)mod n
Bi = Xi mod 2
每次迭代都只取出最低位的bit。可以看出BBS产生随机数的过程非常复杂,运算量很大,嗯,这就是为安全性付出的代价-

2、平方截取法:

找一个大数,如123456789
平方,得15241578750190521
取中段157875019
把它平方,得24924521624250361
取中段452162425
平方.......
这样能得一个伪随机序列
123456789
157875019
452162425  

3、C语言算法示例:

/*1.从同一个种子开始*/
#include
#include
static unsigned long int next=1;
int rand0(void)
{
next=next*1103515245+12345;
return (unsigned int)(next/65536)%32768;
}
int main(void)
{
int count;
for(count=0;count<5;count++)
    printf("%hd\n",rand0());
getch();
return 0;
}


/*2.重置种子*/
#include
#include
static unsigned long int next=1;


int rand1(void)
{
next=next*1103515245+12345;
return (unsigned int)(next/65536)%32768;
}


void srand1(unsigned int seed)
{
next=seed;
}


int main(void)
{
int count;
unsigned int seed;
printf("please input seed:");
scanf("%u",&seed);
srand1(seed);
for(count=0;count<5;count++)
    printf("%hd\n",rand1());
getch();
return 0;
}


/*3.利用利用时钟产生种子
ANSI C程序库提供了rand()函数来产生随机数
ANSI C程序库提供了srand()函数来产生种子
ANSI C程序库提供了time()函数返回系统时间。
*/

#include
#include
#include
#include
#include
int main(void)
{
   int i;
   time_t t;
   clrscr();
   t = time(NULL);
   srand((unsigned) t);
   for(i=0; i<10; i++) printf("%d\n", rand()%10);
   getch();
   return 0;
}

4、C++语言算法示例(洗牌算法):

1:首先将4*13的数组(52张牌,4种花色,每种13张)依次从1 ~52进行排列序号。

2:通过随机数,随机选取1个行与列(即随机获得一种花色和此花色的一张牌),然后比较是否等于原本的序号。如果等于,则跳过,如果不等于,.那么寻找牌的序号。在后,交换随机位置的牌与序号牌。通过序号牌来判断是否发牌结束。

C++算法:

#include
#include
#include
#include

void shuff(int [][ 13 ] );      //洗牌
void deal(int [][ 13 ] );       //发牌

//void print(int [][ 13 ]);       //测试打印

int main()
{

int deck[ 4 ][ 13 ] = { 0 }; //初始化牌

srand( time( 0 ) ); //初始化随机数种子
shuff( deck );
deal( deck );

// print( deck );

return 0;
}
//洗牌
void shuff(int wDeck[][ 13 ] )
{
int card = 1;
for(int i = 0; i < 4; i++ )
{
   for(int j = 0; j < 13; j++ )
   {
    wDeck[ i ][ j ] = card++;
   }
}
}
//发牌
void deal(int wDeck[][ 13 ] )
{
int cow,column;    //顺序牌的所在位置
int hang,lie;      //随机牌
int temp;          //交换变量
cow = column = hang = lie = temp = 0;
for(int card = 1; card <= 52; card++ )
{
   hang = rand() % 4;    //随机牌的行
   lie = rand() % 13;    //随机牌的列

   do{
    cow = rand() % 4;
    column = rand() % 13;
   }while( card != wDeck[ cow ][ column ] );   //取得顺序牌

   if(wDeck[ cow ][ column ] != wDeck[ hang ][ lie ]) //交换牌
   {
    temp = wDeck[ cow ][ column ];
    wDeck[ cow ][ column ] = wDeck[ hang ][ lie ];
    wDeck[ hang ][ lie ] = temp;
   }
  
}
}
/*
//测试打印
void print(int wDeck[][ 13 ])
{
for(int i = 0; i < 4; i++ )
{
   for(int j = 0; j < 13; j++ )
   {
    cout << setw( 4 ) << wDeck[ i ][ j ] << " ";
   }
   cout << endl;
}

}*/

5、C++语言算法示例(VC6.0实验):

#include
#include
#include
#include

//srand from [0,n),简单的使用rand()%n,得到的随机数,不够随机
int ssrand(int n)
{
    if( n<=0 || n >RAND_MAX)
{
   cout<<"Error"<   return -1;
}

return (rand()%n);
}

//srand from [0,n),随机数更为随机些
int nsrand(int n)
{
if( n<=0 || n >RAND_MAX)
{
   cout<<"Error"<   return -1;
}

const int bucket_size=RAND_MAX/n;

int r;

do r= rand()/bucket_size;
while(r >= n);


return r;
}

void main()
{
   int n=2; //29

   time_t t;
   t = time(NULL);
   srand((unsigned) t);

int i=0;
while( i< 10*n )
{
   cout<   i++;
}
   cout<


    i=0;
while( i< 10*n )
{
   cout<   i++;
}
   cout<
}