MATLAB如何建立循环将50*50的矩阵对角矩阵线数值删除,直至变成5*5矩阵。

用过Matlab的人都知道Matlab是一种解释性語言,存在计算速度慢的问题为了提高程序的运行效率,matlab提供了多种实用工具及编码技巧

Matlab是为矢量和矩阵操作而设计的,因此可以通过矢量化方法加速M文件的运行。矢量化是指将for循环和while循环转换为等价的矢量或矩阵操作下面给出一个循环的例子:i=0;for n = 0:0.1:1000;y=cos(n);我们可以用tic和toc函数來查看上述各代码运行的时间,采用for循环的程序0.39秒(具体时间和计算机配置有关)而矢量化后几乎耗时为0。

2. 给数组或矩阵预分配内存
    特別是使用大型数组或矩阵时Matlab进行动态内存分配和取消时,可能会产生内存碎片这将导致大量闲置内存产生,预分配可通过提前给大型數据结构预约足够空间来避免这个问题

3. 用函数代替脚本文件
    因为每次调用MATLAB的脚本文件都需要将不必要的中间变量加载到内存中,每执行┅次就加载一次。函数在调用时被编译成了伪代码只需要加载到内存一次。当多次调用同一个函数时会运行快一些因此尽量多使用函数文件而少使用脚本文件,也是提高执行效率的一种方法

4. 用Mex文件编写循环代码
    Matlab提供了与C和C++的接口,那么我们可以在用C或C++语言编写耗时嘚循环代码然后通过接口程序在Matlab中转换成dll文件,这就是我们所要的Mex文件通过这种方法可以极大地提高计算速率。

1. 尽量避免使用循环结構

MATLAB变量的基本类型是矩阵当对矩阵的每个元素循环处理时,运算速度很慢因此编程时应尽量把数组和矩阵看作一个整体来进行编程,洏不是像其他的程序设计语言那样使用循环结构对矩阵的元素循环进行处理。利用MATLAB提供的用于矢量化操作的函数把循环矢量化,这样既可以提高编程效率也可以提高程序的执行效率。下面给出一个循环的例子:i=0;for cos(n)通过使用MATLAB专门提供的测试程序运行时间的函数可以发现,把数组看作一个整体进行操作后,执行效率提高约300倍另外,在必须使用多重循环的情况下建议在循环的外环执行循环次数少的,內环执行循环次数多的这样也可以显著提高程序执行速度。2. 在使用数组或矩阵之前先定义维数MATLAB中的变量在使用之前不需要明确地定义和指定维数但当未预定义数组或矩阵的维数时,当需赋值的元素下标超出现有的维数时MATLAB 就为该数组或矩阵扩维一次,这样就会大大降低程序的执行效率因此,在使用数组或矩阵之前预定义维数可以提高程序的执行效率3. 对矩阵元素使用下标或者索引操作MATLAB中矩阵元素的引用可用两个下标来表示。例如:A(i,j) 表示矩阵的第i行第j列的元素;A(1:k,j)表示矩阵A的第j列的前k个元素;A(:,j) 表示矩阵的第j列的所有元素求矩阵A的苐j列元素的平均值的表达式为mean(A(:,j))。4. 尽量多使用函数文件少使用脚本文件因为每次调用MATLAB的脚本文件都需要将不必要的中间变量加载到内存中烸执行一次,就加载一次函数在调用时被编译成了伪代码,只需要加载到内存一次当多次调用同一个函数时会运行快一些。因此尽量哆使用函数文件而少使用脚本文件也是提高执行效率的一种方法。5. 在必须使用循环时可以考虑转换为C-MEX当必须使用耗时的循环时,可以栲虑将循环体中的语句转换为C-MEXC-MEX是将M文件通过MATLAB的编译器转换为可执行文件,是按照 MEX 技术要求的格式编写相应的程序通过编译连接,生成擴展名为.dll的动态链接库文件可以在MATLAB环境下直接执行。这样循环体中的语句在执行时不必每次都解释(interpret)。一般来说C-MEX 文件的执行速度是相哃功能的M文件执行速率的20~40倍。编写C-MEX不同于M文件需要了解MATLAB C-MEX规范。幸运的是MATLAB提供了将M文件转换为C-MEX的工具6. 内存优化MATLAB在进行复杂的运算时需要占用大量的内存。合理使用内存和提高内存的使用效率可以加快运行速度,减少系统资源的占用7. 内存管理函数和命令(1)Clear all:从内存中刪除所有的变量。(3)Save:将指令的变量存入磁盘(4)Load:将save命令存入的变量载入内存。(5)Quit:退出MATLAB并释放所有分配的内存。(6)Pack:把内存中的变量存入磁盘再用内存中的连续空间载回这些变量。考虑到执行效率问题不能在循环中使用。8. 节约内存的方法(1)避免生成大嘚中间变量并删除不再需要的临时变量。(2)当使用大的矩阵变量时预先指定维数并分配好内存,避免每次临时扩充维数(3)当程序需要生成大量变量数据时,可以考虑定期将变量写到磁盘然后清除这些变量。当需要这些变量时再重新从磁盘加载。(4)当矩阵中數据极少时将全矩阵转换为稀疏矩阵。

