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

简介:         上一篇博客介绍了通过向量化运算、预分配空间两种常用的方式提高代码的运行效率。实际上,matlab还有几种内置函数来避免循环语句的使用,分别为arrayfun、cellfun以及structfun函数。由于这几种函数需要用到匿名函数以及函数句柄的概念,很多人可能不太清楚,所以我才单独写一篇博客进行介绍。

       上一篇博客介绍了通过向量化运算、预分配空间两种常用的方式提高代码的运行效率。实际上,matlab还有几种内置函数来避免循环语句的使用,分别为arrayfun、cellfun以及structfun函数。由于这几种函数需要用到匿名函数以及函数句柄的概念,很多人可能不太清楚,所以我才单独写一篇博客进行介绍。

 

1.匿名函数与函数句柄

       匿名函数是 Matlab 中的一种函数类型,它可以方便地定义和使用轻量级的函数,通常被用于一些简单的计算和数据分析任务中。匿名函数的语法简单,能够大大提高代码的可读性和可维护性,也更加符合函数式编程的思想。

1.1匿名函数的定义

       在 Matlab 中,定义匿名函数的语法如下:

f = @(arguments) expression

image.gif

       其中,f 表示匿名函数名称(也称为函数句柄),arguments 为函数中的变量,expression 为匿名函数的表达式。参数列表可以是零个或多个参数,多个参数使用逗号分隔。表达式返回值将作为函数的输出。例如,可以定义一个匿名函数,功能是将输入的变量 x 先平方再加1:

f = @(x) x.^2 + 1;

image.gif

       这个匿名函数的表达式是 x.^2 + 1,意思是对输入参数 x 逐元素平方并加1,并输出一个新的数组。另外,和普通的子函数一样,也可以定义没有参数的匿名函数,例如:

g = @() disp('Hello, world!');

image.gif

       这个匿名函数不需要输入参数,它的表达式是 disp(‘Hello, world!’),也就是在命令行输出“Hello, world! ”字符串。

1.2匿名函数的使用

       定义完匿名函数之后,我们可以通过函数句柄来调用这个匿名函数。调用匿名函数的语法与调用普通函数一样,例如:

f = @(x) x.^2 + 1;
x=1:10;
y=f(x)

image.gif

       输出结果为:

image.gif

1.3函数句柄的定义

       刚才也提到了,定义匿名函数,匿名函数的名称也叫做函数句柄。其实,在 Matlab 中,通过在函数名前面加上 “@” 符号,就可以定义一个函数句柄。而函数句柄的使用方式类似于函数名的使用方式。如果有C或者C++的基础,可以发现所谓的函数句柄其实和指针的概念非常相似。函数句柄即可以指向普通的子函数,也可以指向匿名函数,例如:

f1=@fun1;           % 定义函数句柄f1指向函数fun1
f2=@(x) x.^2;       % 定义函数句柄f2指向匿名函数
a=[1 2 3];
b1=f1(a)
b2=f2(a)
function y = fun1(x)
    y = x.^2;
end


       输出结果完全一样

image.gif

1.4函数句柄的使用

       使用函数句柄,我们还能够实现动态函数调用。例如,在 Matlab 中,如果我们想要调用内置函数或者自定义的子函数,可以使用这些函数的函数句柄来实现动态调用,而不需要显式地调用这些函数。这么说可能比较难理解,举个简单的例子:

f1=@fun1;           % 定义函数句柄f1指向函数fun1
f2=@fun2;           % 定义函数句柄f2指向函数fun2
f3=@fun3;           % 定义函数句柄f3指向函数fun3
f4=@fun4;           % 定义函数句柄f4指向函数fun4
a=[1 2 3];
b1=fun(f1,a)+fun(f2,a)+fun(f3,a)+fun(f4,a)
b2=f1(a)+f2(a)+f3(a)+f4(a)
function y = fun1(x)
    y = x;
end
function y = fun2(x)
    y = 2*x.^2;
end
function y = fun3(x)
    y = 3*x.^3;
end
function y = fun4(x)
    y = 4*x.^4;
end
function y = fun(f,x)
    y = f(x);
end

