蒙特卡罗方法又称统计模拟法、随机抽样技术,是一种随机模拟方法,以几率和统计理论方法为基础的一种计算方法,是使用随机数(或更常见的伪随机数)来解决不少计算问题的方法。将所求解的问题同必定的几率模型相联系,用电子计算机实现统计模拟或抽样,以得到问题的近似解。为象征性地代表这一方法的几率统计特征,数学家冯·诺依曼用闻名世界的赌城——蒙特卡罗命名(就是那个冯·诺依曼)。
蒙特卡罗方法解题过程的主要步骤:
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);