提高MATLAB程序效率的几点原则这些都是俺在这两年中参加四次数模编写大量m程序总结的经验,加之網上很多英雄也是所见略同

1.“计算向量、矩阵化,尽量减少for循环”[/B]因为MATLAB本来就是矩阵实验室的意思,他提供了极其强大而灵活的矩阵運算能力你就没必要自己再用自己编写的for循环去实现矩阵运算的功能了。另外由于matlab是一种解释性语言所以最忌讳直接使用循环语句。泹在有些情况下使用for循环可以提高程序的易读性,在效率提高不是很明显的情况下可以选择使用for循环口说无凭,下面是利用tic与toc命令计算运算所用时间的方法测试两种编程的效率。需要说明的是没有办法精确计算程序执行时间matlab帮助这样写到“Keep programs.”意思是说在程序执行的褙后很可能有其他程序在执行,这里涉及到程序进程的的问题m程序执行的过程中很可能有其他进程中断m程序来利用cup,所以计算出来的时間就不仅是m程序的了在这种情况下我把那些寄点去掉,进行多次计算求他的平均时间

t1、t2分别代表用for循环编程和矩阵化编程计算矩阵乘姠量所用时间,u代表时间的比值u(u<0) = [];是认为t1不可能小于t2,所以去掉不可能出现的情况然后画出图形计算平均值。

3.“尽可能利用matlab内部提供的函数”[/U]因为matlab内部提供的函数绝对是各种问题的最优算法那写程序都是他们大师级人物写出来的,程序应该说相当高效有现成的为什么鈈用那!     这个原则就不用实际的程序测试了。

关于MATLAB程序提速的问题可以参考网上很多智者的文章,都比较经典也可以看看我的上一篇攵章,和网上大部分帖子有点不同我是以实际的测试程序作为依据对如何提高MATLAB程序速度进行介绍的。      这里我再补充几点大家需要注意的下面是我在国内一个比较出名的论坛看到的关于m程序提速的帖子,开始还真以为他们谈论的都应该遵循(尽信书不如无书)

100);”凡事不能只凭自己的感觉,没有一点实际的例子对于权威我们还要有挑战精神那,就不用说现在还不是经典的观点了下面是我写的测试程序,我本来是想得到这位网友大哥的结果但是实事不是我们想象的那么简单。


由此我想说的是不管是在我们做论文,还是写博客的时候别直接从网上或者别人文章那儿找点知识定理之类的补充自己那苍白无力的文章。最好是自己动手编一下“实践是检验真理的唯一标准”。

经过这一测试我感觉有必要,也有责任对这个论坛上的一大批经典谈论加以测试尽管这个结论是错误的但这还不足以证明论坛仩的帖子都不是经典。
还有一点关于m程序提速的这样说到:“在必须使用多重循环时下如果两个循环执行的次数不同,则在循环的外环執行循环次数少的内环执行循环次数多的。这样可以显著提高速度”

由这个时间图可以看出for循环的嵌套顺序对于速度是有影响的,虽嘫相对来说差别不是很大但是毕竟论坛上的观点是正确的。至于他所说的“显著”二字就没必要加上了此论坛还有一些提速的观点列舉如下:“遵守Performance Acceleration的规则     classes,int64,uint642、matlab不会对超过三维的数组进行加速。3、当使用for循环时只有遵守以下规则才会被加速:a、for循环的范围只用标量值来表示;b、for循环内部的每一条语句都要满足上面的两条规则,即只使用支持加速的数据类型只使用三维以下的数组;c、循环内只调用了内建函数(build-in function)。4、当使用if、elseif、while和switch时其条件测试语句中只使用了标量值时,将加速运行5、不要在一行中写入多条操作,这样会减慢运行速喥即不要有这样的语句:x = ; for k=1:10000, sin(A(k)), end;6、当某条操作改变了原来变量的数据类型或形状(大小,维数)时将会减慢运行速度7、应该这样使用复常量x = 7 + 2i,而不应该这样使用:x = 7 + 等”“优先使用matlab内建函数,将耗时的循环编写进MEX-File中以获得加速b、使用Functions而不是Scripts 。”“ 绝招:你也许觉得下面两条昰屁话但有时候它真的是解决问题的最好方法。1、改用更有效的算法2、采用Mex技术或者利用matlab提供的工具将程序转化为C语言、Fortran语言。关于洳何将M文件转化为C语言程序运行可以参阅本版帖子:“总结:m文件转化为c/c++语言文件,VC编译” ”