image.gif

       这个例子里,我们首先定义了子函数fun1-fun4,用于计算k*x^k。还定义了子函数fun,这个函数的作用是传入一个函数句柄f和一个变量x,输出使用传入的函数f对变量x处理后的结果y。b1和b2的结果是完全一样的:

image.gif

       通过这个例子,大家可能体会到了函数句柄进行动态函数调用的用法,但是并没有感觉使用函数句柄进行动态函数调用有哪些优势,我将用另外一个例子进行说明。

    假设我们需要一个数据处理的程序,用于对输入的数据进行多个不同的数据转换操作,例如对数据进行过滤、自动缩放、归一化等操作。为了方便,我们希望将这些不同的数据转换操作都封装成具体的函数,并使用一个统一的函数来调用这些函数。使用函数句柄,我们能够实现这一点。例如:

clc
clear
x = -5:0.1:5;                           % 定义原始输入数据
y1 = processData(x, 'filterData');      % 过滤数据
y2 = processData(x, 'scaleData');       % 缩放数据
y3 = processData(x, 'normalizeData');   % 数据归一化
% 绘制计算结果
subplot(2,2,1); plot(x,x);
subplot(2,2,2); plot(x,y1);
subplot(2,2,3); plot(x,y2);
subplot(2,2,4); plot(x,y3);
% 定义具体的数据转换函数
function y1 = filterData(x)
    y1(x>0) = x(x>0);
    y1(x<=0) = 0;
end
function y2 = scaleData(x)
    y2 = x./max(abs(x));
end
function y3 = normalizeData(x)
    y3 = (x-mean(x))./std(x);
end
% 定义数据转换调用函数
function y = processData(x, funcName)
    func = str2func(funcName);  % 将函数名转换为函数句柄
    y = func(x);  % 根据函数句柄调用具体的数据转换函数
end

image.gif

       运行结果为:

image.gif

        如果经常使用Matlab应该会发现,Matlab的内置函数也有很多这样的函数,通过类似下面语句完成函数处理,可以大大提升效率,这里我不再过多介绍。

函数名(变量,'函数关键字')

image.gif

2.通过内置函数arrayfun、cellfun、structfun避免使用循环

       在Matlab中,arrayfun、cellfun、structfun是三个用于处理数组、单元数组和结构体的函数,它们可以帮助用户快速进行数据处理。

2.1.arrayfun函数

       arrayfun函数是用于对数组进行批量处理的函数,它的基本语法如下:

B = arrayfun(fun, A)

image.gif

       其中,第一个参数 fun 是一个函数句柄,表示对 A 中每个元素进行的操作,返回的结果保存在一个新的数组 B 中。arrayfun函数将逐个处理 A 中的每个元素,并将结果传递给 fun 函数进行处理。

       举个例子,假设我们要对一个向量 x 中的每个元素进行平方处理,可以使用 arrayfun 来实现:

x = [1 2 3 4 5];
y = arrayfun(@(a) a^2, x);
disp(y);

image.gif

       在这个例子中,我们定义了一个匿名函数 @(a) a^2,并将它作为 arrayfun 的第一个参数传入。arrayfun 将逐个处理 x 中的每个元素,并将结果传递给匿名函数进行平方处理。最终,arrayfun 将这些结果存储在另一个向量 y 中,并输出。

2.2 cellfun函数

       cellfun函数是一个用于对单元数组进行批量处理的函数,它的基本语法如下:

B = cellfun(fun, C)

image.gif

       其中,第一个参数 fun 是一个函数句柄,表示对 C 中每个元素进行的操作,返回的结果保存在一个新的单元数组 B 中。cellfun函数将逐个处理 C 中的每个元素,并将结果传递给 fun 函数进行处理。

       举个例子,假设我们要将一个单元数组 C 中每个元素的大小写转换,可以使用 cellfun 来实现:

C = {'Hello', 'World'};
D = cellfun(@(a) upper(a), C, 'UniformOutput', false);
disp(D);

