Chapt 5: Monte Carlo method

Feng Wan

B832@仲英楼 or 3133@泓理楼 // School of Physcis @ Xian Jiaotong University

1 Monte Carlo 方法简介

1.1 什么是Monte Carlo方法?

一般来说,涉及到随机性的模拟方法都可称之为蒙特卡罗方法。通常也称为统计模拟方法。

Monte Carlo方法的名字来源于摩纳哥的一个城市蒙特卡罗,该城市以赌博业闻名。

Monte-Carlo, Monaco

Monte-Carlo, Monaco

1.2 Monte Carlo方法的历史

MC 方法的提出可追溯到19世纪末期。20世纪40年代以后,随着电子计算机的发展,该方法得到迅速的发展和应用。

特别是在原子弹的研制过程中,Fermi, von Neumann, Ulam, Metropolis 等人为了研究核物理中的问题,如中子扩散等,系统发展了蒙特卡罗方法。

四位奠基人

Ulam

Ulam

Fermi

Fermi

Von Neumann

Von Neumann

Metroplis

Metroplis

1.3 我们能用MC方法来做些什么?

  • 自然界中有的过程本身就是随机的过程(随机性问题)。物理现象中如小颗粒的布朗运动、粒子的衰变、粒子在介质中的输运过程等,城市里的交通,传染病的传播。
  • 蒙特卡罗方法也可借助概率模型来解决不直接具有随机性的确定性问题。

1.4 应用的领域

除了在物理学及相关自然科学中的应用外,蒙特卡罗方法广泛的应用于社会学、金融等诸多领域。一个典型的例子是数量金融学。

随机性的例子 — 交通流模型

1.4.1 不具随机性的一个例子:Buffon投针实验(1777年)

该试验方案是:在平滑桌面上划一组相距为 \(s\) 的平行线,向此桌面随意地投掷长度也为 \(s\) 的细针,那么从 针与平行线相交的概率就可以得到 \(p\) 的数值。

  1. 针与竖直方向的线的方向夹角为\(\alpha\),则相交概率为: \[ P = \frac{s|\cos \alpha|}{s} = |\cos \alpha| \]
  2. 各向同性随机投针,则夹角\(\alpha\)\([0, \phi]\)均匀分布,所以 \[ E(|\cos \alpha|) = \int_0^\pi |\cos \alpha|\cdot f(\alpha) d\alpha = \frac{2}{\pi} \]
  3. 设投针\(N\)次,相交次数为\(M\),则相交概率的期望值: \[ E(|\cos \alpha|) = \frac{2}{\pi} \equiv \frac{M}{N} \rightarrow \pi = \frac{2N}{M} \]

大数定理

\[ N \rightarrow \infty \]

一些实验结果

实验者 年份 投计次数 \(\pi\) 的实验值
沃尔弗(Wolf) 1850 5000 3.1596
斯密思(Smith) 1855 3204 3.1553
福克斯(Fox) 1894 1120 3.1419
拉查里尼(Lazzarini) 1901 3408 3.1415929

1.4.2 不具随机性的另一个例子 — 定积分

构造一个将积分区域包围起来的矩形 \([a, b] \times [0, y_0]\)。然后 随机的在矩形内撒 \(N\) 个点,假设其中有 \(M\) 个点落在积分线的下方,则积分为 \[ \int_a^b f(x)dx = \frac{M}{N}[y_0 (b - a)] \]

Code
fx = lambda x: np.sin(2.0 * x)
x = np.linspace(0, 1, 100)
y = fx(x)
fig = plt.figure(figsize = (6, 3))
plt.plot(x, y)

N = 500000
xx = np.random.random(N)
yy = np.random.random(N)
yy_f = fx(xx)
indexes = np.where(yy > yy_f)
indexes2 = np.where(yy <= yy_f)
plt.plot(xx[indexes], yy[indexes], 'ro', markersize = 1)
plt.plot(xx[indexes2], yy[indexes2], 'bo', markersize = 1)
plt.show()

Code
var('x')
var('y', cls = Function)
eqn = Eq(y(x), sin(2*x))
a = integrate(eqn.rhs, (x, 0, 1))
Markdown(f'$${latex(eqn)}$$')

\[y{\left(x \right)} = \sin{\left(2 x \right)}\]

Code
Markdown(f'$${latex(a)}$$')

\[\frac{1}{2} - \frac{\cos{\left(2 \right)}}{2}\]

Code
print("N = %d, monte carlo result: %6.5f, \naccurate result: %6.5f." %(N, len(indexes2[0]) / N, a.evalf()))
N = 500000, monte carlo result: 0.70794, 
accurate result: 0.70807.

1.4.3 定积分的另外一种算法

类似于定积分求解中的矩形法思想

在区间 \([a, b]\) 内随机的撒 \(N\) 个点, 则 \(f(x)\) 在该区间内的积分可写为 \[ \int_a^b f(x)dx = \frac{(b - a)}{N}\sum_{i = 1}^N f(x_i) \qquad(1)\]

这就是用蒙特卡罗方法计算定积分的基本思想!

2 随机数的分类

2.1 真随机数

  • 生成方法:随机的物理过程,例如:
    • 掷骰子
    • 计算机芯片中电子的热涨落( 固有噪声)
    • 辐射衰变
  • 缺点:速度慢、不可复制、不可移植、昂贵
  • 用途: 例如信息安全领域(密码)

显然,真随机数满足不了计算物理的模拟的需要。我们要设计算法,用计算机快速生成大量的随机数。

2.2 伪随机数

伪随机数是用确定性的算法生成的随机数。它能够利用计算机快速的大量生成。

问题:它不可能真的随机。对于不知道该伪随机数产生算法的人来说,序列中的数具有“不可预测性”。虽然复杂的技术能够更好的隐藏随机数之间的关联,但是经验表明,只要你用的次数足够多,总能发现其中的关联。

计算物理学对随机数的要求:

  • 随机性好
  • 可重复性
  • 可移植性
  • 效率高
  • 独立性、均匀性是随机数必备的两个特点

2.3 产生伪随机数的常用的算法

  • 乘同余法

由Lehmer在1951年提出来,它的一般形式是:对于任一初始值\(x_1\),伪随机数序列由下面递推公式确定: \[ x_{i+1} = a \cdot x_i (\text{Mod}~M); ~~~~~ \xi_{i+1} = \frac{x_{i+1}}{M}, ~~ i = 1,2,... \] 其中\(a\)为常数,\(x_1\)为种子。

为了便于在计算机上使用,通常取 :\(M=2^S\),其中\(S\)为计算机中二进制数的最大可能有效位数, \(x_1 =\) 奇数;\(a = 5^{2k+1}\);其中\(k\)为使\(5^{2k+1}\)在计算机上所能容纳的最大整数,即\(a\)为计算机上所能容纳的\(5\)的最大奇次幂。一般地,\(s=32\)时,\(a=5^{13}\)\(s=48\)时,\(a=5^{15}\)等。伪随机数序列的最大容量\(2^{s-2}\)。乘同余方法是使用的最多、最广的方法,在计算机上被广泛地使用。

  • “线性同余”法(乘加同余法)——最常用的算法之一

产生伪随机数的乘加同余法是由Rotenberg于1960年提出来的,由于这个方法有很多优点,已成为除乘同余方法产生伪随机数之外的另一主要方法。 一般公式为 \[r_{i+1} = (ar_i + c) \text{Mod}~M\],\(r_1\)称为种子,\(i\) 是序列标号,\(a ,c\)\(M\) 是诸幻数。这几个幻数常常很大,其精确值依赖于所用计算机的字长。

乘同余法与线性同余方法的思想: 两个数做乘法,高位数字容易预测,低位很难。很难预测,就说明低位数字很随机。

  • “线性同余”法的最大容量

关于“线性同余”法的最大容量问题,有如下结论:如果

  1. 对于正整数\(M\)的所有素数因子\(P\),下式均成立:\(a ({\rm mod})P= 1\)
  2. \(M\)\(4\)的倍数时,还有下式成立:\(a ({\rm mod}) 4= 1\)
  3. \(c\)\(M\)互素,则乘加同余方法所产生的伪随机数序列的最大容量达到最大可能值\(M\)
  • \(M, x_1, a, c\)的取值

为了便于在计算机上使用,通常取 \(M = 2^s\) 其中\(s\)为计算机中二进制数的最大可能有效位数。 \[a = 2^b + 1 ~~~ (b \geq 2); ~~ c = 1\]
这样在计算中可以使用移位和指令加法,提高计算速度。

