蒙特卡羅方法又稱統計模擬法、隨機抽樣技術,是一種隨機模擬方法,以機率和統計理論方法爲基礎的一種計算方法,是使用隨機數(或更常見的僞隨機數)來解決不少計算問題的方法。將所求解的問題同必定的機率模型相聯繫,用電子計算機實現統計模擬或抽樣,以得到問題的近似解。爲象徵性地代表這一方法的機率統計特徵,數學家馮·諾依曼用聞名世界的賭城——蒙特卡羅命名(就是那個馮·諾依曼)。
蒙特卡羅方法解題過程的主要步驟:
a.針對實際問題創建一個簡單且便於實現的機率統計模型,使所求的量剛好是該模型的機率分佈或數字特徵。
b.對模型的隨機變量創建抽樣方法,在計算機上進行模擬測試,抽取足夠多的隨機數。
c.對模擬實驗結果進行統計分析,給出所求解的「估計」。
d.必要時,改進模型以提升估計精度和減小實驗費用,提升模擬效率。ios
二、馮·諾依曼算法
馮·諾依曼(John von Neumann,1903~1957),20世紀最重要的數學家之一,在現代計算機、博弈論和核武器等諸多領域內有傑出建樹的最偉大的科學全才之一,被稱爲「計算機之父」和「博弈論之父」。主要貢獻是:2進制思想與程序內存思想,固然還有蒙特卡洛方法。經過第一部分,可知,蒙特卡洛方法更多的是一種思想的體現(這點遠不一樣於快排等「嚴格」類算法),下面介紹最多見的一種應用——隨機數生成。dom
三、U(0,1)隨機數的產生函數
對隨機系統進行模擬,便須要產生服從某種分佈的一系列隨機數。最經常使用、最基礎的隨機數是在(0,1)區間內均勻分佈的隨機數,最經常使用的兩類數值計算方法是:乘同餘法和混合同餘法。post
乘同餘法:其中,被稱爲種子,是模,是(0,1)區間的隨機數。測試
混合同餘法:其中,是非負整數。ui
這些隨機數是具備週期性的,模擬參數的選擇不一樣,產生的隨機數質量也有所差別。更復雜的生成方法還有:spa
四、從U(0,1)到其它機率分佈的隨機數code
離散型隨機數的模擬
設隨機變量X的機率分佈爲:,分佈函數有
設隨機變量U~U(0,1)的均勻分佈,則,代表的機率與隨機變量u落在與之間的機率相同。
例如:離散隨機變量X有分佈律
X | 0 | 1 | 2 |
P(x) | 0.3 | 0.3 | 0.4 |
U是(0,1)的均勻分佈,則有,這樣獲得的x便具備X的分佈律。
連續型隨機變量的模擬
經常使用的有兩種方法:逆變換法和舍選法。逆變換法
定理:設隨機變量Y的分佈函數爲F(y)是連續函數,而U是(0,1)上均勻分佈的隨機變量。另,則X和Y具備相同的分佈。
證實:由定義知,X的分佈函數
因此X和Y具備相同的分佈。
這樣計算得,帶入均勻分佈的U,便可獲得服從的隨機數Y。
例如:設X~U(a,b),則其分佈函數爲
則。因此生成U(0,1)的隨機數U,則即是來自U(a,b)的隨機數。
有些隨機變量的累計分佈函數不存在或者難以求出,即便存在,但計算困難,因而提出了舍選法
要產生服從的隨機數,設x的值域爲[a,b],抽樣過程以下:
1.已知隨機分佈且x的取值區間也爲[a,b],並要求,如圖:
2.從中隨機抽樣得,而後由的均勻分佈抽樣得。
3.接受或捨棄取樣值,若是捨棄該值;返回上一步,不然接受。幾何解釋以下:
常數c的選取:c應該儘量地小,由於抽樣效率與c成反比;通常取。這裏的能夠取均勻分佈,這樣由第二步中兩個均勻分佈便能獲得其餘任意分佈的模擬抽樣。
五、正態隨機數的生成
除了上面的反函數法和舍選法,正態隨機數還能夠根據中心極限定理和Box Muller(座標變換法)獲得。
中心極限定理:若是隨機變量序列 獨立同分布,而且具備有限的數學指望和方差,則對於一切有
也就是說,當n個獨立同分布的變量和,服從的正態分佈(n足夠大時)。
設n個獨立同分布的隨機變量,它們服從U(0,1)的均勻分佈,那麼漸近服從正態分佈。
Box Muller方法,設(X,Y)是一對相互獨立的服從正態分佈的隨機變量,則有機率密度函數:
令,其中,則有分佈函數:
令,則分佈函數的反函數得:。
若是服從均勻分佈U(0,1),則可由模擬生成(也爲均勻分佈,可被代替)。令爲,服從均勻分佈U(0,1)。得:
X和Y均服從正態分佈。用Box Muller方法來生成服從正態分佈的隨機數是十分快捷方便的。
下面介紹幾種簡單的隨機數的算法
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 5 int main() 6 { 7 int i,j; 8 srand((int)time(0)); 9 for (int i = 0; i < 10; i++) 10 { 11 for (int j = 0; j < 10; j++) 12 { 13 printf("%d ",rand()); 14 } 15 printf("\n"); 16 } 17 return 0; 18 }
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 5 int main() 6 { 7 int i,j; 8 srand((int)time(0)); 9 for (int i = 0; i < 10; i++) 10 { 11 for (int j = 0; j < 10; j++) 12 { 13 printf("%d ",rand()*100/32767); 14 } 15 printf("\n"); 16 } 17 return 0; 18 }
也能夠生成100——200之間的隨機數
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 5 int main() 6 { 7 int i,j; 8 srand((int)time(0)); 9 for (int i = 0; i < 10; i++) 10 { 11 for (int j = 0; j < 10; j++) 12 { 13 printf("%d ",rand()/1000+100); 14 } 15 printf("\n"); 16 } 17 return 0; 18 }
使用rand()函數獲取必定範圍內的一個隨機數
若是想要獲取在必定範圍內的數的話,直接作相應的除法取餘便可。
1 #include<iostream> 2 #include<ctime> 3 using namespace std; 4 int main() 5 { 6 srand(time(0)); 7 for(int i=0;i<10;i++) 8 { 9 //產生10之內的整數 10 cout<<rand()%10<<endl; 11 } 12 }
1 #include <stdio.h> 2 3 4 double rand0_1(double *r) 5 { 6 double base=256.0; 7 double a=17.0; 8 double b=139.0; 9 double temp1=a*(*r)+b; 10 //printf("%lf",temp1); 11 double temp2=(int)(temp1/base); //獲得餘數 12 double temp3=temp1-temp2*base; 13 //printf("%lf\n",temp2); 14 //printf("%lf\n",temp3); 15 *r=temp3; 16 double p=*r/base; 17 return p; 18 } 19 20 int main() 21 { 22 double r=5.0; 23 printf("output 10 number between 0 and 1:\n"); 24 for (int i = 0; i < 10; i++) 25 { 26 printf("%10.5lf\n",rand0_1(&r)); 27 } 28 return 0; 29 }
1 #include <stdio.h> 2 3 4 double rand0_1(double *r) 5 { 6 double base=256.0; 7 double a=17.0; 8 double b=139.0; 9 double temp1=a*(*r)+b; 10 //printf("%lf",temp1); 11 double temp2=(int)(temp1/base); //獲得餘數 12 double temp3=temp1-temp2*base; 13 //printf("%lf\n",temp2); 14 //printf("%lf\n",temp3); 15 *r=temp3; 16 double p=*r/base; 17 return p; 18 } 19 20 int main() 21 { 22 double m=1.0,n=5.0; 23 double r=5.0; 24 printf("output 10 number between 0 and 1:\n"); 25 for (int i = 0; i < 10; i++) 26 { 27 printf("%10.5lf\n",m+(n-m)*rand0_1(&r)); 28 } 29 return 0; 30 }
u爲均值, 爲方差,當n趨向於無窮大的時候,獲得隨機的隨機分佈爲正態分佈;
1 #include <stdio.h> 2 #include <math.h> 3 4 double rand0_1(double *r) 5 { 6 double base=256.0; 7 double a=17.0; 8 double b=139.0; 9 double temp1=a*(*r)+b; 10 //printf("%lf",temp1); 11 double temp2=(int)(temp1/base); //獲得餘數 12 double temp3=temp1-temp2*base; 13 //printf("%lf\n",temp2); 14 //printf("%lf\n",temp3); 15 *r=temp3; 16 double p=*r/base; 17 return p; 18 } 19 20 double random_normality(double u,double t,double *r ,double n) 21 { 22 double total=0.0; 23 double result; 24 for (int i = 0; i < n; i++) 25 { 26 total+=rand0_1(r); 27 } 28 result=u+t*(total-n/2)/sqrt(n/12);