迭代取中法、乘同餘法及混合同餘法產生隨機數方法

在用計算機編制程序時,經常需要用到隨機數,尤其在仿真等領域,更對隨機數的產生提出了較高的要求,僅僅使用 C 語言類庫中的隨機函數已難以勝任相應的工作。現實中,用投色子計數的方法產生真正的隨機數,但電腦若也這樣做,將會佔用大量內存;雖然用噪聲發生器或放射性物質也可產生真正的隨機數,但操作不可重複。而用數學方法產生隨機數則最適合計算機,這就是週期有限,易重複的“僞隨機數”。
(注:這裏生成的隨機數所處的分佈爲 0-1 區間上的均勻分佈。不是 0-1 區間怎麼辦? 除以 (high-low), 再加上 low 就可以完成任務。我們需要的隨機數序列應具有非退化性,週期長,相關係數小等優點。)
 
1、迭代取中法:
這裏在迭代取中法中介紹平方取中法 , 其迭代式如下 : 
                  Xn+1=(Xn^2/10^s)(mod 10^2s) 
                  Rn+1=Xn+1/10^2s

其中,Xn+1是迭代算子,而 Rn+1 則是每次需要產生的隨機數。 
第一個式子表示的是將 Xn 平方後右移 s 位,並截右端的 2s 位。 
而第二個式子則是將截尾後的數字再壓縮 2s 倍,顯然 :0=<Rn+1<=1。
迭代取中法有一個顯著的不良特性就是它比較容易退化成 0。
 
實現代碼(C語言):
#include <stdio.h>
#include <math.h>
#define S 2
 
float Xn=12345;  //Seed & Iter
float Rn;        //Return Val
 
void InitSeed(float inX0)
{
      Xn=inX0;
}
 
float MyRnd()
{
      //implementation: Xn+1=(Xn^2/10^S)(mod 10^2S)
      Xn=(int)fmod((Xn*Xn/pow(10,S)),pow(10,2*S));//here can's use %
      
      //implementation: Rn+1=Xn+1/10^2S
      Rn=Xn/pow(10,2*S);
 
      return Rn;
}
 


/*測試主程序,注意,本文只列舉一次測試主程序,以下不再重複*/
int main()
{
     int i;
     FILE * debugFile;
     if((debugFile=fopen("outputData.txt","w"))==NULL)
     {
          fprintf(stderr,"open file error!");
          return -1;
     }   
     printf("\n");
     for(i=0;i<100;i++)
     {
          tempRnd=MyRnd();
          fprintf(stdout,"%f ",tempRnd);
          fprintf(debugFile,"%f ",tempRnd);
     }                  
     getchar(); 
     return 0;
}


 

前一百個測試生成的隨機數序列(明顯可見其容易退化爲0):

0.399000 0.920100 0.658400 0.349000 0.180100 0.243600 0.934000 0.235600 0.550700 0.327000 0.692900 0.011000 0.012100 0.014600 0.021300 0.045300 0.205200 0.210700 0.439400 0.307200 0.437100 0.105600 0.115100 0.324800 0.549500 0.195000 0.802500 0.400600 0.048000 0.230400 0.308400 0.511000 0.112100 0.256600 0.584300 0.140600 0.976800 0.413800 0.123000 0.512900 0.306600 0.400300 0.024000 0.057600 0.331700 0.002400 0.000500 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000

 

2、乘同餘法:

乘同餘法的迭代式如下:

                                         Xn+1=Lamda*Xn(mod M)

                                         Rn+1=Xn/M

       當然,這裏的參數選取是有一定理論基礎的,否則所產生的隨機數的週期將較小,相關性會較大。經過前人檢驗的兩組性能較好的素數取模乘同餘法迭代式的係數爲:

                                        Lamda=5^5,M=2^35-31

                                        Lamda=7^5,M=2^31-1

實現代碼(C語言)關鍵部分:

double long  M;//請注意,這裏一定要用到double long,否則計算2^32會溢出
float MyRnd()
{
              Xn=fmod(Lamda*Xn,M);//here can's use %
              Rn=Xn/M;
              return Rn;
}
 
另外初始化段應有:
Lamda=pow(5,5);
M=pow(2,35)-31;


 

 

前三百個測試生成的隨機數序列(可見該隨機數生成方法所生成的隨機序列比較符合0-1上的均勻分佈,不過在某些數據段還有些起伏):

圖1:  乘同餘法生成的300隨機數的產生序列圖

 

 

 2: 乘同餘法生成的300隨機數的分佈情況
 
3、混合同餘法:
混合同餘法是加同餘法和乘同餘法的混合形式,其迭代式如下:
                Xn+1=(Lamda*Xn+Miu)%M
                Rn+1=Xn/M
經前人研究表明,在M=2^q的條件下,參數lamda,miu,X0按如下選取,週期較大,概率統計特性好:
                Lamda=2^c+1,c取q/2附近的數
                Miu=(1/2+sqrt(3))/M
                X0爲任意非負整數
      
實現代碼(C語言)關鍵部分:
float MyRnd()
{
    Xn=fmod(Lamda*Xn+Miu,M);
    Rn=Xn/M;
    return Rn;
}
 
另外初始化段應有:
M=pow(2,32);
Lamda=pow(2,16)+1;
Miu=(0.5+sqrt(3)/6)/M;


 
前三百個測試生成的隨機數序列(可見該種隨機數生成方法已相當接近0-1上的均勻分佈。但在圖3中可以看出它的一個致命的弱點,那就是隨機數的生成在某一週期內成線性增長的趨勢,顯然,在大多數場合,這種極富“規律”型的隨機數是不應當使用的。):
圖3: 乘同餘法生成的300隨機數的產生序列圖
圖4: 乘同餘法生成的300隨機數的分佈情況
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章