計算機產生隨機數

tips:本文摘自July新浪博客,感謝做者整理!ios

 

C語言/C++中怎樣產生隨機數web

C語言/C++怎樣產生隨機數:這裏要用到的是rand()函數, srand()函數,C語言/C++裏沒有自帶的random(int number)函數。算法


(1)  若是你只要產生隨機數而不須要設定範圍的話,你只要用rand()就能夠了:rand()會返回一隨機數值, 範圍在0至RAND_MAX 間。RAND_MAX定義在stdlib.h, 其值爲2147483647。
例如:dom

#include<stdio.h>
#include<stdlib.h>
void main()
{
       for(int i=0;i<10;i+)
             printf("%d\n",rand());   //printf("%d\n",rand()%100);
}函數

//這裏貌似有點小問題,若用rand()%n(n表示範圍),也是能夠產生必定範圍的隨機數的。性能

 

(2)  若是你要隨機生成一個在必定範圍的數,你能夠在宏定義中定義一個random(int number)函數,而後在main()裏面直接調用random()函數:測試

例如:隨機生成10個0~100的數(主題代碼):

#define random(x) (rand()%x)       spa


for(int x=0;x<10;x++)
   printf("%d\n",random(100));    //範圍,0~100。ssr

---------------------設計

(3)可是上面兩個例子所生成的隨機數都只能是一次性的,若是你第二次運行的時候輸出結果仍和第一次同樣。這與srand()函數有關。srand()用來設置rand()產生隨機數時的隨機數種子。

在調用rand()函數產生隨機數前,必須先利用srand()設好隨機數種子(seed), 若是未設隨機數種子, rand()在調用時會自動設隨機數種子爲1。

 

上面的兩個例子就是由於沒有設置隨機數種子,每次隨機數種子都自動設成相同值1 ,進而致使rand()所產生的隨機數值都同樣。

srand()函數定義 : void srand (unsigned int seed);
一般能夠利用geypid()或time(0)的返回值來當作seed
若是你用time(0)的話,要加入頭文件#include<time.h>

例如:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define random(x) (rand()%x)         //此x表示。。

int main()
{

     srand((int)time(0));
     for(int x=0;x<10;x++)
        printf("%d\n",random(100));   //產生0~100之間的數。

     return 0;
}

這樣兩次運行的結果就會不同了!!

 

 

------------------------------------------------------------------ 

rand()一般的使用方法是這樣的:
rand()不須要參數,它會返回一個從0到最大隨機數的任意整數,最大隨機數的大小一般是固定的一個大整數。
這樣,若是你要產生0~10的10個整數,能夠表達爲:
int N = rand() % 11;
這樣,N的值就是一個0~10的隨機數,若是要產生1~10,則是這樣:
int N = 1 + rand() % 11;


總結來講,能夠表示爲:
a + rand() % n
其中的a是起始值,n是整數的範圍。

//此處,需用一個for循環控制要產生隨機數的個數。

 

--------

ok。我本身寫小段代碼測試下(主體代碼):


 for(int x=0;x<10;x++)
  printf("%d\n",rand()%100);

//不過,每次產生的隨機數,都是相同的。莫急,如下有解決辦法。
---------------------------------------------


若要0~1的小數,則能夠先取得0~10的整數,而後均除以10便可獲得隨機到十分位的10個隨機小數,若要獲得隨機到百分位的隨機小數,則須要先獲得0~100的10個整數,而後均除以100,其它狀況依此類推。

----

如 in a=rand()%10;      //0~10的整數

   return b=a/10;     //0~1的小數。

-----------


一般rand()產生的隨機數在每次運行的時候都是與上一次相同的,這是有意這樣設計的,是爲了便於程序的調試。若要產生每次不一樣的隨機數,可使用srand( seed )函數進行隨機化,隨着seed的不一樣,就可以產生不一樣的隨機數。

----------

如:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>