除了m程序提速的问题,这里还列出了《MATLAB玳码矢量化指南(译)》

y^2) 我们有一系列x值保存在x向量中,同时我们还有一系列y值我们要对向量x上的每个点和向量y上的每个点计算F值。換句话说我们要计算对于给定向量x和y的所确定的网格上的F值。 使用meshgrid我们可以复制x和y来建立合适的输入向量。然后 可以使用第2节中的方法来计算这个函数 x Setting, and Counting Operations) 在迄今为止讨论过的例子中,对向量中一个元素的计算都是独立于同一向量的其他元素的但是,在许多应用中你偠做的计算则可能与其它元素密切相关。例如假设你用一个向量x来表示一 个集合。不观察向量的其他元素你并不知道某个元素是不是┅个冗余元素,并应该被去掉如何在不使用循环语句的情况下删除冗余元素,至少在现在并不是一个明显可以解决的问题。 解决这类問题需要相当的智巧以下介绍一些可用的基本工具

继续我们的实例,消除向量中的多余元素注意:一旦向量排序后,任何多余的元素僦是相邻的了同时,在任何相等的相邻元素在向量diff运算时变为零这是我们能够应用以下策略达到目的。我们现在在已排序向量中选取那些差分非零的元素。

这离正确结果很近了但是我们忘了diff函数返回向量的元素个数比输入向量少1。在我们的初次尝试中没有考虑到朂后一个元素也可能是相异的。为了解决这个问题我们可以在进行差分之前给向量x加入一个元素,并且使得它与以前的元素一定不同┅种实现的方法是增加一个NaN。

我们使用(:)运算来保证x是一个向量我们使用~=0运算,而不用find函数因为find函数不返回NaN元素的索引值,而我们操作Φ差分的最后元素一定是NaN这一实例还有另一种实现方式:

后者当然很简单,但是前者作为一个练习并非无用它是为了练习使用矢量化技术,并示范如何编写你自己的高效代码此外,前者还有一个作用:Unique函数提供了一些超出我们要求的额外功能这可能降低代码的执行速度。 
      假设我们不只是要返回集合x而且要知道在原始的矩阵里每个相异元素出现了多少个“复本”。一旦我们对x排序并进行了差分我們可以用find来确定差分变化的位置。再将这个变化位置进行差分就可以得到复本的数目。这就是"diff of find of diff"的技巧基于以上的讨论,我们有:

这个圖画出了x中每个相异元素出现的复本数注意,在这里我们避开了NaN因为find不返回NaN元素的索引值。但是作为特例,NaN和Inf 的复本数可以容易地計算出来:

另一个用于求和或者计数运算的矢量化技巧是用类似建立稀疏矩阵的方法实现的这还将在第9节中作更加详细的讨论. 

在某些情況下,你可以使用稀疏矩阵来增加计算的效率如果你构造一个大的中间矩阵,通常矢量化更加容易在某些情况下,你可以充分利用稀疏矩阵结构来矢量化代码而对于这个中间矩阵不需要大的存储空间。 

例如这样的数据可能代表一个调色板的索引值。然后你就可以對集合中每个元素的出现进行计数(构建色彩直方图?译者)这是对上一节中"diff of find of diff"技巧的一种变形。现在让我们来构造一个大的m x n矩阵A这里m昰原始x向量中的元素数, n是集合y中的元素数 

回想一下第3节和第4节,你可能认为我们需要从x和y来构造矩阵A如果当然可以,但要消耗许多存储空间我们可以做得更好,因为我们知道矩阵A中的多数元素为0,x中的每个元素对应的行上只有一个值为1 

现在我们对A的列进行求和,得到出现次数 

这里的关键是使用数据,(也就是说用x控制矩阵A的结构)。由于x在一个已知范围内取整数值我们可以更加有效地构慥矩阵。 假设你要给一个很大矩阵的每一列乘以相同的向量使用稀疏矩阵,不仅可以节省空间并且要比在第5节介绍的方法更加快速. 下媔是它的工作方式: 
% 对F的所有行进行点型乘法. 
% 对F的所有列进行点型乘法. 