Example 1 利用“线性同余”法获得 \([0, 1]\) 之间的随机数,其中\(c = 1; a = 5; M = 16 = 2^4; r_1 = 3\) \[ r_{i+1} = (ar_i + c)\text{mod} M \Longrightarrow r_{i+1} = (5r_i + 1)\text{mod} 16 \] 得到如下一列数 \[ \begin{aligned}[l] &r_1 = 3 \\ &r_2 = (5\times 3 + 1)\text{mod}16 = 0 \\ &r_3 = 1 \\ &r_4 = 6; \\ &r_5, ... r_{18} = 15, 12, 13, 2, 11, 8, 9, 14, 7, 4, 5, 10, 3, 0 \end{aligned} \] 将其转化为 \([0, 1]\) 之间的随机数(除以 \(16\)

Solution 1.

Code
def linmod(seed, a, c, M, n):
    result = np.zeros(n)
    result[0] = seed
    for i in range(n-1):
        result[i+1] = np.mod(a * result[i] + c, M)
    return result

result = linmod(3, 5, 1, 16, 18)
mystr = ""
for i in range(18):
    mystr = mystr + "r(" + str(i+1) + ") = " + str(result[i]) + ",\t"
    if (np.mod(i+1, 4) == 0):
        mystr = mystr + "\n"
print(mystr)

mystr = "\n"
for i in range(18):
    mystr = mystr + "r(" + str(i+1) + ") = " + str(result[i] / 16) + ",\t"
    if (np.mod(i+1, 4) == 0):
        mystr = mystr + "\n"
print(mystr)
r(1) = 3.0, r(2) = 0.0, r(3) = 1.0, r(4) = 6.0, 
r(5) = 15.0,    r(6) = 12.0,    r(7) = 13.0,    r(8) = 2.0, 
r(9) = 11.0,    r(10) = 8.0,    r(11) = 9.0,    r(12) = 14.0,   
r(13) = 7.0,    r(14) = 4.0,    r(15) = 5.0,    r(16) = 10.0,   
r(17) = 3.0,    r(18) = 0.0,    

r(1) = 0.1875,  r(2) = 0.0, r(3) = 0.0625,  r(4) = 0.375,   
r(5) = 0.9375,  r(6) = 0.75,    r(7) = 0.8125,  r(8) = 0.125,   
r(9) = 0.6875,  r(10) = 0.5,    r(11) = 0.5625, r(12) = 0.875,  
r(13) = 0.4375, r(14) = 0.25,   r(15) = 0.3125, r(16) = 0.625,  
r(17) = 0.1875, r(18) = 0.0,    

此算例可以满足“线性同余”法的最大容量M

随机数序列可能出现周期性的循环现象。对于我们上述“线性同余”算法,只要有一个随机数重复,其后面的随机数全部重复。

%filename: chapter5_example_1_random_number.m
%利用线性同余法生成[0,1]上的随机数;
clear all
close all
clc
%format short
a=5;c=1;r1=3;m=2^14
N=1000;%随机数个数
r=zeros(1,N);
for i=1:N
    r(i)=mod(a*r1+c,m);
    r1=r(i);
end
%figure; plot(1:N,r)
figure; plot(1:N,r/m,'-*')











Code
result1 = linmod(3, 5, 1, 16, 100)
result2 = linmod(3, 5, 1, 2**14, 100)
plt.figure(figsize = (12, 4))
plt.subplot(121)
plt.plot(result1 / 2**4, '-*')
plt.title('M = ' + str(16))
plt.subplot(122)
plt.plot(result2 / 2**14, '-*')
plt.title('M = ' + str(2**14))
plt.show()

如果需要得到 \([A, B]\) 之间的随机数,可作如下变换 \[ x_i = A + (B-A)r_i, ~~ 0 < r_i < 1 ~~ \Longrightarrow ~~ A < r_i < B \] 随机数序列在 \([0, M -1]\) 之间,所以周期最多为 \(M\) 。 为了获得具有更大的周期的随机数列,尽可能取大的 \(M\)。考虑到计算机的位数限制,\(32\) 位机器整数的取值范围为 \([-2^{31}, 2^{31}-1]\) , \(M\) 通常取为 \[ M \approx 2^{31} \approx 2\times 10^9 \] 用一个随机数列来“扰乱”另一个随机数列会使得随机数列的周期变长,随机性也加强。对于上述问题,如果需要的随机数超过这个长度,则中途需要重设种子或者利用另一个随机数列来“扰乱”

2.4 Matlab中的随机函数

rand 函数——生成 [0, 1] 之间的随机实数;该指令采用梅森绞纽发生器,默认以\(0\)为初始种子,产生周期长度为\(2^{19936}-1\)(0,1)区间均匀分布的伪随机数。

  1. 0个参数:rand,表示生成一个随机数。
  2. 1个参数:rand(3),表示生成一个 \(3 \times 3\) 的随机数方阵。
  3. 2个参数:rand(2, 3),表示生成一个\(2 \times 3\)的随机数矩阵
  4. rand(method, s), 自行重置随机数发生器的状态,参数 method 表明生成随机数的算法,可取‘twister’, ‘state’, ‘seed‘ 三个值,其中’twister’方法产生的伪随机序列重复周期最长, 也是MATLAB 7.4及更高版本的默认方法. 随机种子s一般取正整数。

plot(1:100,rand(1,100),'*-')

Code
plt.plot(np.arange(100), np.random.random(100), '-*')
plt.show()

randn 函数生成正态分布随机实数,

randi函数生成随机整数,如randi([a,b],2,3)表示产生一个在[a,b]区间上的\(2 \times 3\)的随机数组。

Example 2 利用计算机模拟计算,求连续掷两颗骰子,点数之和大于\(6\)且第一次掷出的点数大于第二次掷出点数的概率。

Solution 2.

要两次之和大于\(6\)且第一次的点数比第二次的大,第一次点数一定要是\(4, 5, 6\)中的一个。若为\(4\),则第二次可为\(3\);若为\(5\),则第二次可为\(2, 3, 4\);若为\(6\),则第二次可为\(1, 2, 3, 4, 5\)。因此题目要求概率为 \[ \frac{1}{6} \times \frac{1}{6} + \frac{1}{6} \times \frac{3}{6} + \frac{1}{6} \times \frac{5}{6} = \frac{1}{4} \]

%filename: chapter5_example_2.m
clear all
close all
clc
nn=0;
N=500000;
for i=1:N
    a=randi([1,6]);
    b=randi([1,6]);
    if a>b & (a+b)>6
        nn=nn+1;
    end
end
format long
nn/N

















Example 3 计算圆周率\(\pi\) 单位圆的面积是\(\pi\),它在第一象限的面积为\(\pi / 4\),因此有 \[ \pi = 4\int_0^1 dx_1 \int_0^1 dx_2 \theta(1-x_1^2-x_2^2) \] with \[ \theta(x) = \left\lbrace \begin{aligned}[l] &1, ~~ (x > 0) \\ &0, ~~ (x < 0) \end{aligned} \right. \]

计算时,在第一象限内的正方形\(0 \leq x \leq 1, 0 \leq y \leq 1\)中生成二维的等概率分布的随机数(\(x,y\)),统计所有满足\(x^2 + y^2 < 1\) 的点数,计算它们与总点数之比,就可得到结果。(撒点法求定积分)

Solution 3.

%filename: chapter5_pi_MC.m
%采用蒙特卡洛积分求pi
clear all
close all
clc
N=10^8;
x=rand(2,N);
%第一种写法
p=4*length(find(sum(x.^2)<1))/N
%第二种写法
nn=0;
for i=1:N
    if x(1,i)^2+x(2,i)^2<1
        nn=nn+1;
    end
end
pp=4*nn/N
















2.5 随机数的测试

下面我们对如下参数生成的\([0,1]\)上的随机数列进行测试 \[ (a, c, M, r_0) = (57, 1, 256, 10) \] 没有完美的测试。这里只对均匀程度(均匀性检验)和短程关联程度(独立性检验)做直观的判断。

  • 均匀性检验-频率检验—均匀性是指在\([0,1]\)区间内等长度子区间中随机数的数量是一样的。将区间\([0,1]\)分为K个子区间,统计随机数落在第\(k\)个子区间的实际频数\(n_k\),它应当趋近于理论频数\(m_k= N/K (k = 1,… K )\),注意此处的\(n_k\)是整数而\(m_k\)可以是小数。

  • 独立性检验—独立性是指按先后顺序出现的随机数中,每个随机数的取值与其相距一定间隔的随机数取值之间无关。用连续的两个随机数作为点(\(x, y\))的坐标作图可以直观看出随机数之间的关联性。

%filename: chapter5_example_2_random_number.m
%利用线性同余法生成[0,1]上的随机数
clear all
close all
clc

a=57;c=1;r1=10;m=2^8; %test m=2^18
N=10000;%随机数个数,10000
r=zeros(1,N);
for i=1:N
    r(i)=mod(a*r1+c,m);
    r1=r(i);
end
figure; plot(1:N,r/m,'*')%视觉验证均匀性
figure; plot(r(1:2:N-1)/m,r(2:2:N)/m,'+')%视觉验证独立性
title('验证随机数的独立性')

%验证产生随机数的分布
Num=50;                %区间大小
y=zeros(1,Num);
x=linspace(0,1,Num+1);   %0,1间分为51个点,50段
h=x(2)-x(1);
for i=1:N
    index=fix(r(i)/m/h)+1; %注意fix为舍去小数的取整运算
    y(index)=y(index)+1;
end
y=y/N;
figure;plot(x(1:end-1),y,'*-',0:0.001:1,1/Num,'r');title('验证随机数的均匀分布');
figure;bar(x(1:end-1),y)




















Code
nn = 10000
M = 2**8
a = 57
c = 1
r1 = 10

def checkrand(r1, a, c, M, nn):
    result1 = linmod(r1, a, c, M, nn)
    result1 = result1 / M
    plt.figure(figsize=(12, 3))
    plt.subplot(131)
    plt.plot(result1, '*', markersize = 1)
    plt.xlabel(r'$i$')
    plt.ylabel(r'$r_i$')
    plt.subplot(132)
    plt.plot(result1[0::2], result1[1::2], '*', markersize = 1)
    plt.xlabel(r'$r_i$')
    plt.ylabel(r'$r_{i+1}$')
    plt.subplot(133)
    c, xx = np.histogram(result1, np.linspace(0, 1, 50))
    plt.plot(xx[1:] - 0.5 * xx[0], c, '-*')
    plt.xlabel(r'$r_i$')
    plt.ylabel(r'$N(r_i)$')
    plt.suptitle('M = '+ str(M))
    plt.show()
checkrand(r1, a, c, M, nn)

“线性同余”法生成随机数的均匀性与独立性

Code
M = 2**14
checkrand(r1, a, c, M, nn)

2.6 问题

不管你做多少测试,也不能证明这个随机数在任何情况下都表现很好。实际的计算问题的复杂程度远远大于随机性测试。

务实的做法是选择几个常用的,久经考验的随机数,对相同的问题,用不同的随机数做测试,比较结果。

下面我们对Matlab中利用rand指令生成的随机数列进行测试

%filename: chapter5_example_2_random_number_matlab.m
%验证rand生成的随机数列的均匀性与独立性
clear all
close all
clc

N=20000;
r=rand(1,N);
figure; plot(1:N,r,'*')%视觉验证均匀性
figure; plot(r(1:2:N-1),r(2:2:N),'+')%视觉验证独立性
title('验证随机数的独立性')

%验证产生随机数的分布
Num=50;                %区间大小
y=zeros(1,Num);
x=linspace(0,1,Num+1);   %0,1间分为51个点,50段
h=x(2)-x(1);
for i=1:N
    index=fix(r(i)/h)+1;
    y(index)=y(index)+1;
end
y=y/N;
figure;plot(x(1:end-1),y,'*-',0:0.001:1,1/Num,'r');title('验证随机数的均匀分布');
figure;bar(x(1:end-1),y)


















Code
def checkrandpy():
    result1 = np.random.random(10000)
    plt.figure(figsize=(12, 3))
    plt.subplot(131)
    plt.plot(result1, '*', markersize = 1)
    plt.xlabel(r'$i$')
    plt.ylabel(r'$r_i$')
    plt.subplot(132)
    plt.plot(result1[0::2], result1[1::2], '*', markersize = 1)
    plt.xlabel(r'$r_i$')
    plt.ylabel(r'$r_{i+1}$')
    plt.subplot(133)
    c, xx = np.histogram(result1, np.linspace(0, 1, 50))
    plt.plot(xx[1:] - 0.5 * xx[0], c, '-*')
    plt.xlabel(r'$r_i$')
    plt.ylabel(r'$N(r_i)$')
    plt.suptitle('random from python')
    plt.show()
checkrandpy()

3 Monte Carlo 积分

物理学常对一些自由度很大的系统感兴趣。而对这种系统的描述常常又包括(或者可以归结为)很高维的积分求值。

  1. 一团凝聚物质中的许多原子;
  2. 一个原子中的许多电子;
  3. 一个量子场在一个时空区域的一切点上的无穷多个值。

例如,由 \(N\) 个原子组成的气体,这些原子在温度 \(1/\beta\) 下通过一个偶对位势 \(v\) 相互作用,其经典配分函数正比于 \(3N\) 维积分 \[ Z = \int d^3 r_1 ...d^3 r_N e^{-\beta \sum_{i<j}v(r_{ij})}\]

只有 \(N\) 非常小时,我们才可用第一章中讲过的求积公式直接计算这样的积分,否则是完全不可能的。

我们采用很粗的离散化,设每一维坐标只取\(10\)个不同的值,于是被积函数必须在 \(10^{3N}\) 个点上求值。

对于一个并不大的\(N\)值,如\(N=20\),和一台每秒能求大约\(10^{10}\)个值的非常快速的计算机。

需要大约\(10^{50}\)秒的时间—-比宇宙的年龄的\(10^{32}\)倍还多!

当然,通过利用被积函数的置换对称性的技巧, 可以把这一估计值减少很多,但是应该明白,直接求积仍然是毫无希望的.

基本思想

不在大量的求积点的每一点上计算被积函数,而只在坐标点的一组代表性的随机样点上计算。具体地讲:根据待求问题的变化规律,人为地构造出一个合适的概率模型,依照该模型进行大量的统计试验,使它的某些统计参量,正好是待求问题的解。

3.1 蒙特卡洛积分在一维问题中的应用

3.1.1 一维积分的蒙特卡洛算法

对于积分 \(\int_a^b g(y)dy\) 作变量替换 \(y = a+(b-a)x\) \[ \Longrightarrow x\in[0, 1]; ~~ (b-a)\int_0^1g(a+(b-a)x)dx \equiv (b-a)\int_0^1f(x)dx \]

因此,所有一维定积分的计算都可以转化成 \[ I = \int_0^1f(x)dx \]

  • \(I = M/N \cdot\) 矩形面积
  • \(N\): \(I \approx \frac{1}{N}\sum_{i=1}^Nf(x_i)\)

这是用蒙特卡罗方法计算定积分的基本策略,又称直接抽样法

总结:当问题可以抽象为某个确定的数学问题时,应当首先根据待求问题的变化规律,建立一个恰当的概率模型,即确定某个随机事件 \(A\) 或随机变量 \(X\) (如例子中的投点事件或随机变量 \(f\)),使得待求的解等于随机事件出现的概率或随机变量的数学期望值。然后进行模拟试验,即重复多次地模拟随机事件 \(A\) 或随机变量 \(X\)。最后对随机试验结果进行统计平均,求出 \(A\) 出现的频率或 \(X\) 的平均值作为问题的近似解。—–间接模拟法

3.1.2 Monte Carlo 积分的误差分析

前面讲的算法(如梯形法,Simpson算法等)都是确定性算法,分析误差主要是用解析的方法。对蒙特卡罗方法,前面的误差分析方法就不适用了,需要用概率论。

大数法则中心极限定理 —— Monte Carlo 方法的基础

Theorem 1 (大数法则) 设函数 \(f\) 在区间 \([a,b]\) 上可积,我们在区间 \([a,b]\) 上以均匀的概率密度随机地取 \(N\) 个数 \(x_i\) ,对每个 \(x_i\) 计算出函数值 \(f(x_i)\) ,大数法则告诉我们,这些函数值之和除以 \(N\) 的值将收敛于函数 \(f\) 的期望值,也就是说下式是一个计算定积分的合理方法 \[ \frac{1}{N}\lim_{N\rightarrow\infty}\sum_{i=1}^N f(x_i) = \frac{1}{b-a}\int_a^bf(x)dx = \int_0^1f(x')dx' \] 收敛性正是由大数法则来保证的.

Theorem 2 (中心极限定理) 无论单个随机变量的分布如何,许多独立随机变量之和总是满足正则分布(Gauss分布)。该分布可由给定期望值 \(\mu\) 和方差 \(\sigma\) 完全确定下来。

收敛速度可从方差给出: \[ \sigma_I^2 \approx \frac{1}{N}\sigma_f^2 = \frac{1}{N}\left[ \frac{1}{N}\sum_{i=1}^Nf_i^2 - \left(\frac{1}{N}\sum_{i=1}^Nf_i \right)^2 \right] \] \(\sigma_f^2\)\(f\) 的方差; 即 \(f\) 在积分区间上偏离其平均值的程度的量度。 此式揭示了Monte Carlo 方法求积方法的 两个 重要的方面:

  1. 积分估值中的不确定度(标准偏差) \(\sigma_I\)\(N^{-1/2}\) 减小。因此,用的点越多,就会得到更加精确的答案,但是其收敛非常慢。
  2. \(\sigma_f\) 越小(也就是说,若 \(f\) 尽可能平滑),精度越高。

3.1.3 考虑如下三种情况:

  1. \(f\) 很平坦时,只需不多的点就能保证精度。
  2. 一个极端情况是: \(f\) 一个常数,这时我们只需一点上的值就可定出它的平均值。
  3. 另一极端情况是: \(f\) 除了在 \(x\) 的定义域内有一个很窄的峰之外其余都是零(如 \(\delta\) 函数)。若 \(x_i\) 以等概率位于 \(a\)\(b\) 之间的任何地方,那么除了少数几个之外它们绝大多数都将位于 \(f\) 的尖锋之外,而只有这少数的几个的 \(f_i\) 才不是零。这将导致对积分的一个很差的估值。

Example 4 用Monte Carlo 方法计算积分 \(I = \int_0^1\frac{dx}{1+x^2} = \frac{\pi}{4} = 0.78540\)

Solution 4.

%filename: chapter5_example_3_integration.m
clear all
close all
clc

N=[10,20,50,100,200,500,1000,2000,5000,10000,20000,50000];
for i=1:length(N)
    x=rand(1,N(i)); %注意随机数要在计算函数值和误差,方差前给定
    y(i)=sum(1./(1+x.^2))/N(i);
    error(i)=abs(y(i)-pi/4);
    sigma(i)=sqrt(1/N(i)*sum((1./(1+x.^2)).^2)-(1/N(i)*sum(1./(1+x.^2)))^2)/sqrt(N(i));
end

plot(1:length(N),error,'*-',1:length(N),sigma,'+-')
legend('实际偏差','标准偏差')


















Code
N=np.array([10,20,50,100,200,500,1000,2000,5000,10000,20000,50000])
y = np.zeros(len(N))
err = y.copy()
sigma = y.copy()
def f(x):
    return 1.0 / (1.0 + x**2.0)
for i in range(len(N)):
    x = np.random.random(N[i])
    fi = f(x)
    favg = np.mean(fi)
    y[i] = favg
    err[i]=np.abs(y[i] - 0.25 * np.pi)
    sigma[i] = np.sqrt(np.mean(fi**2.0) - favg**2.0) / np.sqrt(N[i])
plt.plot(N, err, 'r-x', label = 'error')
plt.plot(N, sigma, 'b-v', label = r'$sigma$')
plt.xscale('log')
plt.xlabel(r'$N_i$')
plt.title(r'error $\propto \sigma$ ')
plt.legend()
plt.show()

3.2 有待解决的问题

函数标准偏差很大时,积分误差也相应的很大(蒙特卡洛积分的实际误差与标准偏差呈正相关)。

物理中重要的积分,被积函数标准偏差都很大,例如统计力学中的配分函数。

波尔兹曼因子在相空间的绝大多数区域都近似为 0。 \[ Z = \int d^3 r_1 ... d^3r_N e^{\beta \sum_{i<j}v(r_{ij})} \]

3.3 解决办法 🤔

Monte Carlo求积中的误差正比于被积函数的方差。我们不难设计出一种普遍的方案以减小方差,从而改进Monte Carlo方法的效率。

4 重要抽样法(Importance Sampling)

我们设想有一经过归一化的正的权函数 \(w(x)\): \(\int_0^1 w(x)dx = 1\),

把被积函数乘以和除以权函数 \(w(x)\),则积分可以写成 \[ I = \int_0^1 \underbrace{dxw(x)}_{dy} \frac{f(x)}{w(x)} \]

\[ \frac{dy}{dx} = w(x); ~~ y(x) = \int_0^x w(x')dx'; ~~ y(x=0) = 1; y(x = 1) = 1 \]

作变量替换 \(x = x(y) \leftrightarrow y(x) = \int_0^x w(x')dx'\) \[ I = \int_0^1 dy \frac{f(x(y))}{w(x(y))} \Longrightarrow I \approx \frac{1}{N}\sum_{i = 1}^N \frac{f(x(y_i))}{w(x(y_i))} \]

\(y\) 坐标的区间 \([0, 1]\) 上均匀分布的一组随机样点对 \(f / w\) 的值求平均。

如果挑选一个 \(w\) 使它的行为和 \(f\) 近似相同,则就可以使积分中的被积函数 \(f / w\) 很平滑,从而减小Monte Carlo估值的方差。

4.0.1 如何理解变量替换的潜在好处?— 重要性抽样

可以证明,点在 \(y\) 坐标的区间 \([0, 1]\) 内均匀分布隐含着点在 \(x\) 坐标分布的概率密度是 \(dy/dx=w(x)\)

这又意味着点集中在最“重要”的 \(x\) 值附近,此处 \(w\) 大(因而 \(f\) 也有希望大,对积分的贡献也大)。

对于那些 \(w\)\(f\) 值小的“不重要的” \(x\) 值附近,只有很少的样点。

在对积分贡献大的区域多撒点,其余地方少撒点。

问题的关键在于:能不能找到一个合适的 \(w\),以及能不能从变量替换中反推出反函数 \(x(y)\)

对前例 \(\int_0^1 \frac{dx}{1+x^2} = \frac{\pi}{4} \approx 0.78540\)

选择归一化的权函数 \(w(x) = \frac{1}{3}(4-2x)\)

其在积分区间上是单调下降的( \(f\) 也是)。由于在 \(x=0\)\(x=1\) 两个端点上都有 \(f / w = 3 / 4\), \(w\)\(f\) 的行为近似得很好。这样,新的积分变量为

\[ \begin{aligned} &y(x) = \int_0^x w(x')dx' = \frac{1}{3}x(4-x) \\ &\xrightarrow{\text{inverse function}} x = 2-(4-3y)^{1/2} \end{aligned} \]

Code
x = np.linspace(0, 1, 100)
y1 = f(x)
def wfunc(x): 
    return 1.0 / 3.0 * (4.0 - 2.0 * x)
w = wfunc(x)

plt.plot(x, y1, 'r-', label = r'$f(x)$')
plt.plot(x, w, 'b-', label = r'$w(x)$')
plt.legend()
plt.show()

\[ w(x) = \frac{6}{5}\left( 1 - \frac{1}{2}x^2\right) \rightarrow ~~~ y\sim x^3 \]

%filename: chapter5_example_4_integration.m
%采用重要抽样后的蒙特卡洛积分
clear all
close all
clc
format long

N=[10,20,50,100,200,500,1000,2000,5000,10000,20000,50000];
%重要抽样法
for i=1:length(N)
    y=rand(1,N(i)); %注意随机数要在计算函数值和误差,方差前给定
    x=2-(4-3*y).^0.5; %原有自变量x,x的取值范围比y更偏向接近于0的区域
    w=(4-2*x)./3;     %权函数w
    f=1./(1+x.^2);    
    ff=f./w;          %含有权函数的被积函数
    yy(i)=sum(ff)/N(i); %求积分
    error(i)=abs(yy(i)-pi/4);
    sigma(i)=sqrt((1/N(i)*sum(ff.^2)-(1/N(i)*sum(ff))^2)/N(i));
end
%均匀抽样法
for i=1:length(N)
    x_1=rand(1,N(i)); %注意随机数要在计算函数值和误差,方差前给定
    y_1(i)=sum(1./(1+x_1.^2))/N(i);
    error_1(i)=abs(y_1(i)-pi/4);
    sigma_1(i)=sqrt(1/N(i)*sum((1./(1+x_1.^2)).^2)-...
    (1/N(i)*sum(1./(1+x_1.^2)))^2)/sqrt(N(i));
end
%原有被积函数
xx=0:0.001:1;
f=1./(1+xx.^2);    %原有被积函数
w=(4-2*xx)./3;     %权函数w
ff=f./w;          %含有权函数的被积函数

figure;plot(1:length(N),error,'*-',1:length(N),sigma,'+-')
legend('重要抽样法实际偏差','重要抽样法标准偏差')
figure;plot(1:length(N),error_1,'*-',1:length(N),sigma_1,'+-')
legend('均匀抽样法实际偏差','均匀抽样法标准偏差')
figure;plot(xx,f,'r',xx,ff,'b')
legend('原有被积函数','含有权函数的被积函数')
% figure;plot(1:N(end),y,'*')
% figure;plot(1:N(end),x,'*')%原有自变量x,x的取值范围比y更偏向接近于0的区域,点数少的时候更加明显





















Code
y = np.random.random(1000)
xx = 2.0 - (4.0 - 3.0 * y)**0.5
result = np.mean(f(xx) / wfunc(xx))
print(result, result - np.pi * 0.25)

plt.plot(x, f(x), label = 'f(x)')
plt.plot(x, f(x) / wfunc(x), label = 'f/w')
plt.legend()
plt.show()
0.7848693932259098 -0.0005287701715385174

Example 5 极端例子: \[ \int_0^1 8 x dx = 4 \] let \(w = 2x \rightarrow y(x) = \int_0^x w(x')dx' = x^2 \rightarrow x = \sqrt{y}\) \[ \int_0^1 8xdx = \int_0^1 8\sqrt{y} \times \frac{1}{2}\frac{1}{\sqrt{y}}dy = \int_0^1 4 dy \]

Solution 5.

%filename: chapter5_example_4_integration_1.m
%采用重要抽样后的蒙特卡洛积分
clear all
close all
clc
N=[10,20,50,100,200,500,1000,2000,5000]%,10000,20000,50000];
for i=1:length(N)
    y=rand(1,N(i)); %注意随机数要在计算函数值和误差,方差前给定
    x=(y).^0.5; %原有自变量x,x的取值范围比y更偏向接近于0的区域
    w=2*x;     %权函数w
    f=8*x;    
    ff=f./w;          %含有权函数的被积函数
    yy(i)=sum(ff)/N(i); %求积分
    error(i)=abs(yy(i)-4);
    sigma(i)=sqrt((1/N(i)*sum(ff.^2)-(1/N(i)*sum(ff))^2)/N(i));
end

figure;plot(1:N(end),y,'*')
figure;plot(1:N(end),x,'*')%原有自变量x,x的取值范围比y更偏向接近于0的区域,点数少的时候更加明显
figure;plot(1:length(N),error,'*-',1:length(N),sigma,'+-')
legend('重要抽样法实际偏差','重要抽样法标准偏差')



















%filename: chapter5_example_4_integration_2.m
%采用均匀抽样的蒙特卡洛积分
clear all
close all
clc

N=[10,20,50,100,200,500,1000,2000,5000,10000,20000,50000];
for i=1:length(N)
    x=rand(1,N(i)); %注意随机数要在计算函数值和误差,方差前给定
    f=8*x;    
    y(i)=sum(f)/N(i); %求积分
    error(i)=abs(y(i)-4);
    sigma(i)=sqrt((1/N(i)*sum(f.^2)-(1/N(i)*sum(f))^2)/N(i));
end

figure;plot(1:length(N),error,'*-',1:length(N),sigma,'+-')
legend('均匀抽样法实际偏差','均匀抽样法标准偏差')



















重要抽样法中,函数 \(w(x)\) 的选择十分关键,应满足条件:

  1. \(w(x)\) 应当是个分布密度函数: \(\int_0^1w(x)dx = 1\)
  2. \(f / w\) 在积分域内应起伏不大,使之尽量等于常数,以保证方差 \(V\{ f /w\}\)\(V\{ f \}\) 小;
  3. 分布密度函数 \(w(x)\) 所对应的分布函数 \(y(x)\) 能够比较方便地解析求出 \(y(x) = \int_0^x w(x')dx'\)
  4. 能方便地产生在积分域内满足分布函数 \(y(x)\) 分布的随机点。

前面对一维积分的讨论很容易推广到形式为 \(I=\int d^n x f(\vec{x})\)\(n\) 维积分。 \[I \approx \frac{1}{N} \sum_{i=1}^N f(\vec{x}_i),\] 其中随机点的诸分量 \(\vec{x}_i\) 要独立选取。

Example 6 用蒙特卡洛方法计算二重积分 \[ I = \int_0^1\int_0^1 \ln(1+2x+2y)dxdy = 1.057615826 \]

Solution 6.

%filename: chapter5_example_5_2d_integration.m
%采用蒙特卡洛积分求2D积分;
clear all
close all
clc
format long
f=@(x,y)log(1+2*x+2*y);
s=dblquad(f,0,1,0,1,10^-10) %Matlab中的二维积分命令
N=[10,20,50,100,200,500,1000,2000,5000,10000,20000,50000];
for i=1:length(N)
    x=rand(1,N(i)); %注意随机数要在计算函数值和误差,方差前给定
    y=rand(1,N(i));
    yy(i)=sum(log(1+2*x+2*y))/N(i);
    error(i)=abs(yy(i)-s);
end
error1=error/s
plot(1:length(N),error1,'*-')
title('误差与精确解的比例')






















Example 7 用蒙特卡洛方法计算三重积分 \[ I = \int_0^1\int_0^1\int_0^1(2\pi)^{-3/2} \exp\left[-\frac{1}{2}(x^2+y^2+z^2)\right]dxdydz = 0.039772182 \]

Solution 7.

%filename: chapter5_example_6_3d_integration.m
%采用蒙特卡洛积分求3D积分
clear all
close all;clc
format long
f=@(x,y,z)exp(-(x.^2+y.^2+z.^2)/2)*(2*pi)^(-1.5);
s=triplequad(f,0,1,0,1,0,1,10^-10) %Matlab中三重积分命令
N=[10,20,50,100,200,500,1000,2000,5000,10000,20000,50000,10^7];
for i=1:length(N)
    x=rand(1,N(i)); %注意随机数要在计算函数值和误差,方差前给定;
    y=rand(1,N(i));
    z=rand(1,N(i));
    yy(i)=sum((2*pi)^(-1.5)*exp(-0.5*(x.^2+y.^2+z.^2)))/N(i);
    error(i)=abs(yy(i)-s);
end
error1=error/s
plot(1:length(N),error1,'*-')



















Code
Ns = np.array([10,20,50,100,200,500,1000,2000,5000,10000,20000,50000])
res = np.zeros(Ns.shape)
for i in range(len(Ns)):
    [x, y, z] = np.random.random((3, Ns[i]))
    ff = np.exp(-0.5 * (x**2.0 + y**2.0 + z**2.0))
    res[i] = (2.0 * np.pi)**(-1.5) * np.mean(ff)
plt.plot(Ns, res - 0.039772182)
plt.xscale('log')
plt.show()

4.0.2 与传统积分方法做比较

  • 求解一维定积分时,蒙特卡罗方法的效率其实是很差的。

  • 标准的梯形积分方法的效率达到 \(N^{-2}\)\(O(h^2)\), 误差数量级)

  • 蒙特卡罗方法的效率只有 \(N^{-1/2}\)\(O(h^{1/2})\),误差数量级) 。

  • 蒙特卡洛罗方法的误差不依赖于维数。梯形法是依赖于维数的,总误差为 \(O(N^{-2/d})\)

  • 虽然在低维积分时不如常规积分方法,一般情况下,当维数大于 4 维时,用蒙特卡罗方法的优势就体现出来了。

  • 在维数很高的情况,只有蒙特卡洛罗方法可以用。

5 具有特定分布的随机变量的产生

Monte Carlo求积包括两种基本运算:

  1. 产生以规定的概率密度 \(w(x)\) (它可以是\(1\))在积分体积内随机分布的坐标点;
  2. 然后在这些坐标点上计算函数 \(f / w\) 的值。

均匀分布的随机数是容易实现的,我们怎样按照一个规定的权函数 \(w(x)\) 分布选取一维随机变量? 能否以及如何通过均匀分布的随机数得到特定分布的随机数?

5.1 指数分布随机数的生成

例如,如果要计算积分 \(I = \int_0^\infty dx e^{-x} g(x)\), 设想 \(g\) 是一个比较平滑的函数。那么一种可取的作法是在 \(0\)\(\infty\) 之间产生按 \(e^{-x}\) 分布的 \(x\),然后在这些值上对 \(g\) 求平均。

\[ w(x) = e^{-x}, ~~ y(x) = \int_0^\infty w(x')dx' = \int_0^\infty e^{-x'}dx' \]

\[ \rightarrow y = 1-e^{-x}, ~~ x = -\ln(1-y), ~~ dy = e^{-x}dx \]

\[ y_i \rightarrow x_i; ~~ I = \int_0^\infty dx e^{-x}g(x) = \int_0^1dyg[-\ln(1-y)] \]

%filename: chapter5_e_zhishu.m
function e_zhishu
clear all
close all
clc
%指数分布的随机数;
N=100000;
y=rand(1,N);
figure;plot(1:N,y,'*')
x=-log(1-y);
figure;plot(1:N,x,'*')
%---------统计均匀分布的随机数的分布是否均匀------------
M=100;
%Max=max(y);
yy=zeros(1,M);
h=1/M;
xx=0:h:1;
for i=1:length(y)
    index=fix(y(i)/h)+1;
    yy(1,index)=yy(1,index)+1;
end
figure;bar(xx(1:M),yy)
yy=yy/N;
figure;plot(xx(1:M),yy,'*-',0:0.01:1,1/M,'r');
title('均匀分布的随机数');

%---------统计指数分布的随机数的分布------------
Max1=max(x)+10^-9 %加上一个很小的数10^-9,主要是为了for循环中的统计的需要,使得max(x)所在分区不会超过M值
yy1=zeros(1,M);
h1=Max1/M;
xx1=0:h1:Max1;
for i=1:length(x)
    index=fix(x(i)/h1)+1;
    yy1(1,index)=yy1(1,index)+1;
end
figure;bar(xx1(1:end-1),yy1); 
w=@(xx2)exp(-xx2);
S=interg(0,Max1,w)
yy1=yy1/N/h1*S;  
%因为yy1/N/h1在[a,b]上的积分为1,为了与w比较,乘以其在[0,Max1]上的积分S,绝大部分情况S=1
%yy1=yy1/N;
figure;title('指数分布的随机数');plot(xx1(1:M),yy1,'r*-');
hold on
plot(xx1(1:M),exp(-xx1(1:M)),'b')
hold off
legend('生成随机数的分布','精确的指数分布')

function S=interg(a,b,w) %w函数在[a,b]上的积分
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式















    

5.2 高斯分布随机数的生成

物理学中一个有用的分布是Gauss分布(正态分布) \[ w(x) = \frac{1}{\sqrt{2\pi}} e^{-x^2/2} \] with \(\bar{x} = 0, \sigma = 1\)

\[ y(x) = \int_0^xw(x)dx = \int_0^x \frac{1}{\sqrt{2\pi}} e^{-x^2/2} \] 此方法无法获得\(x\)的反函数

👍 一个“聪明”的产生Gauss分布的方法建立在中心极限定理上。该定理表明:大量均匀分布的随机数之和将趋近于一个Gauss分布。由于\([0,1]\)区间上的均匀分布的平均值和方差分别为\(1/2\)\(1/12\), \(12\)个均匀分布的随机数之和的平均值将是\(6\),方差是\(1\),并且将非常接近于Gauss分布。

%filename: chapter5_Gauss.m
function Gauss_1
clear all
close all
clc
%Gauss分布的随机数;

N=100000;
Gauss=zeros(1,N);
for i=1:12
    Gauss=Gauss+rand(1,N);
end

Gauss=Gauss-6;
figure;plot(Gauss,1:N,'*')

%------判断分布是否为Gauss分布-----------------
Num=501;                                                %区间大小
Max=max(abs(Gauss))+10^-9;                       %边界
y=zeros(1,Num-1);
x=linspace(-Max,Max,Num);   %-Max,Max间分为501个点,500段
h=x(2)-x(1);
for i=1:length(Gauss)
    index=fix((Gauss(i)+Max)/h)+1;
    y(index)=y(index)+1;
end
figure;bar(x(1:end-1),y);                       %生成数绘图
f=@(x) exp(-x.^2/2)/(2*pi)^0.5;                 %精确值绘图
figure,fplot(f,[-Max,Max],'r');
hold on;
S=interg(-Max,Max,f)
y=y/N/h*S; 
%因为y/N/h在[-Max,Max]上的积分为1,为了与w比较,乘以其在[-Max,Max]上的积分S,绝大部分情况S=1
plot(x(1:end-1),y,'*-');
legend('精确的Gauss分布','随机数的分布')
title('验证随机数的Gauss分布');
hold off

function S=interg(a,b,w) %w函数在[a,b]上的积分,利用的梯形算法
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式






















5.3 任意分布的随机数的生成

概率分布函数 \(w(x)\) 较简单时,可以通过解析的积分变换将均匀分布的随机数转化为按照 \(w(x)\) 分布的随机数。 \(w(x)\) 较复杂时,只能通过数值方法来获得相应分布的随机数序列。

5.4 方法一

设想 \(y\)\([0, 1]\) 区间一系列等间隔的值 \(y(j) \equiv j / M\)\(j=0,1,…, M\) ,则 \(y(j)\)\(x(j)\) 通过下式相联系 \[ y(j) \equiv \frac{j}{M} = \int_0^{x((j)} dx' \cdot w(x') \] 现在问题当然就在于如何产生 \(x(j)\) ,这可以通过把微分方程 \(dy/dx=w(x)\) 的积分简单的离散化而得出。 \[ \frac{y(j+1) - y(j)}{x(j+1) - x(j)} = w(x(j)); ~~~ y(j+1) - y(j) = \frac{1}{M} \] \[ \Longrightarrow x(j+1) = x(j) + \frac{1}{Mw(x(j))} \] 该关系可以同启动值 \(x(0) =0\) 一起使用。

5.5 方法二: von Neumann Rejection

  • 设我们对产生在0和1之间按 \(w(x)\) 分布的 \(x\) 有兴趣,令 \(w'(x)\) 是一个正函数,并且在积分区域上 \(w'(x) > w(x)\)

  • 实用的做法是选择两个随机变数 \(x_i\)\(h\) ,前者按 \(w'\) 分布(简单的做法可以取 \(w'\) 为大于 \(w(x)\) 的极大值的常数),而后者 \(h\)\(0\)\(1\) 之间均匀分布, 若比值 \(w(x_i )/w'(x_i )\) 大于 \(h\) ,就接受该 \(x_i\) 值;而如果一点被舍弃,我们只要继续往下做,产生另一对 \(x_i\)\(h\)

  • 显然,只有当 \(w'\) 在全部积分范围内都接近 \(w\) 时,该方法的效率才高。否则,大量时间将浪费在舍弃无用的点上。

5.5.1 产生\([a,b]\)区间上按 \(w(x)\) 分布的随机数的von Neumann舍选法

设随机变量x的取值区间为 \(x\in[a,b]\), 其概率密度函数 \(w(x)\) 有界,即 \[ \max\{w(x)|a\leq x \leq b\} = c \equiv w'(x) \]

舍选法抽样步骤

  1. 产生 \([a, b]\) 区间内均匀分布的随机数 \(x\): \(x = (b-a)r_1+a\), \(r_1 \in U[0, 1]\);
  2. 产生 \([0,c]\) 区间内均匀分布的随机数 \(y\): \(y = cr_2\), \(r_2 \in U[0,1]\);
  3. \(y \leq w (x)\) 时(即 \(r_2 \leq w (x) /c\)),接受 \(x\) 为所需的随机数,否则,返回到第一步重新抽取一对 \((x,y)\).

flowchart LR

A("`select r1, r2 from U[0, 1]`")
B("`x = a + (b-a)r1, y = c * r2`")
C{"y <= w(x)"}
D("X = x")

A --> B --> C -->|true| D
C -->|false| A

Example 8 用von Neumann舍选法在 \([0,1]\) 区间上产生按照 \[ w(x) = \frac{6}{5}\left(1-\frac{1}{2}x^2\right) \] 分布的样点,比较选择不同 \(w'\) 时的计算效率

Solution 8.

%filename: chapter5_example_8_von_Neumann.m
function Von_Neumann
clear all
close all
clc

tic
%利用Von Neumann舍选法产生随机数
a=0;b=1.0;
x=a:0.001:b;
w=@(x) 6/5*(1-0.5*x.^2);
y_m=max(w(x))*1.1;   %即c,也即选择w'为常数

N=50000; %随机数的个数
i=0;     
n=0;
while i<N
    xx=(b-a)*rand(1,1)+a;%产生[a,b]区间上的均匀分布的随机数
    yy=y_m*rand(1,1);
    n=n+1;   %统计产生了多少次随机数
    if yy<=w(xx) %舍选法的判断标准
        i=i+1;
        xx_s(i)=xx; 
    end
end
N/n   %选取比例
plot(xx_s(1:N),1:N,'*')

%---------统计随机数的分布------------
M=100;
yy1=zeros(1,M);
h1=(b-a)/M;
xx1=a:h1:b;
for i=1:length(xx_s)
    index=fix(xx_s(i)/h1)+1;
    yy1(1,index)=yy1(1,index)+1;
end
figure;bar(xx1(1:end-1),yy1);                       %生成数绘图
S=interg(a,b,w)
yy1=yy1/N/h1*S; %因为yy1/N/h1在[a,b]上的积分为1,为了与w比较,乘以其在[a,b]上的积分S
figure;plot(xx1(1:M),yy1,'r*-');title('Von Neumann舍选法产生的随机数分布');
hold on
plot(xx1(1:M),w(xx1(1:M)),'b')
legend('Von Neumann舍选法产生的随机数分布','精确分布')
hold off
toc
%figure,plot(xx_s,1:N,'*')

function S=interg(a,b,w) %w函数在[a,b]上的积分
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式


















Code
b = 1.0
a = 0.0
w = lambda x: 1.2 * (1.0 - 0.5 * x * x)
ymax = np.max(w(np.linspace(a, b, 1000)))

nn = 1000000
i = 0
xres = np.zeros(nn)
while i < nn:
    xx = a + (b - a) * np.random.random()
    yy = ymax * np.random.random()
    if (yy < w(xx)):
        xres[i] = xx
        i += 1

cc, cx = np.histogram(xres, bins = 100)
dx = cx[1] - cx[0]
plt.plot(cx[1:] - 0.5 * (cx[1] - cx[0]), cc / nn / dx)
plt.plot(np.linspace(a, b, 100), w(np.linspace(a, b, 100)))
plt.show()

Example 9 用von Neumann舍选法在 \([0,8]\) 区间上产生按照 \[ w(x) = e^{-x} \] 分布的随机数

Solution 9.

%filename: chapter5_e_zhishu.m
function e_zhishu
clear all
close all
clc
%指数分布的随机数;
N=100000;
y=rand(1,N);
figure;plot(1:N,y,'*')
x=-log(1-y);
figure;plot(1:N,x,'*')
%---------统计均匀分布的随机数的分布是否均匀------------
M=100;
%Max=max(y);
yy=zeros(1,M);
h=1/M;
xx=0:h:1;
for i=1:length(y)
    index=fix(y(i)/h)+1;
    yy(1,index)=yy(1,index)+1;
end
figure;bar(xx(1:M),yy)
yy=yy/N;
figure;plot(xx(1:M),yy,'*-',0:0.01:1,1/M,'r');
title('均匀分布的随机数');

%---------统计指数分布的随机数的分布------------
Max1=max(x)+10^-9 %加上一个很小的数10^-9,主要是为了for循环中的统计的需要,使得max(x)所在分区不会超过M值
yy1=zeros(1,M);
h1=Max1/M;
xx1=0:h1:Max1;
for i=1:length(x)
    index=fix(x(i)/h1)+1;
    yy1(1,index)=yy1(1,index)+1;
end
figure;bar(xx1(1:end-1),yy1); 
w=@(xx2)exp(-xx2);
S=interg(0,Max1,w)
yy1=yy1/N/h1*S;  
%因为yy1/N/h1在[a,b]上的积分为1,为了与w比较,乘以其在[0,Max1]上的积分S,绝大部分情况S=1
%yy1=yy1/N;
figure;title('指数分布的随机数');plot(xx1(1:M),yy1,'r*-');
hold on
plot(xx1(1:M),exp(-xx1(1:M)),'b')
hold off
legend('生成随机数的分布','精确的指数分布')

function S=interg(a,b,w) %w函数在[a,b]上的积分
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式















    
%filename: chapter5_e_zhishu_von_Neumann.m
function e_zhishu_Neumann
clear all
close all
clc

%利用Von Neumann舍选法产生指数分布随机数
a=0;b=8.0;
x=a:0.001:b;
w=@(x) exp(-x);
y_m=max(w(x));   %即c,也即选择w'为常数

N=20000; %随机数的个数
i=0;     
n=0;
while i<N
    xx=(b-a)*rand(1,1)+a;%产生[a,b]区间上的均匀分布的随机数
    yy=y_m*rand(1,1);
    n=n+1;   %统计产生了多少次随机数
    if yy<=w(xx) %舍选法的判断标准
        i=i+1;
        xx_s(i)=xx; 
    end
end
N/n   %选取比例
plot(xx_s(1:N),1:N,'*')

%---------统计随机数的分布------------
M=100;
yy1=zeros(1,M);
h1=(b-a)/M;
xx1=a:h1:b;
for i=1:length(xx_s)
    index=fix(xx_s(i)/h1)+1;
    yy1(1,index)=yy1(1,index)+1;
end
figure;bar(xx1(1:end-1),yy1);                       %生成数绘图
S=interg(a,b,w)
yy1=yy1/N/h1*S; %因为yy1/N/h1在[a,b]上的积分为1,为了与w比较,乘以其在[a,b]上的积分S
figure;plot(xx1(1:M),yy1,'r*-');title('Von Neumann舍选法产生的随机数分布');
hold on
plot(xx1(1:M),w(xx1(1:M)),'b')
legend('Von Neumann舍选法产生的随机数分布','精确分布')
hold off

function S=interg(a,b,w) %w函数在[a,b]上的积分 
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式


















    

Example 10 用von Neumann舍选法产生 \([-8,8]\) 区间上按照 \[ w(x) = \frac{1}{\sqrt{2\pi}} e^{-x^2/2} \] 分布的随机数

Solution 10.

%filename: chapter5_Gauss.m
function Gauss_1
clear all
close all
clc
%Gauss分布的随机数;

N=100000;
Gauss=zeros(1,N);
for i=1:12
    Gauss=Gauss+rand(1,N);
end

Gauss=Gauss-6;
figure;plot(Gauss,1:N,'*')

%------判断分布是否为Gauss分布-----------------
Num=501;                                                %区间大小
Max=max(abs(Gauss))+10^-9;                       %边界
y=zeros(1,Num-1);
x=linspace(-Max,Max,Num);   %-Max,Max间分为501个点,500段
h=x(2)-x(1);
for i=1:length(Gauss)
    index=fix((Gauss(i)+Max)/h)+1;
    y(index)=y(index)+1;
end
figure;bar(x(1:end-1),y);                       %生成数绘图
f=@(x) exp(-x.^2/2)/(2*pi)^0.5;                 %精确值绘图
figure,fplot(f,[-Max,Max],'r');
hold on;
S=interg(-Max,Max,f)
y=y/N/h*S; 
%因为y/N/h在[-Max,Max]上的积分为1,为了与w比较,乘以其在[-Max,Max]上的积分S,绝大部分情况S=1
plot(x(1:end-1),y,'*-');
legend('精确的Gauss分布','随机数的分布')
title('验证随机数的Gauss分布');
hold off

function S=interg(a,b,w) %w函数在[a,b]上的积分,利用的梯形算法
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式






















%filename: chapter5_Gauss_von_Neumann.m
function e_Gauss_Neumann
clear all
close all
clc

%利用Von Neumann舍选法产生Gauss分布随机数
a=-8;b=8.0;
x=a:0.001:b;
w=@(x) 1/sqrt(2*pi)*exp(-x.^2/2);
y_m=max(w(x));   %即c,也即选择w'为常数

N=100000; %随机数的个数
i=0;     
n=0;
while i<N
    xx=(b-a)*rand(1,1)+a;%产生[a,b]区间上的均匀分布的随机数
    yy=y_m*rand(1,1);
    n=n+1;   %统计产生了多少次随机数
    if yy<=w(xx) %舍选法的判断标准
        i=i+1;
        xx_s(i)=xx; 
    end
end
N/n   %选取比例

%------判断分布是否为Gauss分布-----------------
Num=501;                                                %区间大小
Max=max(abs(xx_s))+10^-9;                       %边界
y=zeros(1,Num-1);
x=linspace(-Max,Max,Num);   %-Max,Max间分为501个点,500段
h=x(2)-x(1);
for i=1:length(xx_s)
    index=fix((xx_s(i)+Max)/h)+1;
    y(index)=y(index)+1;
end
figure;bar(x(1:end-1),y);     
S=interg(a,b,w)
yy1=y/N/h*S; %因为y/N/h在[a,b]上的积分为1,为了与w比较,乘以其在[a,b]上的积分S
figure;plot(x(1:Num-1),yy1,'r*-');title('Von Neumann舍选法产生的随机数分布');
hold on
plot(x(1:Num-1),w(x(1:Num-1)),'b')
legend('Von Neumann舍选法产生的随机数分布','精确分布')
hold off

function S=interg(a,b,w) %w函数在[a,b]上的积分 
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式
















    

Example 11 用蒙特卡洛方法模拟氢原子的电子云分布 由原子物理理论和量子理论可知,氢原子态的波函数只是半径的函数,与角度无关。而氢原子中电子的分布密度,即电子在半径处单位厚度球壳内出现的几率 \[ D = 4\pi r^2\psi_s^2 \] 习惯上把这种分布形象地称为电子云。

  •  氢原子的基态即 \(1s\) 态(\(n=1,l=0,m=0\)),有 \[ D = \frac{4r^2}{a_1^3}e^{-2r/a_1}, ~~~ D_\max = 1.1, ~~~ r_0 \approx 0.25\text{nm} \] 其中 \(a_1 = 5.29\times 10^{-2}~\text{nm}\), ,是 \(D\) 的最大值处的 \(r\) 值,其值与玻尔半径相同。 \(r_0\)\(D\) 收敛处的 \(r\) 值,即 \(D\) 的收敛点。

Solution 11.

%filename: chapter5_Von_Neumann_H_1S.m(1s态电子云)
function Von_Neumann_H_1S
clear all
close all
clc

%利用Von Neumann舍选法模拟H原子的1S态电子云;
a=0;b=5.5; %长度单位为AA(angstrom)
r=a:0.001:b;
a1=0.529;
w=@(r) 4/a1^3*r.^2.*exp(-2*r/a1);
y_m=max(w(r));   %即c,也即选择w'为常数

N=90000; %随机数的个数
i=0;     
n=0;
while i<N
    rr=(b-a)*rand(1,1)+a;%产生[a,b]区间上的均匀分布的随机数
    yy=y_m*rand(1,1);
    n=n+1;   %统计产生了多少次随机数
    if yy<=w(rr) %舍选法的判断标准
        i=i+1;
        rr_s(i)=rr; 
    end
end
N/n   %选取比例

%nn=length(rr_s);
Q=2*pi*rand(1,N);
[X,Y]=pol2cart(Q,rr_s); %坐标变换,将极坐标转化为笛卡尔坐标,polarity to Cartesian 
figure,
subplot(131)
plot(X,Y,'r.','markersize',6)
title('利用Von Neumann舍选法模拟H原子的1S态电子云')

%---------统计随机数的分布------------
M=200;
yy1=zeros(1,M);
h1=(b-a)/M;
rr1=a:h1:b;
for i=1:length(rr_s)
    index=fix(rr_s(i)/h1)+1;
    yy1(1,index)=yy1(1,index)+1;
end
subplot(132)
bar(rr1(1:end-1),yy1);     
S=interg(a,b,w)
yy1=yy1/N/h1*S; %因为yy1/N/h1在[a,b]上的积分为1,为了与w比较,乘以其在[a,b]上的积分S
%figure;
subplot(133)
plot(rr1(1:M),yy1,'r.');title('Von Neumann舍选法产生的随机数分布');
hold on
plot(rr1(1:M),w(rr1(1:M)),'b')
legend('Von Neumann舍选法产生的随机数分布','精确分布')
hold off
setFigure(gcf, 'latex')
%figure,plot(xx_s,1:N,'*')

function S=interg(a,b,w) %w函数在[a,b]上的积分,利用的梯形算法
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式

















  • 氢原子的 \(2s\) 态(\(n=2,l=0,m=0\)),有 \[ D = \frac{r^2}{8a_1^3}\left(2 - \frac{r}{a_1}\right)^2 e^{-r/a_1}, ~~~ D_\max = 0.14, ~~~ r_0 = 1.0~~\text{nm} \]
%filename: chapter5_Von_Neumann_H_2S.m(2s态电子云)
function Von_Neumann_H_2S
clear all
close all
clc

%利用Von Neumann舍选法模拟H原子的2S态电子云
a=0;b=15; %长度单位为AA(angstrom)
r=a:0.001:b;
a1=0.529;
w=@(r) (r.^2/8/a1^3).*(2-r/a1).^2.*exp(-r/a1);
y_m=max(w(r));   %即c,也即选择w'为常数

N=90000; %随机数的个数
i=0;     
n=0;
while i<N
    rr=(b-a)*rand(1,1)+a;%产生[a,b]区间上的均匀分布的随机数
    yy=y_m*rand(1,1);
    n=n+1;   %统计产生了多少次随机数
    if yy<=w(rr) %舍选法的判断标准
        i=i+1;
        rr_s(i)=rr; 
    end
end
N/n   %选取比例

%nn=length(rr_s);
Q=2*pi*rand(1,N);
[X,Y]=pol2cart(Q,rr_s);
figure,
subplot(131)
plot(X,Y,'r.','marker','.','markersize',6)
title('利用Von Neumann舍选法模拟H原子的2S态电子云')

%---------统计随机数的分布------------
M=200;
yy1=zeros(1,M);
h1=(b-a)/M;
rr1=a:h1:b;
for i=1:length(rr_s)
    index=fix(rr_s(i)/h1)+1;
    yy1(1,index)=yy1(1,index)+1;
end
%figure;
subplot(132)
bar(rr1(1:end-1),yy1);  
S=interg(a,b,w)
yy1=yy1/N/h1*S; %因为yy1/N/h1在[a,b]上的积分为1,为了与w比较,乘以其在[a,b]上的积分S
%figure,
subplot(133)
plot(rr1(1:M),yy1,'r*');title('Von Neumann舍选法产生的随机数分布');
hold on
plot(rr1(1:M),w(rr1(1:M)),'b')
legend('Von Neumann舍选法产生的随机数分布','精确分布')
hold off

setFigure(gcf, 'latex')
%figure,plot(xx_s,1:N,'*')

function S=interg(a,b,w) %w函数在[a,b]上的积分 
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式














  • 氢原子的 \(3s\) 态 (\(n=3,l=0,m=0\)),有 \[ D = \frac{4}{3a_1^3}\left(\frac{r}{81}\right)^2\left[27 - \frac{18r}{a_1} + 2\left(\frac{r}{a_1}\right)^2\right]^2e^{-2r/3a_1} \] \[ D_\max = 0.2, ~~~ r_0 \approx 0.2\text{nm} \]
%filename: chapter5_Von_Neumann_H_3S.m(3s态电子云)
function Von_Neumann_H_3S
clear all
close all
clc

%利用Von Neumann舍选法模拟H原子的3S态电子云;
a=0;b=25; %长度单位为AA(angstrom)
r=a:0.001:b;
a1=0.529;
w=@(r) 4/3/a1^3*(r/81).^2.*(27-18/a1*r+2*r.^2/a1^2).^2.*exp(-2*r/3/a1);
y_m=max(w(r));   %即c,也即选择w'为常数

N=90000; %随机数的个数
i=0;     
n=0;
while i<N
    rr=(b-a)*rand(1,1)+a;%产生[a,b]区间上的均匀分布的随机数
    yy=y_m*rand(1,1);
    n=n+1;   %统计产生了多少次随机数
    if yy<=w(rr) %舍选法的判断标准
        i=i+1;
        rr_s(i)=rr; 
    end
end
N/n   %选取比例

%nn=length(rr_s);
Q=2*pi*rand(1,N);
[X,Y]=pol2cart(Q,rr_s);
figure,
subplot(121)
plot(X,Y,'r.','marker','.','markersize',6)
title('利用Von Neumann舍选法模拟H原子的3S态电子云')

%---------统计随机数的分布------------
M=200;
yy1=zeros(1,M);
h1=(b-a)/M;
rr1=a:h1:b;
for i=1:length(rr_s)
    index=fix(rr_s(i)/h1)+1;
    yy1(1,index)=yy1(1,index)+1;
end
S=interg(a,b,w);
yy1=yy1/N/h1*S; %因为yy1/N/h1在[a,b]上的积分为1,为了与w比较,乘以其在[a,b]上的积分S
%figure,
subplot(122)
plot(rr1(1:M),yy1,'r*');title('Von Neumann舍选法产生的随机数分布');
hold on
plot(rr1(1:M),w(rr1(1:M)),'b')
legend('Von Neumann舍选法产生的随机数分布','精确分布')
hold off
setFigure(gcf, 'latex')
%figure,plot(xx_s,1:N,'*')

function S=interg(a,b,w) %w函数在[a,b]上的积分 
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式















Example 12 处于平衡态的气体中粒子的速率分布的概率函数为麦克斯韦-波尔兹曼分布,产生符合该分布的随机数对于系统宏观性质的计算非常重要。利用von Neumann舍选法产生符合如下麦克斯韦-波尔兹曼分布的随机数 \[ f(v) = \sqrt{\frac{2}{\pi} \left(\frac{m}{kT}\right)^3} v^2 \exp\left( -\frac{mv^2}{2kT}\right) \]\(m=6.64 \times 10^{-27}\) Kg(He原子),\(T=300\text{K},k=1.38 \times 10^{-23} \text{J} \cdot \text{K}^{-1}\) 根据产生的速率分布,证明具有 \(80000\) 个氦原子的系统,其平均速率与均方根速率与解析结果一致,比较数值结果与解析结果的相对误差。

平均速率 \(\bar{v} = \int_0^\infty vf(v)dv = \frac{8kT}{\pi m}\)

均方根速率 \(v_{rms} = \left( \int_0^\infty v^2 f(v) dv \right)^{1/2} = \sqrt{\frac{3kT}{m}}\)

Solution 13.

%filename: chapter5_example_12_von_Neumann.m
function chapter6_example_12_von_Neumann
clear all
close all
clc

%利用Von Neumann舍选法产生指数分布随机数
a=0;b=4000.0;
x=a:0.001:b;
m=6.64*10^-27;
T=300;
k=1.38*10^-23;
w=@(v) sqrt(2/pi*(m/k/T)^3)*v.^2.*exp(-m*v.^2/(2*k*T)); %概率密度函数,与例题8相同
y_m=max(w(x));   %即c,也即选择w'为常数

N=80000; %随机数的个数
i=0;     
n=0;
while i<N
    xx=(b-a)*rand(1,1)+a;%产生[a,b]区间上的均匀分布的随机数
    yy=y_m*rand(1,1);
    n=n+1;   %统计产生了多少次随机数
    if yy<=w(xx) %舍选法的判断标准
        i=i+1;
        xx_s(i)=xx; 
    end
end
N/n   %选取比例

%---------统计随机数的分布------------
M=100;
yy1=zeros(1,M);
h1=(b-a)/M;
xx1=a:h1:b;
for i=1:length(xx_s)
    index=fix(xx_s(i)/h1)+1;
    yy1(1,index)=yy1(1,index)+1;
end
figure;bar(xx1(1:end-1),yy1);  
S=interg(a,b,w)
yy1=yy1/N/h1*S; %因为yy1/N/h1在[a,b]上的积分为1,为了与w比较,乘以其在[a,b]上的积分S
figure;plot(xx1(1:M),yy1,'r*-');title('Von Neumann舍选法产生的随机数分布');
hold on
plot(xx1(1:M),w(xx1(1:M)),'b')
legend('Von Neumann舍选法产生的随机数分布','精确分布')
hold off
%--------比较数值平均速率,均方根速率与解析结果的相对误差---------
v_bar=sqrt(8*k*T/pi/m)
v_num=sum(xx_s)/length(xx_s)
error1=abs(v_bar-v_num)/v_bar
v2_bar=sqrt(3*k*T/m)
v2_num=sqrt(sum(xx_s.^2)/length(xx_s))
error2=abs(v2_bar-v2_num)/v2_bar

function S=interg(a,b,w) %w函数在[a,b]上的积分 
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式


















6 Metropolis算法

虽然前面我们讲述了多种按照规定分布产生随机数的有效方法,但要推广这些方法以对多维空间中的一个复杂的权函数抽样却是很困难或者是不可能的。一种很普遍的产生具有任意形状的给定概率分布的随机变量的方法叫做Metropolis-Rosenbluth-Rosenbluth-Teller-Teller算法(简称Metropolis算法)。该算法已经广泛地应用在统计力学等很多学科中。

Nicolas Metropolis, Arianna W. Rosenbluth, Marshall N. Rosenbluth, Augusta H. Teller, Edward Teller J. Chem. Phys. 21, 1087 (1953).

6.1 Metropolis算法的实现

  • 设想在变量 \(X\) 的空间(可能是多维的)内产生一组按概率密度 \(w (X )\) 分布的点,Metropolis算法假想有一个随机行走者\(X\) 空间中运动,该随机行走过程相继各步的终点产生出点的一个序列:\(X_0, X_1, …\);随着行走的路程越长,它连接的点就越接近所要求的分布。

  • 随机行走在位形空间中进行的规则为:设行走者处于序列中的 \(X_n\) 点上,为了产生 \(X_{n+1}\) ,行走者迈出试探性的一步到一新点 \(X_t\) ,该新点可以用任何方便的方法选取。例如可以在点 \(X_n\) 周围的一个边长 \(d\) 很小的多维立方体中均匀地随机选取,然后按比值 \[ r = \frac{w(x_t)}{w(x_n)} \] 来决定是“接受”还是“拒绝”该步试验。如果 \(r >1\),接受该步(即取 \(X_{n+1} =X_t\) );而如果 \(r <1\),则以概率 \(r\) 接受这一步。

  • 这时我们把 \(r\) 和一个在 \([0,1]\) 区间上均匀分布的随机数 \(h\) 比较,若 \(h <r\) 就接受这一步。如果该步试验未被接受,就舍弃,取 \(X_{n+1} = X_n\)

  • 这样产生出 \(X_{n+1}\) 之后,可以再从 \(X_{n+1}\) 出发迈出一个试验步按照同样的过程产生 \(X_{n+2}\) 。任意点 \(X_0\) 都可用作随机行走的起点。

上述算法的核心思想就是尽量多地选择 \(w(X)\) 较大的点。

6.2 高斯分布随机数的生成(Metropolis算法)

\[ w(x) = \frac{1}{\sqrt{2}}e^{-x^2 / 2} \] Code:

%filename: chapter5_metropolis_0.m(一维高斯分布)
function metropolis_1_example
clear all
close all
clc

tic
x1=0;
delta=2;
w=@(x)1/sqrt(2*pi)*exp(-x.^2/2); %一维高斯分布
%w=@(x1) x1^2;   %抛物线分布;
%w=@(x1) exp(-x1); %指数分布
n=0;
N=100000;
for i=1:N
    [x1,a]=my_metropolis(x1,delta,w);
    if a==1
        n=n+1;
        xx(n)=x1; %将接受的x1值存储起来
        plot(x1,n,'*')
    end
   %pause(0.001)
    hold all
end
%hold off
n/N
%下面的程序判断metropolis算法生成的随机数分布是否是gauss分布
%========================================================
Num=501;                                                %区间大小
Max=max(abs(xx))+10^-9;                       %边界
y=zeros(1,Num-1);
x=linspace(-Max,Max,Num);
h=x(2)-x(1);
for i=1:length(xx)
    index=fix((xx(i)+Max)/h)+1; %判定xx(i)处于哪个子区间,fix为舍去小数取整运算
    y(index)=y(index)+1;          %该子区间的随机数数目+1
end
figure;bar(x(1:end-1),y);  
f=@(xx1) exp(-xx1.^2/2)/(2*pi)^0.5;                 %精确值绘图
figure,fplot(f,[-Max,Max],'r');
hold on;
S=interg(-Max,Max,f)
y=y/length(xx)/h*S;  %生成的随机数的数目为n(length(xx)),走的总步数为N
plot(x(1:Num-1),y,'*');title('验证随机数的Gauss分布');
legend('精确的Gauss分布','随机数的分布')
hold off
toc

function S=interg(a,b,w) %w函数在[a,b]上的积分 
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式
%=========================================================================

function [x1,a] = my_metropolis(x1,delta,w)
x1t=x1+delta*(2*rand-1); %2*rand-1用来产生在[-1,1]上均匀分布的随机数
r=w(x1t)/w(x1);
if r<rand
    a=0;
else
    x1=x1t;
    a=1;
end


















Code
def metroplis1d(x0, delta, w):
    xt = x0 + delta * (2.0 * np.random.random() - 1.0)
    wx0 = w(x0)
    wxt = w(xt)
    if (wxt > wx0):
        return xt
    else:
        prob = np.random.random()
        if (wxt / wx0 > prob):
            return xt
        else:
            return x0


x0 = 0
nn = 10000
xres = np.zeros(nn)
i = 0
xres[0] = 0
wfunc = lambda x: 1.0 / np.sqrt(2.0) * np.exp(-0.5 * x * x)
while i < nn-1:
    xres[i+1] = metroplis1d(xres[i], 2.0, wfunc)
    i += 1

plt.hist(xres, bins = 100)
plt.show()

\[ w(x) = \frac{1}{\sqrt{2}}e^{-(x^2+y^2) / 2} \]

Code:

%filename: chapter5_metropolis_1.m
function metropolis_1_example
clear all
close all
clc

x1=0;
x2=0;
delta=0.5;
w=@(xx1,xx2)1/(2*pi)*exp(-(xx1^2+xx2^2)/2); %二维高斯分布

n=0; N=10000;
for i=1:N
    [x1,x2,a]=my_metropolis(x1,x2,delta,w);
    if a==1
        n=n+1;
        xx1(n)=x1;
        xx2(n)=x2;
        plot(x1,x2,'*')
        %plot3(x1,x2,w(x1,x2),'*')
    end
pause(0.001)
hold all
end
%将x1,x2平面网格化,同样可以验证x1,x2的分布符合二维gaussian分布,可以留作拓展作业
hold off
n
n/N

function [x1,x2,a] = my_metropolis(x1,x2,delta,w)
x1t=x1+delta*(2*rand-1); %2*rand-1用来产生在[-1,1]上均匀分布的随机数
x2t=x2+delta*(2*rand-1);
r=w(x1t,x2t)/w(x1,x2);
if r<rand
    a=0;
else
    x1=x1t;
    x2=x2t;
    a=1;
end















Code
def metroplis2d(x0, delta, w):
    xt = x0 + delta * (2.0 * np.random.random(2) - 1.0)
    wx0 = w(x0)
    wxt = w(xt)
    if (wxt > wx0):
        return xt
    else:
        prob = np.random.random()
        if (wxt / wx0 > prob):
            return xt
        else:
            return x0


x0 = 0
nn = 100000
xres = np.zeros([nn, 2])
i = 0
xres[0] = 0
wfunc = lambda x: 1.0 / np.sqrt(2.0) * np.exp(-0.5 * (x[0] * x[0] + x[1] * x[1]))
while i < nn-1:
    xres[i+1, :] = metroplis2d(xres[i, :], 2.0, wfunc)
    i += 1

plt.hist2d(xres[:, 0], xres[:, 1], bins = 100);
plt.show()

Code
plt.figure(figsize = (12, 4))
plt.subplot(121)
plt.hist(xres[:, 0], bins = 100);
plt.xlabel(r'$x$');
plt.subplot(122)
plt.hist(xres[:, 1], bins = 100);
plt.xlabel(r'$y$');

用Metropolis算法在 \([0,1]\) 区间上产生按照

%filename: chapter5_example_8_von_Neumann_metropolis.m
function metropolis_2
clear all
close all
clc

x1=0.9;
delta=5.5;
w=@(x1) 6/5*(1-0.5*x1.^2); %概率密度函数,与例题8相同
n=0;
N=300000;
for i=1:N
    [x1,a]=my_metropolis(x1,delta,w);
    if a==1
        n=n+1;
        xx(n)=x1; %将接受的x1值存储起来        
    end
end
n/N
%下面的程序判断metropolis算法生成的随机数分布是否符合分布函数
%========================================================
Num=201;                                                %区间大小
Max=max(abs(xx))+10^-9                    %边界
Min=min(xx)*1.0                      %边界
y=zeros(1,Num);
x=linspace(Min,Max,Num);
h=x(2)-x(1);
for i=1:length(xx)
    index=fix((xx(i)-Min)/h)+1; %判定xx(i)处于哪个子区间,fix为舍去小数取整运算
    y(index)=y(index)+1;          %该子区间的随机数数目+1
end
f=@(x1) 6/5*(1-0.5*x1.^2);            %精确值绘图
figure,fplot(f,[Min,Max],'r');
hold on;
S=interg(Min,Max,f)
y=y/length(xx)/h*S;  %生成的随机数的数目为n(length(xx)),走的总步数为N
plot(x(1:Num-1),y(1:Num-1),'-*');title('验证随机数的分布');
legend('精确分布','随机数的分布')
hold off

function S=interg(a,b,w) %w函数在[a,b]上的积分 
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式
%=========================================================================

function [x1,a] = my_metropolis(x1,delta,w)
x1t=x1+delta*(2*rand-1);  
r=w(x1t)/w(x1);
if r<rand | x1t<0 |x1t>1
    a=0;
else
    x1=x1t;
    a=1;
end


















Code
def metroplis1d(x0, delta, w):
    xt = x0 + np.random.random() - 0.5
    wx0 = w(x0)
    wxt = w(xt)
    if (wxt > wx0):
        return xt
    else:
        prob = np.random.random()
        if (wxt / wx0 > prob):
            return xt
        else:
            return x0

x0 = 0.5
nn = 1000000
xres = np.zeros(nn)
i = 0
xres[0] = 0
wfunc = lambda x: 1.2 * (1 - 0.5 * x * x) if (x <= 1 and x >= 0) else 0.0
wfuncc = lambda x: 1.2 * (1 - 0.5 * x * x)
while i < nn-1:
    xres[i+1] = metroplis1d(xres[i], 0.5, wfunc)
    i += 1

cc, cx = np.histogram(xres, bins = 100)
plt.plot(cx[1:] - 0.5 * (cx[1] - cx[0]), cc / (cx[1] - cx[0]) / nn)
plt.plot(np.linspace(0, 1, 100), wfuncc(np.linspace(0, 1, 100)))
plt.show()

为了证明上述算法的确产生按照 \(w\) 分布的随机数,考虑有大量行走者在 \(X\) 空间是从不同的初始点出发独立运动。若 \(N_n(X )\)\(n\) 步后这些行走者在 \(X\) 点的密度,那么在下一步从 \(X\) 点走到 \(Y\) 点的行走者净数目为 \[ \begin{eqnarray} \Delta N(x) &=& N_n(X) P(X \rightarrow Y) - N_n(Y) P(Y \rightarrow X) \\ &=& N_n(Y) P(X \rightarrow Y) \left[ \frac{N_n(X)}{N_n(Y)} -\frac{P(Y \rightarrow X)}{P(X \rightarrow Y)}\right] \end{eqnarray} \]

\(\frac{N_n(X)}{N_n(Y)} = \frac{N_e(X)}{N_e(Y)} \equiv \frac{P(Y \rightarrow X)}{P(X \rightarrow Y)}\) 时,行走者的布局没有净变化,系统将会达到平衡,且当系统未达到平衡时 \(N(X)\) 的变化将驱使系统朝向平衡变动(即若 \(X\) 点上的行走者“太多”,或者若 \(N_n(X)/ N_n(Y)\) 大于其平衡值,\(\Delta N(X)\) 为正)。因此,在许多步后,行走者的布局将安排在其平衡分布 \(N_e\) 上。

下面证明,Metropopis算法的转移概率导致行走者的平衡分布 \(N_e(X ) \sim w (X )\) 。走从 \(X\)\(Y\) 的一步的概率为 \[ P(X \rightarrow Y) = T(X \rightarrow Y) A(X \rightarrow Y) \]

其中 \(T\) 是走出从 \(X\)\(Y\) 的试验步的概率,\(A\) 是接受这一步的概率。如果从 \(X\) 可以一步走到 \(Y\) (即 \(Y\) 是在以 \(X\) 为中心,\(d\) 为边长的立方体内),则 \[ T(X \rightarrow Y) = T(Y \rightarrow X) \]

因此,Metropopis随机行走者的平衡分布满足 \[ \frac{N_e(X)}{N_e(Y)} \equiv \frac{A(Y \rightarrow X)}{A(X \rightarrow Y)} \]

\(w(X) > w(Y)\),则 \(A(Y \rightarrow X)=1\)\[ A(X \rightarrow Y) = \frac{w(Y)}{w(X)} \]

反之,若 \(w(X) < w(Y)\),则 \[ A(Y \rightarrow X) = \frac{w(X)}{w(Y)} \]\(A(X \rightarrow Y )=1\) ,故在两种情况下,Metropolis随机行走者的平衡布局都满足 \[ \frac{N_e(X)}{N_e(Y)} \equiv \frac{w(X)}{w(Y)} \] 所以行走者的确是按照正确的概率密度分布。

👍 虽然我们为了使讨论更具体,而选点 \(X_t\) 在点 \(X_n\) 邻近,但总可以使用满足 \[ \frac{w(X)}{w(Y)} = \frac{T(Y \rightarrow X) A(Y \rightarrow X)}{T(X \rightarrow Y) A(X \rightarrow Y)} \] 的任何转移和接受法则。

  • 一种极限选取法: \(T (X \rightarrow Y )= w (Y )\)\(X\) 无关,及 \(A =1\),显然这样效率最高,但不切实际。因为如果我们知道怎样对 \(w\) 抽样以迈出试探步,就不需要用这个算法进行从头开始计算了。

👍 步长 \(d\) 如何选取?经验的做法是使得大约一半的试探步入选。

👍 Metropolis算法对一个分布抽样的隐患是:

  1. 构成随机行走的点 \(X_0, X_1, …\),由于产生的方法,彼此不是独立的,也就是说 \(X_{n+1}\) 总是在 \(X_n\) 附近。在用它们计算积分时必须注意。
  2. 随机行走从何出发,即选择何处为\(X_0\)。原则上,任何位置都合适,但实际中,合适的起始点是 \(w\) 值大的地方。同时,在开始抽样前,先取若干步进行热化,消除对出发点的依赖。

Example 13 利用Metropolis算法产生符合如下麦克斯韦-波尔兹曼分布的随机数 (Example 13)

Solution 13.

%filename: chapter5_example_12_metropolis.m
function metropolis_2
clear all
close all
clc

x1=1200;
delta=1000;
m=6.64*10^-27;
T=300;
k=1.38*10^-23;
w=@(v) sqrt(2/pi*(m/k/T)^3)*v.^2.*exp(-m*v.^2/(2*k*T)); %概率密度函数,与例题8相同
n=0;
N=50000;
for i=1:N
    [x1,a]=my_metropolis(x1,delta,w);
    if a==1
        n=n+1;
        xx(n)=x1; %将接受的x1值存储起来   
    end
end
n/N              %算法接受比例
%下面的程序判断metropolis算法生成的随机数分布是否符合分布函数
%========================================================
Num=201;                                                %区间大小
Max=max(xx)+10^-9                   %边界
Min=min(xx);
y=zeros(1,Num-1);
x=linspace(Min,Max,Num);
h=x(2)-x(1);
for i=1:length(xx)
    index=fix((xx(i)-Min)/h)+1; %判定xx(i)处于哪个子区间,fix为舍去小数取整运算
    y(index)=y(index)+1;          %该子区间的随机数数目+1
end
f=@(v) sqrt(2/pi*(m/k/T)^3)*v.^2.*exp(-m*v.^2/(2*k*T));            %精确值绘图
figure,fplot(f,[Min,Max],'r');
hold on;
S=interg(Min,Max,f);
y=y/length(xx)/h*S;  %生成的随机数的数目为n(length(xx)),走的总步数为N
plot(x(1:end-1),y,'*-');title('验证随机数的分布');
legend('精确分布','随机数的分布')
hold off
%--------比较数值平均速率,均方根速率与解析结果的相对误差---------
v_bar=sqrt(8*k*T/pi/m)
v_num=sum(xx)/length(xx)
error1=abs(v_bar-v_num)/v_bar
v2_bar=sqrt(3*k*T/m)
v2_num=sqrt(sum(xx.^2)/length(xx))
error2=abs(v2_bar-v2_num)/v2_bar

function S=interg(a,b,w) %w函数在[a,b]上的积分 
    n=10000;
    h=(b-a)/n;
    xx_h=a:h:b;
    ww=w(xx_h);
    %S=sum(w(xx_h).*(b-a)/n)-w(a)*(b-a)/2/n-w(b)*(b-a)/2/n; %梯形算法
    S=(4*sum(ww(2:2:n))+2*sum(ww(3:2:n-1))+ww(1)+ww(n+1))*h/3; %simpson积分公式
%=========================================================================

function [x1,a] = my_metropolis(x1,delta,w)
x1t=x1+delta*(2*rand-1);  
r=w(x1t)/w(x1);
if x1t<0
    a=0;
else
 if r<rand
    a=0;
 else
    x1=x1t;
    a=1;
 end
end






















7 Ising(伊辛)模型

用磁学的语言来说,Ising 模型是由一组自旋自由度组成,这些自旋自由度彼此相互作用并和一个外磁场相互作用。这些自旋自由度可以代表固体中原子的磁矩。

经典的二维 Ising 模型:

在一个二维方格子(\(N_x*N_y\)尺度)上,每个结点 \(i\) 上有一个自旋,可以取值 \(+1\)\(-1\)。(此处自旋为经典自由度)相邻自旋通过一个交换耦合能 \(J\) 相互作用,此外还有一个外磁场 \(B\)

系统的哈密顿量为

\[ H = -J \sum_{<\alpha\beta>} S_\alpha S_\beta - B \sum_{\alpha} S_\alpha \]

交换耦合能 J

零温下,当没有外磁场时

\(J > 0\):则所有自旋都朝同一方向,系统能量会最低,对应铁磁态(铁磁性)

\(J < 0\):则相邻两个自旋朝向相反,系统能量会最低,对应反铁磁态(反铁磁性)

\[ H = -J \sum_{<\alpha\beta>} S_\alpha S_\beta - B \sum_{\alpha} S_\alpha \]

8 Homework

  1. 分别利用Monte Carlo 方法中的均匀抽样和重要抽样计算下述定积分 \[\int_0^\infty \frac{3+4x}{1+x^2} e^{-x}\,dx\] 并将计算结果与Simpson法计算结果进行比较。

  2. 阅读并理解二维Ising模型数值求解程序,并求解下述问题 对\(8 \times 8\),\(16 \times 16\)\(32 \times 32\)的格子,在\(B=0\)和铁磁耦合强度取\(0.1\)\(0.6\)之间一系列数值的条件下,得到程序运行的结果;特别注意靠近预期的相变的区域,把你的结果同无穷大格子的确切结果进行比较,并且证明,格子的有限大小平缓了热力学可观察量中的奇异性。注意:磁畴的大小在接近临界耦合时变得非常大。 (教材第148页,习题8.9,同时做习题8.6)

8.1 Code for Ising model

function Exapmple_Ising_model
clear all
close all
clc

NX=16;NY=16; %格子尺寸
J=[0.0:0.05:0.3,0.305:0.005:0.605,0.61:0.05:0.92]; 
%耦合强度,并不等间隔的原因是为了加强0.3-0.6段的计算
B=0.0;  %外磁场强度

N_therm=50; %热化扫描步数
N_group=10; %数据分组数目
N_size=50;   %每组样本数
N_freq=5;   %抽样间隔步数

S=zeros(NX,NY);
[N,S]=initialize(NX,NY); %初始化格子的自旋
S1=S
for iii=1:length(J)
    JJ=J(iii); %依次取不同的J值,即不同的耦合强度
    R=zeros(5,2); %R数组用于存放接受概率值,有10个矩阵元,即PPT中的(2)式
    R=flip_probs(JJ,B);% %初始化R数组

    for i=1:N_therm %做热化扫描
        [S,accept]=thermal_sweep(NX,NY,S,R);
         %accept/N   %反转操作接受率
    end

    S2=S;
    Sum_M=0; Sum_M2=0; Sum_SigM=0; %初始化待求物理量M, E,Chi,CB
    Sum_E=0; Sum_E2=0; Sum_SigE=0;
    Sum_Chi=0; Sum_Chi2=0;
    Sum_CB=0; Sum_CB2=0;

    for igroup=1:N_group
        Group_M=0; Group_M2=0;
        Group_E=0; Group_E2=0;
        for sweep=1:N_size*N_freq  %完成此轮循环,就完成了一组数值的计算
            [S,accept]=thermal_sweep(NX,NY,S,R); %抽样前,先做热化扫描
            %accept/N   %反转操作接受率
            if mod(sweep,N_freq)==0
                [Magic,Energy]=Magic_Energy(B,JJ,NX,NY,S); %求出一种情况下的磁化强度和能量E
                %一次抽样,获得M和E
                Group_M=Group_M+Magic;
                Group_M2=Group_M2+Magic^2;
                Group_E=Group_E+Energy;
                Group_E2=Group_E2+Energy^2;
            end
        end
    %===================================
        Group_M=Group_M/N_size; %组内抽样的平均,即期望值,PPT中的(1)式,教材中的(8.21a)
        Group_M2=Group_M2/N_size;
        Group_E=Group_E/N_size;
        Group_E2=Group_E2/N_size;
        Chi=Group_M2-Group_M^2; %(8.21b),一个组里面求得的磁化率
        CB=Group_E2-Group_E^2;  %(8.21d)

        Sum_M=Sum_M+Group_M;
        Sum_E=Sum_E+Group_E;
        Sum_Chi=Sum_Chi+Chi;
        Sum_CB=Sum_CB+CB;
    end
    E=Sum_E/N_group;
    M=Sum_M/N_group;
    Chi=Sum_Chi/N_group;         %几个组求得的磁化率的平均
    CB=Sum_CB/N_group;

    CBB(iii)=CB; %用于存储不同J值对应的CB比热容
    EE(iii)=E/N; %N为总格子数
    MM(iii)=M/N;
    Chi_1(iii)=Chi/N;

end   %对应于for iii=1:length(J)
 figure;plot(1./J,CBB,'r*-') %注意:由于1/0为无穷大,程序没有画出该点
 title('比热随耦合强度的倒数的变化关系')
 xlabel('1/J'); ylabel('C_B');
 figure;plot(1./J,EE,'b')       %J值大,意味着温度低,M值大
 title('能量随耦合强度的倒数的变化关系')
 xlabel('1/J'); ylabel('能量E');
 figure;plot(1./J,MM,'r') %注意,磁化强度可以朝不同方向,因此可以为负
 title('磁化强度随耦合强度的倒数的变化关系')
 xlabel('1/J'); ylabel('磁化强度M');
 figure;plot(1./J,Chi_1,'k')
 title('磁化率随耦合强度的倒数的变化关系')
 xlabel('1/J'); ylabel('磁化率Chi');
 S


function [N,S]=initialize(NX,NY)   %初始化每个格子的自旋
N=NX*NY;
for i=1:NY
    for j=1:NX
        if rand < 0.5
            S(i,j)=1;
        else
            S(i,j)=-1;
        end
    end
end

function [Magic,Energy]=Magic_Energy(B,JJ,NX,NY,S)  %求出一种情况下的磁化强度和能量E
Magic=0;
Sum_ss=0;
for i=1:NY
    if i>1
        IM=i-1;
    else
        IM=NY;
    end
    %====================
    for j=1:NX
        if j>1
            JM=j-1;
        else
            JM=NX;
        end
        %==================
        Magic=Magic+S(i,j);
        Sum_ss=Sum_ss+S(i,j)*(S(IM,j)+S(i,JM)); 
        %注意上式求和实际上是进行(8.18)式第一项的运算,但S(i,j)只与左格点和上格点相乘求和
        %是因为如果上下左右四个格点都相乘求和,则存在重复计算的现象
    end
end
Energy=-JJ*Sum_ss-B*Magic; %(8.18)式
        
function R=flip_probs(JJ,B) %设定翻转判定矩阵
for i=1:5
    R(i,2)=exp(-2*(JJ*(2*i-6)+B));  %Sa为1
    %自旋反转判定因子,page 147页的(8.23)式,PPT中的(2)式,R(:,2)代表中心点自旋向上
    R(i,1)=exp(2*(JJ*(2*i-6)+B)); %分别对应于f=-4,-2, 0, 2, 4
end

function [S,accept]=thermal_sweep(NX,NY,S,R)   
%热化扫描函数,每次热化扫描后,所有的点都被遍历一次
accept=0;
for i=1:NY
    if i<NY         %采用周期性边界条件,此if循环给定(i,j)粒子下面的相邻格点(i+1,j)
        IP=i+1;
    else
        IP=1;
    end
    if i>1         %采用周期性边界条件,此if循环给定(i,j)粒子上面的相邻格点(i-1,j)
        IM=i-1;
    else
        IM=NY;
    end
    %==================
    for j=1:NX
        if j<NX %采用周期性边界条件,此if循环给定(i,j)粒子右边的相邻格点(i,j+1)
            JP=j+1;
        else
            JP=1;
        end
        if j>1 %采用周期性边界条件,此if循环给定(i,j)粒子左边的相邻格点(i,j-1)
            JM=j-1;
        else
            JM=NX;
        end
        %==================
        spin=S(i,j);     
        f=S(IP,j)+S(IM,j)+S(i,JP)+S(i,JM);
        if rand>R(3+f/2,(3+spin)/2) %如果反转概率比随机数小,则不反转
            %注意:f的取值只有-4,-2,0,2,4五种,spin只有-1,1两种,
            %因此用R(5,2)二维数组即可以判定是否反转
            % 这里的3+f/2, (3+spin)/2是将自旋转换到指标
        else
            S(i,j)=-spin;  %反转(i,j)点的自旋
            accept=accept+1;
        end
    end
end