int main()
{
 srand((int)time(0));   //加了這個東東。
 for(int x=0;x<10;x++)
  printf("%d\n",rand()%100);     //0~100以內。
 return 0;
}

//這樣,就能每次都產生出不一樣的隨機數了。

 


如你們所說,還能夠包含time.h頭文件,而後使用srand( time(0))來使用當前時間使隨機數發生器隨機化,這樣就能夠保證每兩次運行時能夠獲得不一樣的隨機數序列(只要兩次運行的間隔超過1秒)。

 

待續中。。。

 

-------------------------------------------------------------

 

 

隨機數產生原理及應用

原創做者:EmilMatthew

摘要 : 
本文簡述了隨機數的產生原理,並用 C 語言實現了迭代取中法,乘同餘法等隨機數產生方法,同時,還給出了在符合某種機率分佈的隨機變量的產生方法。

關鍵詞 : 僞隨機數產生,機率分佈 , 正態隨機數 , 泊松隨機數 , 高斯隨機數

1 前言 :
在用計算機編制程序時,常常須要用到隨機數,尤爲在仿真等領域,更對隨機數的產生提出了較高的要求,僅僅使用 C 語言類庫中的隨機函數已難以勝任相應的工做。
用投色子計數的方法產生真正的隨機數 , 但電腦若也這樣作 , 將會佔用大量內存 ; 用噪聲發生器或放射性物質也可產生真正的隨機數 , 但不可重複 .
而用數學方法產生最適合計算機 , 這就是週期有限 , 易重複的 」 僞隨機數 
注 : 這裏生成的隨機數所處的分佈爲 0-1 區間上的均勻分佈。不是 0-1 區間怎麼辦 ? 除以 (high-low), 再加上 low 不就好了 . 咱們須要的隨機數序列應具備非退化性,週期長,相關係數小等優勢。

2.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.
實現 : 網上一大把 , 略 . 

 2.2 乘同餘法: 
乘同餘法的迭代式以下 : 

  Xn+1=Lamda*Xn(mod M)  (Lamda 即參數λ ) 
   
Rn+1=Xn/M

各參數意義及各步的做用可參 2.1 
固然,這裏的參數的選取相當重要 .
通過前人檢驗的兩組性能較好的素數取模乘同餘法迭代式的係數爲 :
1 )   lamda=5^5,M=2^35-31
2 )   lamda=7^5,M=2^31-1
實現 : 請注意,這裏必定要用到 double long, 不然計算 2^32 會溢出

2.3 混合同餘法 C++ 中的 rand() 就是這麼實現的 , 僞隨機 , 故使用前必定要加 srand() 來隨機取種 , 這樣每次實驗結果纔不會相同 .

混合同餘法是加同餘法和乘同餘法的混合形式 , 其迭代式以下 :

Xn+1=( Lamda*Xn+C )%M
Rn+1=Xn/M 

 經前人研究代表,在 M=2^q 的條件下,參數 lamda,miu,X0 按以下選取,週期較大,機率統計特性好 :
Lamda=2^b+1,b 取 q/2 附近的數 
 C=(1/2+sqrt(3))/M
X0 爲任意非負整數

它的一個致命的弱點,那就是隨機數的生成在某一週期內成線性增加的趨勢,顯然,在大多數場合,這種極富「規律」型的隨機數是不該當使用的。
 
實現 :
1 關於怎樣產生隨機數的完全研究 <wbr>[自行理解] double  _random( void ) 
 2 關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
 3 關於怎樣產生隨機數的完全研究 <wbr>[自行理解] int  a; 
 4 關於怎樣產生隨機數的完全研究 <wbr>[自行理解] double  r; 
 5 關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
 6 關於怎樣產生隨機數的完全研究 <wbr>[自行理解]a = rand() % 32767 ;            //生成整型的隨機數
 7 關於怎樣產生隨機數的完全研究 <wbr>[自行理解]r = (a + 0.00 ) / 32767.00 ;     //生成浮點型的隨機數
 8 關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
 9 關於怎樣產生隨機數的完全研究 <wbr>[自行理解] return  r; 
