对于理工类的学生而言,矩阵是再熟悉不过的数学工具了。即使在学习完线性代数这门课程之后并没有在现实中经常使用里面的各种计算方法,比如求逆矩阵、LU 分解等,也在不知不觉中和矩阵有广泛的接触。实际上,各种编程语言中的一维数组就是一个行向量;二维数组就是一个矩阵。说到和矩阵运算有关的软件,很多人都会想到 Matlab 和 Scilab,这两个软件在语法和功能上具有高度的相似性,只不过前者是商业软件,后者是开源软件。另外,Python 语言里也有与矩阵运算相关的强大的库,可以满足基本的运算需求。但是在很多领域,人们依然会选择使用 C++ 进行科学计算,尽管 GSL 中也有和矩阵运算有关的功能,但是由于它本身不是使用 C++ 的语法写的,因此不具备面向对象的功能,使用起来不是那么方便。在这篇文章中,将介绍一下 Armadillo 的使用方法。
Armadillo 是一个可以被 C++ 调用的高性能线性代数库,这个库被推出的主要目的就是为了兼顾运算速度和对使用人员的友好性。Armadillo 的语法和 Matlab 非常相似,支持向量、矩阵、分块矩阵、稀疏矩阵的使用。与 GSL 不同的是,只需要头文件和少数几个用来加速运算的库,就可以使用 Armadillo 中定义的方法。可以在下面的网站下载到最新版本的库:http://arma.sourceforge.net/download.html。关于它在 Windows、Linux、Mac 中的安装方法,可以在网络上找到,这里就不展开介绍了。需要注意的是,如果是在 Windows 上安装的话,最好保证自己电脑上的 Visual Studio 的版本高于2012(即 VS2012),否则在编译的时候会报错。这是因为最近几个版本的库需要编译器支持 C++11,而 VS2010 对 C++11 的支持并不充足。有两种解决这个问题的方法,要么安装更高版本的 VS,要么就寻找更低版本的 Armadillo。小编比较推荐前者,因为较低版本的 Armadillo 可能会带来功能上的下降。下面就来看一下 Armadillo 的使用方法吧。
矩阵的声明和输出
无论是矩阵(mat)还是向量(vec),在 Armadillo 中都是一个 Class,由于 C++ 支持函数重载以及运算符重载,因此矩阵类和向量类都有很多种构造函数,也就有了很多种声明方法。
#include #include using namespace std;using namespace arma;int main(){ vec y(10, fill::zeros); y.print("y:"); mat A(10, 10, fill::randu); vec z = A.col(5); z.print("z:"); imat B = "1 2 3; 4 5 6; 7 8 9;"; B.print("B:"); return 0;}
第2行是要包含的头文件,第4行是 Armadillo 的命名空间,如果不加入这句的话,就需要显式的使用 arma::mat 这样的词语。程序中的 vec 表示列向量的类,如果是行向量的话,就用 rowvec 类,mat 表示矩阵类,这两个类的数据成员都是 double 型的。而 imat 类的数据成员是整型。第8行表示用0填充这个列向量,第11行表示用均匀分布的随机数填充这个10*10的矩阵,第15行是利用运算符重载对矩阵进行赋值,可以看到,这种声明方式与 Matlab 中的语法高度相似。在输出矩阵的时候既可以使用 C++ 里面的 cout 类,也可以使用 Armadillo 提供的 print 方法。print 方法在输出的时候会自动将数据对齐,使其看上去较为美观。下图是编译代码的方法和运行结果:
在编译的时候要选择 C++11 标准,并且链接上 armadillo 这个库。
输出到文件与从文件中载入
Armadillo 支持将矩阵输入到 txt、csv、hdf5 等格式的文件中,也支持以二进制的形式保存数据。
#include "allvars.h"int main(){ mat A(5, 6, fill::randu); A.save("A.txt", raw_ascii); A.save("A.csv", csv_ascii); mat C; C.load("A.txt", raw_ascii); C.print("C:");}
在这个程序中,已经把几个必要的头文件和使用命名空间的语句放到了 allvars.h 头文件中。save 方法和 load 方法分别可以向文件中写入和从文件中读取数据。第一个参数表示文件名,第二个参数表示数据的格式。raw_ascii 和 csv_ascii 分别表示使用空格分割数据和使用逗号分隔数据,两种数据格式都以人类可读的形式保存在文件中。
矩阵的相关运算
现在看一下和矩阵有关的运算,这一部分涉及的内容比较多,因为篇幅所限,只展示了比较常用的几种方法。
#include"allvars.h"int main(){ imat x = "1 2 3; 4 5 6; 7 8 9;"; x.print("x:"); cout << "x has " << x.n_cols << " columns" << endl; cout << "x has " << x.n_rows << " rows" << endl; cout << "x has " << x.n_elem << " elements" << endl; cout << "x[1,1] = " << x(1,1) << endl; ivec a = x.diag(); a.print("a:"); imat y = x.t(); y.print("y:"); mat A(2, 2); A.zeros(); A.print("A:"); mat B(3, 3); B.ones(); B.print("B:"); B.eye(); B.print("B:"); mat b = B.i(); b.print("b:"); vec c = linspace(0, 5, 6); c.print("c:"); rowvec d = linspace(5, 0, 6); d.print("d:"); mat m = "1 2 3; 4 5 6; 7 8 9;"; cout << "det(m) = " << det(m) << endl; cout <"trace(m) = " <endl;}
第7-9行可以获取矩阵的行数、列数和元素的数目,第11行使用 diag 方法获取方阵的对角元,第14行是对矩阵进行转置。第22行使用 ones 方法生成全1矩阵,第24行生成一个单位阵,第26行是对矩阵进行求逆运算。可以看到这里的很多语法都与 Matlab 非常类似。第29行和第31行是使用 linspace 函数产生一个一维数组,数组的区间和步长都可以在函数中显式指定。第35行和第36行分别计算方阵的行列式和方阵的迹。
此外,Armadillo 还支持特征值计算、快速傅里叶变换、矩阵分解、复矩阵等。更详细的内容可以前往下面的网址进行学习:
http://arma.sourceforge.net/docs.html