问题

今天被问了一个问题:

μ = ∫ ∫ ∫ ∫ ∫ ∫ f ( x 1 , x 2 , x 3 , x 4 , x 5 , x 6 ) d x 1 d x 2 d x 3 d x 4 d x 5 d x 6 σ 2 = ∫ ∫ ∫ ∫ ∫ ∫ [ f ( x 1 , x 2 , x 3 , x 4 , x 5 , x 6 ) − μ ] 2 d x 1 d x 2 d x 3 d x 4 d x 5 d x 6 \begin{array}{l} \mu = \int\int\int\int\int\int f(x_1, x_2, x_3, x_4, x_5,x_6)dx_1dx_2dx_3dx_4dx_5dx_6 \\ \sigma^2 = \int\int\int\int\int\int \left[ f(x_1, x_2, x_3, x_4, x_5,x_6)-\mu\right]^2dx_1dx_2dx_3dx_4dx_5dx_6 \end{array} μ=∫∫∫∫∫∫f(x1,x2,x3,x4,x5,x6)dx1dx2dx3dx4dx5dx6σ2=∫∫∫∫∫∫[f(x1,x2,x3,x4,x5,x6)μ]2dx1dx2dx3dx4dx5dx6
约束:这个函数f的返回值是一个向量。

要求:用Matlab解决。

方案一

function ret = integral6(fun, lb, ub)
% int x2
fx1 = @(x2, x3, x4, x5, x6)integral(@(x1)fun(x1, x2, x3, x4, x5, x6), lb(1), ub(1), 'ArrayValued', 1);

% int x2
dfx2 = @(x2, x3, x4, x5, x6)cell2mat(arrayfun(@(xx2)fx1(xx2, x3, x4, x5, x6), x2, 'UniformOutput', 0));
fx2 = @(x3, x4, x5, x6)integral(@(x)dfx2(x, x3, x4, x5, x6), lb(2), ub(2), 'ArrayValued', 1);

% int x3
dfx3 = @(x3, x4, x5, x6)cell2mat(arrayfun(@(xx3)fx2(xx3, x4, x5, x6), x3, 'UniformOutput', 0));
fx3 = @(x4, x5, x6)integral(@(x)dfx3(x, x4, x5, x6), lb(3), ub(3), 'ArrayValued', 1);

% int x4
dfx4 = @(x4, x5, x6)cell2mat(arrayfun(@(xx4)fx3(xx4, x5, x6), x4, 'UniformOutput', 0));
fx4 = @(x5, x6)integral(@(x)dfx4(x, x5, x6), lb(4), ub(4), 'ArrayValued', 1);

% int x5
dfx5 = @(x5, x6)cell2mat(arrayfun(@(xx5)fx4(xx5, x6), x5, 'UniformOutput', 0));
fx5 = @(x6)integral(@(x)dfx5(x, x6), lb(5), ub(5), 'ArrayValued', 1);
% int x6
dfx6 = @(x6)cell2mat(arrayfun(@(xx6)fx5(xx6), x6, 'UniformOutput', 0));
fx6 = integral(@(x)dfx6(x), lb(6), ub(6), 'ArrayValued', 1);

ret = fx6;
end

看着很玄乎……其实都没啥,一层一层剥洋葱。这个解法唯一的意义在于学习几个函数的用法:arrayfun, cell2mat, integral,以及@定义函数的语法。因为计算时间幂级的,根本没用。

能够在有限时间得到结果的是大名鼎鼎的Monte Carlo法。

Monte Carlo法

关于这个方法的数学,这里不赘述,需要知道的,搜索了sci-hub;那些不知道怎么搜索的,大概率也不需要知道。

function ret = integral6mc(fun, lb, ub, N)
% fun是被积分的函数
% lb和ub是积分变量的范围,每个都是六维数组
% N MC采样的数目,一般取个几千、几万试一下差不多就行
V = prod(abs(ub-lb)); % 计算超立方体的体积,向量化计算每一个维度的长度(绝对值),求所有维度的乘积
n = length(lb); % 维数
sample = (ub-lb) .* rand(N, n) + repmat(lb,N,1); %产生一个Nxn的随机数组,然后转换到lb~ub的范围,repmat函数是复制矩阵,把行向量复制程Nxn

sample_arguments = num2cell(sample, 1);% 把上面的Nxn矩阵换成按列排列的cell(n个元素)
results = cell2mat(arrayfun(fun, sample_arguments{:}, 'UniformOutput', 0));%调用被积函数,被积函数的参数有n个,把cell展开({:}操作),这里arrayfun得到的是cell,再合并成mat,就是N个结果的向量
ret = sum(results) * V / N; % 这是MC的核心算法,乘以体积除以样本数

这个方法就快多了……

而且,这个函数可以自动适应积分重数,被积函数可以是向量也可以是标量,只要fun的参数个数,lbub的大小能够适配就行。

是男人就积分100重啊!!

蒙特卡洛求积分之第二篇

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