当前位置: 首页 > news >正文

002_avoid_for_loop_in_Matlab避免使用for循环

避免使用for循环

在程序设计思想中,循环是一个很有力的工具。在循环中,计算机很轻松地重复执行相同的操作。循环是汇编之上的编程中最重要的概念之一。Matlab的循环有两个语言构造,一个是for循环,另一个是while循环。在Matlab中,for循环是最常用的循环结构。

然而,for循环在Matlab中是非常慢的。这是因为Matlab是一种解释性语言,而不是一种编译性语言。比如,立志于在科学计算领域开辟一“新”道路的Julia语言,其宣传的最重要特点之一就是for循环的速度非常快。

作为经常需要处理大量数据的科学计算工具,Matlab的for循环速度慢是一个很大的问题。因此,我们应该尽量避免使用for循环。

这里有一个说法可以特别地在这里提到,就是所谓的过早优化。过早优化是一种不好的编程习惯。在程序设计的初期,我们应该首先考虑代码的可读性和可维护性。然后,我们应该考虑代码的性能。然而在Matlab中,避免使用for循环并不意味着我们应该在一开始就考虑性能问题。通过采用下面我们提到的方式,编写的Matlab代码的可读性和可维护性也是非常强的。

1. For循环的基本知识

1.1 语法

在Matlab中,for循环的构造如下:

for i = 1:n% do something
end

这个构造同样也可以写成单行的形式:

for i = 1:n, statements, end

如果我们要对一个向量v进行操作,可以使用如下的for循环:

for i = 1:length(v)% do something
end

上面,1:n1:length(v)是for循环的索引。实际上,在Matlab中,:操作符会直接产生一个数组(Matlab最基本的数据结构),并且这个操作符还能够设定步长。比如,1:2:10会产生一个从1到10的数组,步长为2。

for语言构造采用 = 来迭代一个数组。所以,前一个例子里面,可以直接写成:

for vi = v% do something
end

1.2 break和continue

在For循环中,要避免改变索引变量的值来试图改变循环过程,for循环会直接覆盖对这个值的改变。这一点跟很多别的程序设计语言并不相同。

for i = 1:10i = 5; % 这个操作是无效的disp(i);
end

要中途退出一个循环,可以使用关键词break。要跳过当前循环的剩余部分,可以使用关键词continue。

运行一下上面的代码,会打印10个5。

1.3 循环二维数组

另外,我们都知道Matalb的2维数组(矩阵)是列先的,所以在for循环中,对矩阵的操作,应该是按列进行的。比如:

A = [1 2 3; 4 5 6; 7 8 9];
for Ai = Adisp(Ai);
end

上面的代码会打印出矩阵A的每一列。

在这里插入图片描述

Q: 如果上面的A是一个列向量,会怎么样?循环几次?

A: 循环一次。因为列向量是一个列,所以循环一次。

Q: 如果A是一个行向量呢?

A: 行向量有多少个元素,就循环多少次。

这里还挺有意思的,Matlab的for循环是按列进行的,所以对于一个矩阵,我们可以使用for循环来对每一列进行操作。这是一个很有用的特性。而一维数组,列向量和行向量,是一个维数为1的矩阵,所以对于这些数据,for循环的行为是具有一致性的。

2. 避免使用for循环

2.1 算法矩阵化

Matlab是一种矩阵化的语言。这意味着,Matlab的很多操作都是对整个矩阵进行的。与C、Java等语言不同,Matlab的很多操作都是对整个矩阵进行的,Matlab提供了大量针对矩阵进行计算的操作符和操作函数。这些操作符和函数都是高度优化过的,所以在Matlab中,我们应该尽量使用矩阵化的操作。这一点应该是Matlab的使用者在设计算法之初就应该考虑的。

把算法的步骤推导为矩阵的形式,可以为Matlab来实现提供很大的帮助。比如,我们要计算一个向量的平方和,可以使用for循环:

v = [1 2 3 4 5];
sumv = 0;
for vi = vsumv = sumv + vi^2;
end

这里需要注意,如果v是列向量,那么只会循环一次,得到奇怪的结果。(可以自己试试,并思考为什么)

我们可以使用矩阵化的操作:

v = [1 2 3 4 5];
sumv = v * v';

这里用到了行矩阵x列矩阵的乘法。

A m × n × B n × p = C m × p A 1 × n × B n × 1 = c \begin{split} & A_{m \times n} \times B_{n \times p} = C_{m \times p} \\ & A_{1 \times n} \times B_{n \times 1} = c \end{split} Am×n×Bn×p=Cm×pA1×n×Bn×1=c