10 關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
11 關於怎樣產生隨機數的完全研究 <wbr>[自行理解] }

 


 

3連續型隨機變量的生成:


       3.1 反函數法 : 又叫反變換法 , 記住 , 它首先須要使用均勻分佈得到一個 (0,1) 間隨機數 , 這個隨機數至關於原機率分佈的 Y 值 , 由於咱們如今是反過來求 X. 哎 , 聽糊塗了也不要緊 , 只要知道算法怎麼執行的就行 .

    採用機率積分變換原理 , 對於隨機變量 X 的分佈函數 F(X) 能夠求其反函數,得 : 原來咱們通常面對的是機率公式 Y=f(X). 如今反過來 , 由已知的機率分佈或經過其參數信息來反求 X.

Xi=G(Ri)

其中 ,Ri 爲一個 0-1 區間內的均勻分佈的隨機變量 .

F(X) 較簡單時,求解較易,當 F(X) 較複雜時,須要用到較爲複雜的變換技巧。

可能你沒明白 , 看 3.1.1 的例子就必定會明白 .

 

3.1.1 平均分佈 :

已知隨機變量密度函數爲 :

  公式

3.1.2 指數分佈 :

指數分佈的分佈函數爲 :

x<0 時 ,F(x)=0     ; x>=0,F(x)=1-exp(-lamda*x)

利用反函數法,能夠求得 :    x=-lnR/lamda( 怎麼來的別問 )

 

3.2 正態分佈隨機變量的生成 :

正態分佈在機率統計的理論及應用中佔有重要地位,所以,能產生符合正態分佈的隨機變量就在模擬一類的工做中佔有至關重要的地位。

下面介紹兩種方法。

3.2.1

    通過必定的計算變行,符合二維的正態分佈的隨機變量的生成可按下面的方法進行:

1) 產生位於 0-1 區間上的兩個隨機數 r1 和 r2.

2) 計算 u=2*r1-1,v=2*r2-1 及 w=u^2+v^2

3) 若 w>1 ,則返回 1)

4)    x=u[(-lnw)/w]^(1/2) ( 怎麼來的別問 )

       y=v[(-lnw)/w]^(1/2)

若是爲 (miu,sigma^2) 正態分佈 , 則按上述方法產生 x 後, x’=miu+sigma*x

因爲採用基於乘同餘法生成的 0-1 上的隨機數的正態分佈隨機數始終沒法能過正態分佈整體均值的假設檢驗。而採用 C 語言的庫函數中的隨機數生成函數 rand() 來產生 0-1 上的隨機數,效果較爲理想。

 

 

3.2.2 利用中心極限定理生成符合正態分佈的隨機量:

根據獨立同分布的中心極限定理,有 :

             關於怎樣產生隨機數的完全研究 <wbr>[自行理解]

   這裏,其實只要取 n=12 (這裏,亦即生成 12 個 0-1 上的隨機數序列)就會有比較好的效果。

    經驗證,用該種方法生成生的隨機數序列一樣能比較好的符合正態分佈特性。

    因爲生成的都是標準正態分佈,因此,當須要生成 N(a,b) 的正態分佈隨機量時,根據正態分佈的線性變換特性,只要用 x=a*x0+b 便可。(其中, x0 表示生成的符合 N(0,1) 分佈的正態隨機變量。方法 3.1 亦是如此)

實現 :

關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
關於怎樣產生隨機數的完全研究 <wbr>[自行理解]1  : double  _sta( double  mu, double  sigma)  // 利用中心極限定理生成 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
 2    
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
3  int  i; 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
4  double  r,sum = 0.0 ; 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
5  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
6   if (sigma <= 0.0 )   { printf( " Sigma<=0.0 in _sta! " ); exit( 1 ); }  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
7  for (i = 1 ;i <= 12 ;i ++ ) 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
8 sum  =  sum  +  _random(); 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
9 r = (sum - 6.00 ) * sigma + mu; 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
10  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
11  return  r; 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
12  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
13 }
  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
