相见恨晚的Matlab编程小技巧(3)-程序运行太慢了咋解决——合理使用循环语句(1)

文章介绍了在Matlab中优化程序效率的两种方法:使用向量化运算避免循环,以及在循环前预分配空间。通过示例展示了如何运用tic和toc记录运行时间,以及如何通过向量化运算和预分配内存来显著减少程序运行时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        相信大家在使用matlab时候经常会收到程序运行太慢的困扰,当程序比较复杂时,常常需要很长时间等待。我有个朋友就是这样,每次debug都要很长时间,等着的时候就想耍会手机,结果耍完一抬头发现程序运行结束了,但时间已经过去半天了。

        一般来说,程序运行太慢都是因为循环的存在,使用双层甚至多层循环会使得程序运行效率极低。这篇博客将重点介绍如何在Matlab中避免使用循环语句并提高程序效率。我们将深入讨论向量化运算、预分配空间和相关函数(cellfun、arrayfun和structfun等)的用法,同时配有详细的示例代码和解释,帮助大家更好地掌握这些技术和优化方法。

1.记录程序运行时间

        为了比较使用循环语句与否对程序运行时间的影响,我们需要用到matlab中的tic,toc语句,tic函数表示开始计时。执行tic语句后,程序就会记录下当前时间。接下来的代码运行时,而程序会在toc函数调用时输出自tic函数到当前时刻所经过的时间。例如:

tic    % 开始计时
% 模块代码
x = 1:10000000;
y = sin(x);
disp(y(1:10));
toc    % 打印出程序自tic调用以来经过的时间

        运行结果:

         如果你不想在命令行输出运行时间,也可以将程序运行时间存在变量中,避免在命令行输出,例如:

tic    % 开始计时
% 模块代码
x = 1:10^8;
y = sin(x);
time0=toc;    % 将程序自tic调用以来经过的时间存在变量time0中

2.向量化运算

        为了提高程序性能,我们可以使用向量化运算来避免循环,同时实现对向量中所有元素的操作。向量化运算是一种在Matlab中广泛使用的高效操作方式,它可以对整个向量或矩阵进行运算,而不需要使用循环。具体来说,向量化运算使用内部优化的编译器,通过单个指令完成多个元素的处理,因此能够大大提高程序的效率。在Matlab中,常用的向量化运算符包括 .*, ./, .^, .', .*,等。具体的用法如表1所示:

表1 向量化运算符号的用法

运算符用法说明
.*A .* BA 数组和 B 数组中对应元素逐个相乘
./A ./ BA 数组和 B 数组中对应元素逐个相除
.^A .^ BA 数组和 B 数组中对应元素逐个做幂运算
.*A .* BB 对应 A 的每一列进行元素相乘运算

        首先举一个简单的例子来介绍如何使用向量化运算来代替循环语句。假设有两个矩阵 x 和 y,都包含500×500×500个元素,现在你想将它们每个元素相乘,然后将乘积相加。如果如果使用循环语句,可以这样写代码:

tic
x=rand(500,500,500);
y=rand(500,500,500);
c = 0;
for k = 1:500
    for kk = 1:500
        for kkk = 1:500
            c = c + x(k,kk,kkk) * y(k,kk,kkk);
        end
    end
end
toc

        运行时间需要11.25秒。

        如果你使用向量化操作,代码会更加简单,运行时间也会更短:

tic
x=rand(500,500,500);
y=rand(500,500,500);
c = sum(x(:) .* y(:));
toc

         运行只要2.46秒,快了不止一点点,在这个例子中,x(:)和y(:)表示将矩阵x用一维向量的形式表示。x(:) .* y(:)表示将一维向量化之后的 x 和 y 中对应元素相乘,并得到一个新的向量化,然后使用 sum 函数对这个新向量求和,从而得到我们想要的结果。显然,使用向量操作代替循环,代码更简洁、更易读、更易扩展。当处理大型数据集时,向量操作也可以大大提高程序运行速度。

3.预分配空间

        大家在使用Matlab编程时应该都看到过类似这样的警告:

        这是在Matlab中,如果你想把一个新的元素添加到数组中,并且数组的大小没有指定或无法确定时,Matlab会重新分配整个数组并复制现有元素,这个过程会导致程序效率下降。当我们不得不使用循环语句时,我们也需要在使用变量前预分配空间。如果我们想在循环中构建一个数组时,你可以在循环之前预先分配数组空间,然后在循环中直接修改数组元素。这样做可以大大减少程序的运行时间和内存消耗。例如:

tic
x=rand(2000,2000);
y=rand(2000,2000);
for k = 1:2000
    for kk = 1:2000
        c(k,kk) = x(k,kk) * y(k,kk);
    end
end
toc

         上面的代码没有预分配内存,所需运行时间为4.45秒。如果在for循环之前初始化变量c,代码这样写:

tic
x=rand(2000,2000);
y=rand(2000,2000);
c=zeros(2000,2000);
for k = 1:2000
    for kk = 1:2000
        c(k,kk) = x(k,kk) * y(k,kk);
    end
end
toc

         运行时间变成了0.2秒。一个不起眼的变量初始化,可以让代码运行速率快这么多。所以,之后编程的过程中,只要遇到循环语句,一定要确保循环内部用到的变量都是预分配内存的。

        这篇博客就先介绍这两种比较常用的方法,还有另一种使用相关函数的方法,我们将在下一篇博客单独进行介绍。

### 提高Matlab程序运行速度的方法 #### 使用向量化操作替代显式循环 向量化操作能够利用底层优化过的库函数来代替逐元素的循环运算,这通常会带来显著的速度提升。例如,在构建累积求和或累积乘积时,应优先考虑使用`cumsum()`或`cumprod()`这样的内置函数而不是手动实现累加器逻辑[^1]。 ```matlab % 非最优方式:使用for循环进行累计相加 V_B = zeros(1, n); for i = 2:n V_B(i) = V_B(i-1); end % 更优的方式:应用cumsum()函数简化表达并加快计算过程 V_B_optimized = cumsum([100; ScaleFactor.*V_B]); ``` #### 利用 Mex 文件增强性能 对于那些特别耗费时间的部分——尤其是涉及大量迭代或其他复杂控制结构的地方——可以将其重写为C/C++代码并通过MATLAB提供的Mex接口编译成动态链接库(DLL),这样做的好处是可以获得接近原生二进制级别的执行效率,理论上能比纯M脚本快上几十倍不等[^3]。 ```c++ // C++ MEX function example to compute element-wise multiplication of two vectors. #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { double *a = mxGetPr(prhs[0]); // Input vector A double *b = mxGetPr(prhs[1]); // Input vector B size_t m = mxGetNumberOfElements(prhs[0]); plhs[0] = mxCreateDoubleMatrix(m, 1, mxREAL); double *result = mxGetPr(plhs[0]); for (size_t i = 0; i < m; ++i){ result[i] = a[i]*b[i]; } } ``` #### 减少不必要的内存分配与复制 频繁创建新的数组对象或将数据从一处拷贝到另一处都会消耗额外的时间资源;尽可能地预先定义好所需的空间大小,并重复利用已有的变量空间有助于减少这类开销。比如当初始化固定长度的一维或多维表单时,应当一次性指定其完整的尺寸而非逐步扩展它[^2]。 ```matlab L = 1e4; A_unoptimized = filter([1], [1 -2], ones(1, L+1)); % 改善后的做法是提前声明矩阵维度 B_preallocated = nan(L+1, 1); % 或者zeros(),具体取决于实际需求 B_preallocated(:) = filter([1], [1 -2], ones(size(B_preallocated))); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

配电网和matlab

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值