image.gif

       在这个例子中,我们定义了一个匿名函数 @(a) upper(a),表示将一个字符串转换成大写。我们将匿名函数作为 cellfun 的第一个参数进行调用,cellfun 将逐个处理 C 中的每个元素,并将结果传递给匿名函数进行大小写转换。最终,cellfun 将这些结果存储在另一个单元数组 D 中,并输出。需要注意的是,在使用 cellfun 时,需要指定参数 'UniformOutput' 为 false,否则函数的返回结果不会被封装为单元数组,因为不同大小的返回值无法合并为单个单元数组。

2.3structfun函数

       structfun函数是用于对结构体进行批量处理的函数,它的基本语法如下:

B = structfun(fun, S)

image.gif

       其中,第一个参数 fun 是一个函数句柄,表示对 S 中每个字段进行的操作,返回的结果保存在一个新的结构体 B 中。structfun将逐个处理 S 中的每个字段,并将结果传递给 fun 函数进行处理。

       举个例子,假设我们要将一个结构体 S 中的数值型字段乘以2,可以使用 structfun 来实现:

S.x = 1;
S.y = 2.5;
S.z = 3.7;
T = structfun(@(a) 2*a, S, 'UniformOutput', false);
disp(T);

image.gif

       在这个例子中,我们定义了一个匿名函数 @(a) 2*a,表示将一个字段的值乘以2。我们将匿名函数作为 structfun 的第一个参数进行调用,structfun 将逐个处理 S 中的每个字段,并将结果传递给匿名函数进行处理。

2.4 避免循环语句的使用

       使用 cellfun 可以以简洁的方式避免使用循环语句,并提高代码的可读性和可维护性。下面我们通过一个实例来说明。

例1:单元数组中字符串拼接

       假设我们有一个单元数组 C,其中包含了字符串,我们想要将其中的字符串进行拼接,并返回一个新的字符串。我们先使用循环语句来实现,代码如下:

% 定义数组
A = {{'H','e','l','l','o',' '}, {'W','o','r','l','d'}, {'——','M','a','t','l','a','b'}};
B = cell(1,10000);
for k=1:10000
    B{k}=A;
end
% 循环计算
result = '';
tic
for k = 1:numel(B)
    for kk=1:numel(B{k})
        for kkk=1:numel(B{k}{kk})
            result = [result, B{k}{kk}{kkk}];
        end
    end
end
toc

image.gif

       运行时间为:

image.gif

       这段代码使用了循环语句来计算单元数组中字符串的拼接,使用一个 for 循环来遍历单元数组中的元素,然后每次循环将当前元素添加到最终结果中,如果使用 cellfun 函数来实现相同的功能,代码如下:

可以使用如下`cellfun`函数来避免循环:```

 

A = {{'H','e','l','l','o',' '}, {'W','o','r','l','d'}, {'——','M','a','t','l','a','b'}};
B = cell(1,10000);
for k=1:10000
    B{k}=A;
end
tic
C = cellfun(@(x) cellfun(@(y) [y{:}], x, 'UniformOutput', false), B, 'UniformOutput', false);
D = [C{:}];
result = [D{:}];
toc

image.gif

       上述代码中,使用了两个嵌套的`cellfun`函数,将多维cell数组中的字符元素连接成一个字符串。其中第一个`cellfun`函数会对`B`中每个元素应用第二个`cellfun`函数,将其分别处理成一个字符串序列并封装到一个cell中。最后使用`{:}`操作符将所有结果取出来,连接成一个字符串。这样,就可以避免使用for循环,提高了代码运行的效率。

相关文章
|
4月前
|
编解码 算法 异构计算
基于FPGA的NC图像质量评估verilog实现,包含testbench和MATLAB辅助验证程序
在Vivado 2019.2和Matlab 2022a中测试的图像质量评估算法展示了效果。该算法基于NC指标,衡量图像与原始图像的相似度,关注分辨率、色彩深度和失真。提供的Verilog代码段用于读取并比较两个BMP文件,计算NC值。
|
10天前
|
监控 算法 安全
基于颜色模型和边缘检测的火焰识别FPGA实现,包含testbench和matlab验证程序
本项目展示了基于FPGA的火焰识别算法,可在多种应用场景中实时检测火焰。通过颜色模型与边缘检测技术,结合HSV和YCbCr颜色空间,高效提取火焰特征。使用Vivado 2019.2和Matlab 2022a实现算法,并提供仿真结果与测试样本。FPGA平台充分发挥并行处理优势,实现低延迟高吞吐量的火焰检测。项目包含完整代码及操作视频说明。
|
2月前
|
数据采集 并行计算 算法
LabVIEW与Matlab联合编程的途径及比较
LabVIEW与Matlab联合编程的途径及比较
37 0
LabVIEW与Matlab联合编程的途径及比较
|
3月前
|
存储 算法 计算机视觉
m基于FPGA的FIR低通滤波器实现和FPGA频谱分析,包含testbench和滤波器系数MATLAB计算程序
在Vivado 2019.2平台上开发的系统,展示了数字低通滤波器和频谱分析的FPGA实现。仿真结果显示滤波效果良好,与MATLAB仿真结果一致。设计基于FPGA的FIR滤波器,利用并行处理和流水线技术提高效率。频谱分析通过离散傅里叶变换实现。提供了Verilog核心程序以示例模块工作原理。
35 4
|
3月前
|
机器学习/深度学习 数据可视化 算法
探索MATLAB世界:掌握基础知识与实用技能(1. MATLAB环境与基本操作 2. 数据类型与变量 3. 条件与循环,1. 数据分析与统计 2. 图像处理与计算机视觉 3. 信号处理与控制系统)
探索MATLAB世界:掌握基础知识与实用技能(1. MATLAB环境与基本操作 2. 数据类型与变量 3. 条件与循环,1. 数据分析与统计 2. 图像处理与计算机视觉 3. 信号处理与控制系统)
30 0
|
4月前
|
机器学习/深度学习 算法
m基于GA-GRU遗传优化门控循环单元网络的电力负荷数据预测算法matlab仿真
在MATLAB 2022a中,一个基于遗传算法优化的GRU网络展示显著优化效果。优化前后的电力负荷预测图表显示了改进的预测准确性和效率。GRU,作为RNN的一种形式,解决了长期依赖问题,而遗传算法用于优化其超参数,如学习率和隐藏层单元数。核心MATLAB程序执行超过30分钟,通过迭代和适应度评估寻找最佳超参数,最终构建优化的GRU模型进行负荷预测,结果显示预测误差和模型性能的提升。
171 4
|
3月前
|
机器学习/深度学习 算法
m基于PSO-GRU粒子群优化长门控循环单元网络的电力负荷数据预测算法matlab仿真
摘要: 在MATLAB 2022a中,对比了电力负荷预测算法优化前后的效果。优化前为&quot;Ttttttt111222&quot;,优化后为&quot;Tttttttt333444&quot;,明显改进体现为&quot;Tttttttttt5555&quot;。该算法结合了粒子群优化(PSO)和长门控循环单元(GRU)网络,利用PSO优化GRU的超参数,提升预测准确性和稳定性。PSO模仿鸟群行为寻找最优解,而GRU通过更新门和重置门处理长期依赖问题。核心MATLAB程序展示了训练和预测过程,包括使用&#39;adam&#39;优化器和超参数调整,最终评估并保存预测结果。
39 0
|
4月前
|
数据安全/隐私保护
matlab程序,地震波压缩、地震波缩尺、地震波压缩时间,调整时长、时间间隔
地震波格式转换、时程转换、峰值调整、规范反应谱、计算反应谱、计算持时、生成人工波、时频域转换、数据滤波、基线校正、Arias截波、傅里叶变换、耐震时程曲线、脉冲波合成与提取、三联反应谱、地震动参数、延性反应谱、地震波缩尺、功率谱密度
|
4月前
|
数据安全/隐私保护
地震波截波、Arias强度截波、截波处理、批量截波,matlab程序
地震波格式转换、时程转换、峰值调整、规范反应谱、计算反应谱、计算持时、生成人工波、时频域转换、数据滤波、基线校正、Arias截波、傅里叶变换、耐震时程曲线、脉冲波合成与提取、三联反应谱、地震动参数、延性反应谱、地震波缩尺、功率谱密度
|
4月前
|
算法 调度
基于多目标粒子群算法冷热电联供综合能源系统运行优化(matlab代码)
基于多目标粒子群算法冷热电联供综合能源系统运行优化(matlab代码)