14  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
1  double  _sta2( double  mu, double  sigma)  //  利用反函數法公式x=u[(-lnw)/w]^(1/2) 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解]
 2    
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
3  double  r1,r2; 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
4  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
5 r1 = _random(); 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
6 r2 = _random(); 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
7  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
8  return  sqrt( - 2 * log(r1)) * cos( 2 * M_PI * r2) * sigma + mu ; 
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
9  
關於怎樣產生隨機數的完全研究 <wbr>[自行理解] 
10 }
  

 

 

 

4 離散型隨機變量的生成 :

    離散型隨機變量的生成主要是但願獲得在已知 X 符合某類分佈的條件下,生成相應的 X 。


       4.1 符合泊松分佈的隨機變量的生成 :

    這裏,採用 」 上限攔截 」 法進行測試 ,我看了不懂 , 但願有人幫我分析一下 , 從數學角度幫我分析一下,謝謝!


基本的思想是這樣的:

1)在泊松分佈中,求出 X 取何值時, p(X=k) 取最大值時,設爲 Pxmax.

    其時,這樣當於求解 f(x)=lamda^k/k! 在 k 取何值時有最大值,雖然,這道題有必定的難度,但在程序中能夠能過一個循環來獲得取得 f(x) 取最大值時的整數自變量 Xmax 。

2) 經過迭代,不斷生成 0-1 區間上的隨機數,當隨機數 <Pxmax 時,則終止迭代,不然重複 (2)

3) 記錄迭代過程的次數,即爲所須要獲得的符何泊松分佈的隨機量。

顯然,這種方法較爲粗糙,在試驗的過程當中發現:生成的的隨機量只能算是近似的服從泊松分佈,因此,更爲有效的算法還有待嘗試。

實現 : 採用  double 至少能夠支持 lamda=700 ,即 exp(-700)!=0

 1  const  int MAX_VAL = 10000 ;  
 2  double U_Rand( double a, double b)      //    均勻分佈   
 3      {  
 4      double  x = random(   MAX_VAL   );  
 5      return  a + (b - a) * x / (MAX_VAL-  1 );  
 6   }   
 7    double  P_Rand(double Lamda )         //    泊松分佈   
 8       {  
 9            double x = 0 ,b = 1 ,c = exp( - Lamda ),u;  
10           do  {  
11                    = U_Rand(    0 , 1    );  
12                   b *= u;  
13                    if ( b >= c )  
14                      x    ++ ;  
15           } while (b >=  c );    
16            return  x;  
17   

爲防止lamda過大而溢出,故應該本身來寫一個浮點類,我不懂,摘抄的.

  下面是一個簡單的浮點類,還有一個用該浮點類支持的 Exp ,以及簡單的測試   

  能夠不用位域,而只用整數來表示 double ,而後用移位來進行轉換   

  這裏用位域只是爲了簡單和明瞭(看清 double 的結構)

  1   #include    < math.h >  
  2   #include    < iostream.h >  
  3   
  4    const     int    nDoubleExponentBias    =     0x3ff ; 
  5   
  6   typedef    struct  
7      
  8           unsigned    int    uSignificand2   :    32 ; 
  9           unsigned    int    uSignificand1   :    20 ; 
 10           unsigned    int    uExponent           :    11 ; 
 11           unsigned    int    uSign                   :    1 ; 
 12   } DOUBLE_BIT; 
 13   
 14   typedef   union 
 15      
 16            double                lfDouble; 
 17           DOUBLE_BIT       tDoubleBit; 
 18   } DOUBLE; 
 19   
 20   
 21    class    TFloat 
 22      
 23            int                      m_uSign; 
 24            int                      m_uExponent; 
 25           unsigned    int    m_uSignificand1; 
 26           unsigned    int    m_uSignificand2; 
 27   
 28           
 29    public : 
 30   
 31           TFloat(    void    ); 
 32           TFloat(    const     double    lfX   ); 
 33           TFloat(    const    TFloat &    cX   ); 
 34   
 35           TFloat &     operator     *=    (    const    TFloat &    cX   ); 
 36            bool     operator     <=    (    const    TFloat &    cX   );         
 37   
 38            void    GetFromDouble(    double    lfX   ); 
 39            double    ToDouble(    void    );      //    for   test  
 40    } ;

 

 

