Matlab arrayfun 与 bsxfun——提高编程效率的利器!
Matlab arrayfun 与 bsxfun——提高编程效率的利器!
在MATLAB编程中,arrayfun和bsxfun是两个非常重要的函数,它们能够帮助用户更高效地处理数组操作。本文将通过具体的代码示例,详细介绍这两个函数的功能和使用场景。
Matlab arrayfun
概述
arrayfun是Matlab中的一个强大函数,它允许用户对数组中的每个元素应用一个指定的函数,并返回一个新的数组,该数组包含了函数对每个元素应用后的结果。这使得对数组进行逐元素操作变得非常灵活和方便,无需编写循环语句。
测试目的
本测试文档旨在展示arrayfun函数的多种巧妙用法,包括基本用法、匿名函数的应用、多维数组的处理以及与其他函数的结合使用,以全面理解其功能和效率。
- 先看下面两个基本操作
A = 1:5;
B = arrayfun(@(x) x^2, A);
disp(B);
A = -5:5;
B = arrayfun(@(x) x > 0, A);
disp(B);
这两个操作中,arrayfun提供了便利的逐元素操作方式,但在处理大型数组时,直接利用Matlab的内置数组向量化操作(如 +, -, .*, ./ 等)通常会有更好的性能。
- 再看下面这个操作:
[J,I]=meshgrid(1:10);
al=arrayfun(@(ii,jj) integral2(@(u,v)sin(u).*sqrt(v),0,ii,0,jj),I,J);
这段代码在MATLAB环境中执行了一个二维数值积分的计算,具体地,它计算了函数
f ( u , v ) = s i n ( u ) ⋅ v f(u,v)=sin(u)⋅ \sqrt{v}f(u,v)=sin(u)⋅v
在由点 (0, 0) 到点 (ii, jj) 形成的矩形区域上的积分,其中 (ii, jj) 遍历了一个由 meshgrid 函数生成的 10x10 网格的坐标点。让我们逐步解释这段代码的各个部分:
al=arrayfun(@(ii,jj)integral2(@(u,v)sin(u).*sqrt(v),0,ii,0,jj),I,J);
这行代码是代码的核心,它使用了arrayfun函数来对I和J数组中的每个(ii,jj)对执行一个函数。这个函数是一个匿名函数,它本身调用了integral2函数来执行二维数值积分。
integral2(@(u,v)sin(u).*sqrt(v),0,ii,0,jj)
调用integral2来计算函数
f(u, v) = \sin(u) \cdot \sqrt{v}
在矩形区域 [0, ii] x [0, jj] 上的积分。这里,
@(u,v)sin(u).*sqrt(v)
定义了被积分的函数,而0, ii, 0, jj指定了积分的边界。
arrayfun函数将这个integral2调用应用到I和J数组的每一个(ii,jj)对上,并将结果存储在数组al中。因此,al是一个10x10的数组,其中al(i,j)存储了函数f(u, v)在矩形区域[0, I(i,j)] x [0, J(i,j)]上的积分值。
灵活性:这种方法允许用户轻松地对不同区域的函数进行积分,而无需手动编写多个积分调用。通过改变meshgrid函数的参数,可以轻松地调整积分的区域大小和形状。
显然这段代码是向量化编程难以执行的,而靠
arrayfun
函数两行搞定.
bsxfun(binary singleton expansion function)
概述
bsxfun是MATLAB中的一个函数,它允许对两个数组进行逐元素操作,同时自动扩展(或广播)较小的数组以匹配较大数组的维度。这使得在不需要显式循环的情况下执行复杂的数组操作成为可能,提高了代码的效率和简洁性。
测试案例
bsxfun简单的函数操作见帮助文档,这里我们给一个高级的测试案例,展示了bsxfun的妙用:
- 对两个二维数组每一行求差集
MATLAB目前只能对一维数组求差集,高维的话用for loop效率偏低。对于下面这种两个数组每一列只有一个不同元素的矩阵,对每一列求差集,完全可以不用for loop:
%例如下面对 A,B 每一行求差集
% A = [1,2,3,4,5; 8,4,7,9,6];
% B = [2,3; 4,9];
% mask = all(bsxfun(@ne,A,permute(B,[1 3 2])),3);
% At = A.'; %//'
% out = reshape(At(mask.'),[],size(A,1)).';
% ---------------------------------------------------
%下面算例对两个矩阵每一列求差集
B=[4 4 7 7 7 7 6 6 6 6 6 6
3 9 9 5 9 8 2 9 4 4 9 8
9 2 3 9 4 4 9 5 9 8 7 7
1 1 1 1 3 9 1 1 2 9 5 9]';
A=[
7 6 4 6 4 6 4 7 4 7 7 7
9 2 3 9 3 4 9 5 9 8 5 8
3 9 9 5 9 8 2 9 2 4 9 4
1 1 1 1 1 9 1 1 1 9 1 9]';
mask = all(bsxfun(@ne,A,permute(B,[1 3 2])),3);
At = A.'; %//'
out = reshape(At(mask.'),[],size(A,1))
- 再看下面这个:对比bsxfun与repmat运行效率:
n = 300;
k = 1; % Change to 100 for the second graph
a = ones(10,1);
rr = zeros(n,1);
bb = zeros(n,1);
ntt = 100;
tt = zeros(ntt,1);
for i = 1:n
r = rand(1, i * k);
% Timing bsxfun
for it = 1:ntt
tic;
x = bsxfun(@plus, a, r);
tt(it) = toc;
end
bb(i) = median(tt);
% Timing repmat
for it = 1:ntt
tic;
y = repmat(a, 1, i * k) + repmat(r, 10, 1);
tt(it) = toc;
end
rr(i) = median(tt);
end
figure;
plot(1:n, bb, 'b', 'DisplayName', 'bsxfun');
hold on;
plot(1:n, rr, 'r', 'DisplayName', 'repmat');
legend('bsxfun','repmat')
运行时间对比结果:
可见对于大矩阵操作bsxfun效率更高!
So, 当你的矩阵规模比较大时,想想能否用bsxfun代替repmat吧!