我们充分利用矩阵乘法算符来执行大规模运算,并使用稀疏矩阵以防止临时变量变得太大 

注意:这一方法开辟了稀疏矩阵的新用途。在使用sparse命令创建稀疏矩阵时它是对分配到同一个索引的所有值求和,而不是替代已有的数值这称为"向量累加",是MATLAB处理稀疏矩阵的方式

关于MATLAB的效率问题,很多文章包括我之前写的一些,主要集中在使鼡向量化以及相关的问题上但是,最近我在实验时对代码进行profile的过程中发现在新版本的MATLAB下,for-loop已经得到了极大优化而效率的瓶颈更多昰在函数调用和索引访问的过程中。

由于MATLAB特有的解释过程不同方式的函数调用和元素索引,其效率差别巨大不恰当的使用方式可能在夲来不起眼的地方带来严重的开销,甚至可能使你的代码的运行时间增加上千倍(这就不是多买几台服务器或者增加计算节点能解决的了呵呵)。下面通过一些简单例子说明问题(实验选在装有Windows Vista的一台普通的台式机(Core2 Duo 这可能和大部分朋友的环境更相似一些。实验过程是对某一个过程实施多次的整体进行计时然后得到每次过程的平均时间,以减少计时误差带来的影响多次实验,在均值附近正负20%的范围内嘚置信度高于95%为了避免算上首次运行时花在预编译上的时间,在开始计时前都进行充分的“热身”运行) 函数调用的效率一个非常简單的例子,把向量中每个元素加1(当然这个例子根本不需要调函数,但是用它主要是为了减少函数执行本身的时间,突出函数解析和調用的过程)作为baseline,先看看最直接的实现% sec从统计意义上说,和vectorization已经没有显著差别无论是for-loop或者vectorization,每秒平均进行约10亿次“索引-读取-加法-寫入”的过程计算资源应该得到了比较充分的利用。要是这个过程使用了函数调用呢MATLAB里面支持很多种函数调用方式,主要的有m-function, function handle, 则需要0.385秒时间多了几百倍,而它们干的是同一件事情这说明了,函数调用的开销远远大于for-loop自己的开销和简单计算过程——在不同情况可能有點差别一般而言,一次普通的函数调用花费的时间相当于进行了几百次到一两千次双精度浮点加法 使用function handle和anonymous function的开销比使用普通的m-函数要高一些。这可能是由于涉及到句柄解析的时间而普通的函数在第一次运行前已经在内存中进行预编译。 inline function的运行时间则令人难以接受了竟然需要一百多秒(是普通函数调用的四百多倍,是直接计算的十几万倍)这是因为matlab是在每次运行时临时对字符串表达式(或者它的某種不太高效的内部表达)进行parse。 feval(fh, u(i))和fh(u(i))feval(fa,

u(i));end竟然慢了53倍(前者0.652秒,后者34.689秒)由于在MATLAB的内部实现中,function handle的解析是在赋值过程中进行的所以预先用一個变量把句柄接下,其效果就是预先完成了句柄解析而如果直接把@fm或者@(x) x + 1写在参数列上,虽然写法简洁一些但是解析过程是把参数被赋徝到所调函数的局部变量时才进行,每调用一次解析一次造成了巨大的开销。 feval使用字符串作为函数名字时所耗时间比传入句柄大,因為这涉及到对函数进行搜索的时间(当然这个搜索是在一个索引好的cache里面进行(除了第一次)而不是在所有path指定的路径中搜索。)

sec)还慢叻7倍多这个看上去“消除了for-loop"的函数,由于其内部设计的原因未必能带来效率上的正效果。 元素和域的访问除了函数调用数据的访问方式对于效率也有很大影响。MATLAB主要支持下面一些形式的访问:

sec我们可以看到MATLAB对于单个数组元素或者静态的struct field的访问可以达到不错的速度,茬主流台式机约每秒2亿次(连同for-loop的开销)而cell array的访问则明显缓慢,约每秒400万次(慢了50倍)MATLAB还支持灵活的使用字符串来指定要访问域的语法(动态名字),但是是以巨大的开销为代价的,比起静态的访问慢了200倍以上关于Object-oriented ProgrammingMATLAB在新的版本中(尤其是2008版),对于面向对象的编程提供了强大的支持在2008a中,它对于OO的支持已经不亚于python等的高级脚本语言但是,我在实验中看到虽然在语法上提供了全面的支持,但是matlab裏面面向对象的效率很低开销巨大。这里仅举几个例子object中的property的访问速度是3500万次,比struct field慢了6-8倍MATLAB提供了一种叫做dependent property的属性,非常灵活但是,效率更低平均每秒访问速度竟然低至2.6万次(这种速度基本甚至难以用于中小规模的应用中)。object中method调用的效率也明显低于普通函数的调鼡对于instance method,每百万次调用平均需时5.8秒,而对于static method每百万次调用需时25.8秒。这相当于每次调用都需要临时解析的速度而matlab的类方法解析的效率目前还明显偏低。MATLAB中可以通过改写subsref和subsasgn的方法对于对象的索引和域的访问进行非常灵活的改造,可以通过它创建类似于数组的对象但昰,一个符合要求的subsref的行为比较复杂在一个提供了subsref的对象中,大部分行为都需要subsref进行调度而默认的较优的调度方式将被掩盖。在一个提供了subsref的类中(使用一种最快捷的实现)object property的平均访问速度竟然降到1万5千次每秒。建议[/U]


根据上面的分析对于撰写高效MATLAB代码,我有下面一些建议:
  1. 虽然for-loop的速度有了很大改善vectorization(向量化)仍旧是改善效率的重要途径,尤其是在能把运算改写成矩阵乘法的情况下改善尤为显著。
  2. 在不少情况下for-loop本身已经不构成太大问题,尤其是当循环体本身需要较多的计算的时候这个时候,改善概率的关键在于改善循环体本身而不是去掉for-loop
  3. MATLAB的函数调用过程(非built-in function)有显著开销,因此在效率要求较高的代码中,应该尽可能采用扁平的调用结构也就是在保持代碼清晰和可维护的情况下,尽量直接写表达式和利用built-in function避免不必要的自定义函数调用过程。在次数很多的循环体内(包括在cellfun, arrayfun等实际上蕴含循环的函数)形成长调用链会带来很大的开销。
  4. 在可能的情况下使用numeric array或者struct array,它们的效率大幅度高于cell array(几十倍甚至更多)对于struct,尽可能使用普通的域(字段field)访问方式,在非效率关键执行次数较少,而灵活性要求较高的代码中可以考虑使用动态名称的域访问。
  5. 虽嘫object-oriented从软件工程的角度更为优胜而且object的使用很多时候很方便,但是MATLAB目前对于OO的实现效率很低在效率关键的代码中应该慎用objects。
  6. 如果需要设計类应该尽可能采用普通的property,而避免灵活但是效率很低的dependent property如非确实必要,避免重载subsref和subsasgn函数因为这会全面接管对于object的接口调用,往往會带来非常巨大的开销(成千上万倍的减慢)甚至使得本来几乎不是问题的代码成为性能瓶颈。
}

在作业中需要求某个矩阵的特征徝和特征向量当然同学们都会使用MATLAB内置的函数[V,D]=eig(A),这样得到的对角矩阵矩阵D其对角矩阵线元素就是从小到大排列的的特征值有的同学希朢能将这个对角矩阵线元素单独抽取出来变成一个行向量或者列向量,还希望特征值从大到小排列为了达到这个目的,大家用了各种各樣的方法其实MATLAB内置的函数就有diag()可以将一个对角矩阵矩阵转化为一个向量,也可以将一个向量转化

在作业中需要求某个矩阵的特征值和特征向量当然同学们都会使用 MATLAB 内置的函数[V, D] = eig(A),这样得到的对角矩阵矩阵 D 其对角矩阵线元素就是从小到大排列的的特征值有的同学希望能将這个对角矩阵线元素单独抽取出来变成一个行向量或者列向量,还希望特征值从大到小排列为了达到这个目的,大家用了各种各样的方法其实 MATLAB 内置的函数就有diag()可以将一个对角矩阵矩阵转化为一个向量,也可以将一个向量转化为一个对角矩阵矩阵转换完之后对向量进行逆序排列,我看到有同学硬是写了一个冒泡排序来完成这个工作你的数据结构老师一定非常欣慰。但是在 MATLAB 里不用从头开始直接调用sort()函數就行了~但是在这里还有更简单的方法,wrev()函数就可以将一个向量逆序排列fliplr()函数可以将一个矩阵左右镜像对称,所以如果想得到从大到小排列的特征值只要这样就行了:

% 得到从大到小排列的特征值
 
这样特征值和特征向量就都排列好了。

}

我要回帖

更多关于 对角矩阵 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信