4.2符合二項分佈的隨機變量的生成:
符合二項分佈的隨機變量產生相似上限攔截法,不過效果要好許多,這是由二項分佈的特色決定的。
具體方法以下:
設二項分佈B(p,n),其中,p爲每一個單獨事件發生的機率:
關鍵算法:
i=0;reTimes=0
while(i<n)
{
  temp=myrand();//生成0-1區間上的隨機變量
  if(temp>1-p)
          reTimes++;
  i++;
}
顯然,直觀的看來,這種算法將每一個獨立的事件看成一個0-1分佈來作,生成的0-1區間上的隨機數,若小於1-p則不發生,不然認爲發生,這樣的生成方式較爲合理。實驗結果也驗證了其合理性。

 


4.3高斯分佈隨機數:貼一個最經典的程序,看程序大概就知道高斯分佈函數的反函數了.
#include <stdlib.h>
#include <math.h>
double gaussrand()
{
   static double V1, V2, S;
   static int phase = 0;
   double X;
  
   if ( phase == 0 ) {
       do {
           double U1 = (double)rand() / RAND_MAX;
           double U2 = (double)rand() / RAND_MAX;
          
           V1 = 2 * U1 - 1;
           V2 = 2 * U2 - 1;
           S = V1 * V1 + V2 * V2;
       } while(S >= 1 || S == 0);
      
       X = V1 * sqrt(-2 * log(S) / S);
   } else
       X = V2 * sqrt(-2 * log(S) / S);
      
   phase = 1 - phase;
   return X;
}

 

 

 

------------------------------------------------

關於僞隨機浮點數:

用rand() / double(RAND_MAX)能夠取得0~1之間的浮點數(注意,不一樣於整型時候的公式,是除以,不是求模),舉例:
double ran_numf=0.0;
srand((unsigned)time(0));
for(int i=0;i<10;i++){
ran_numf = rand() / (double)(RAND_MAX);
cout<<ran_numf<<" ";
}
運行結果爲:0.716636,0.457725,…等10個0~1之間的浮點數,每次結果都不一樣。

若是想取更大範圍的隨機浮點數,好比1~10,能夠將
rand() /(double)(RAND_MAX) 改成 rand() /(double)(RAND_MAX/10)
運行結果爲:7.19362,6.45775,…等10個1~10之間的浮點數,每次結果都不一樣。
至於100,1000的狀況,如此類推。

以上不是僞隨機浮點數最好的實現方法,不過能夠將就着用用…

---------------------------------

 

 

  如下是線性同餘法生成僞隨機數的僞代碼:

Random(n,m,seed,a,b){ r0 = seed; for (i = 1;i<=n;i++) ri = (a*ri-1 + b) mod m}  其中種子參數seed能夠任意選擇,經常將它設爲計算機當前的日期或者時間;m是一個較大數,能夠把它取爲2w,w是計算機的字長;a能夠是0.01w和0.99w之間的任何整數。  應用遞推公式產生均勻分佈隨機數時,式中參數n0,a,b,M的選取十分重要。  例如,選取M=10,a=b =n0=7,生成的隨機序列爲{6,9,0,7,6,9,……},週期爲4。  取M=16,a=5,b =3,n0=7,生成的隨機序列爲{6,1,8,11,10,5,12,15,14,9,0,3,2,13,4,7,6,1……},週期爲16。  取M=8,a=5,b =1,n0=1,生成的隨機序列爲{6,7,4,5,2,3,0,1,6,7……},週期爲8。

相關文章
相關標籤/搜索