2.2 计算向量化

Matlab还提供了另外一个非常强大的工具,那就是向量化。向量化就是把操作符应用到一个矩阵的每一个元素上。比如,计算向量平方和,可以用向量化的方法写成:

v = [1 2 3 4 5];
sumv = sum(v.^2);

这里用到一个向量化操作的函数sum和一个向量化算符.^。前者是对矩阵的每一个元素求和,后者是对矩阵的每一个元素进行平方。

Matlab提供了大量的向量化操作符和函数,这些函数和操作符都是高度优化过的

3. 数组的逻辑索引与arrayfun函数

Matlab还提供了一些数组的逻辑操作,这些操作也是高度优化过的。比如,我们要找出一个向量中所有大于5的元素:

v = [1 2 3 4 5 6 7 8 9];
vgt5 = v(v > 5);

这里用到了一个逻辑操作符>,这个操作符会产生一个逻辑数组,然后我们可以用这个逻辑数组来索引原数组。

通过逻辑数组索引,还能够实现很多高级的操作。比如,我们要把一个向量中所有的偶数都变成0:

v = [1 2 3 4 5 6 7 8 9];
v(v % 2 == 0) = 0;

这里用到了一个逻辑操作符==,这个操作符会产生一个逻辑数组,然后我们可以用这个逻辑数组来索引原数组。

可以通过比较复杂的函数来产生逻辑数组,然后用逻辑数组来索引原数组。这样,我们就可以把很多循环操作转化为矩阵化的操作。特别是,用逻辑判断的函数加上arrayfun函数,可以实现很多循环操作。

v = [1 2 3 4 5 6 7 8 9];
predicate = @(x) x > 5 && x < 8;
index = arrayfun(predicate, v);
v(index) = 0;

通过这样的办法,可以写出非常易读的代码,并且也可能会更快。

4. 总结

在Matlab中,for循环是非常慢的。我们应该尽量避免使用for循环。Matlab提供了大量的矩阵化操作符和函数,向量化操作符和函数,逻辑操作符和函数,这些操作符和函数都是高度优化过的。我们应该尽量使用这些操作符和函数,来代替for循环。

  1. 使用矩阵化的操作,从设计和推导算法的时候就采用矩阵来表示;
  2. 使用向量化的操作,对矩阵的每一个元素进行操作;
  3. 使用逻辑索引操作矩阵的部分元素;
  4. 可以考虑使用arrayfun函数来代替for循环。

相关文章:

  • Session、Cookie 和 Token的保存
  • 【蓝桥杯选拔赛真题72】python输出整数 第十五届青少年组蓝桥杯python选拔赛真题 算法思维真题解析
  • 软件测试教程 自动化测试之Junit框架
  • 网络安全进入AI赋能时代
  • 记录C++中,子类同名属性并不能完全覆盖父类属性的问题
  • Linux编程3.8 进程-守护进程
  • nodejs中使用@maxmind/geoip2-node 查询地理位置信息
  • MySQL的进阶使用方法
  • 3D开发工具HOOPS如何助力3D项目实现扩展现实技术?
  • Trait与生命周期
  • 学习vue3 第四章(reactive全家桶)
  • playwright自动化项目搭建
  • laravel(源码笔记)服务绑定和解析(依赖注入-反射,控制反转)
  • 【DFS+贪心】第十四届蓝桥杯省赛C++ B组《飞机降落》(C++)
  • wordpress给指定ID分类添加特定的字段
  • 78. Subsets
  • Angular 2 DI - IoC DI - 1
  • Gradle 5.0 正式版发布
  • HTTP中的ETag在移动客户端的应用
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • maven工程打包jar以及java jar命令的classpath使用
  • Octave 入门
  • opencv python Meanshift 和 Camshift
  • php的插入排序,通过双层for循环
  • python 学习笔记 - Queue Pipes,进程间通讯
  • React-flux杂记
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • SQLServer之创建数据库快照
  • tab.js分享及浏览器兼容性问题汇总
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 嵌入式文件系统
  • !!Dom4j 学习笔记
  • #pragma multi_compile #pragma shader_feature
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (LeetCode) T14. Longest Common Prefix
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (二)学习JVM —— 垃圾回收机制
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (黑马C++)L06 重载与继承
  • (六)激光线扫描-三维重建
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (轉)JSON.stringify 语法实例讲解
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .NET 读取 JSON格式的数据
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .Net多线程总结
  • .net开发引用程序集提示没有强名称的解决办法
  • /usr/bin/perl:bad interpreter:No such file or directory 的解决办法
  • :O)修改linux硬件时间