Octave简明教程(一)
GNU Octave是一种以高级编程语言为特色的软件,主要用于数值计算和绘图。通过Octave可以非常方便的进行矩阵运算、求解联立方程组、计算矩阵特征值和特征向量等。本篇文档主要内容是Octave基础命令、数据格式、绘制图形、Octave脚本编写、Octave函数编程等内容。另外,此文档会不断补充常见使用场景。
为什么使用Octave
当我们使用C++来处理矩阵运算方面的问题确实显得过于复杂时,C++这样的编程语言不原生的支持一些数学概念或者生成图表。Octave 是专为解决这类问题而设计的,所以很多时候用Octave来编写软件的原型或者数学部分,因为这样能够很快的检验其算法的正确性。
另外GNU Octave自由、免费、而且轻量级。MATLAB的Licence很难获取,故我选择GNU Octave。
官方文档地址
如果是初次使用Octave,英文比较好的话可以直接阅读官方文档 https://octave.org/octave.pdf ,文档里有最详细的介绍,如果只是想了解日常的使用方式那么看这篇文章足矣,如果遇到到其他在本文中没有出现过的场景,我会翻译后补充在本文中。
Octave环境概述
安装Octave
安装Octave可以参考 https://www.gnu.org/software/octave/download ,Mac下直接使用brew安装即可,如果需要GUI界面,可以参考如下方式:
1brew reinstall octave--with-qt
我们通常在命令行界面运行Octave,每个命令依次输入命令行中,并以回车结束。Octave是一门解释型语言,即每个命令通过解释器转化为机器语言。
Octave工作空间
命令行所处的ENV,也就是本次打开Octave的上下文ENV。所以对于Octave有工作空间的概念,一切的函数文件、脚本文件都只对当前的工作空间生效。
通过who
命令可以查看本次所处于的工作空间的变量,clear用于清除已经存在的变量,也可以指定变量清除:
1octave:1> who
2octave:2> a = 10; b = 20; c = a + b;
3octave:3> who
4Variables visible from the current scope:
5
6a b c
7
8octave:4> d = 200;
9octave:5> who
10Variables visible from the current scope:
11
12a b c d
13
14octave:6> clear
15octave:7> who
16octave:8> d = 200;
17octave:9> clear d
18octave:10> who
19octave:11> clear
20octave:12> a = 10; b = 20; c = a + b;
21octave:13> who
22Variables visible from the current scope:
23
24a b c
25
26octave:14> clear b
27octave:15> who
28Variables visible from the current scope:
29
30a c
31
32octave:16>
帮助命令
如果你不了解某个 Octave 命令的功能或者是你需要找一个特定的函数,Octave 本身强大的帮助系统会很有用。最基本的使用帮助系统的方式就是:help commandname,比如查看clear命令、查看sin函数:
1octave:1> help clear
2'clear' is a built-in function from the file libinterp/corefcn/variables.cc
3
4 -- clear
5 -- clear PATTERN ...
6 -- clear OPTIONS PATTERN ...
7 Delete the names matching the given PATTERNs thereby freeing
8 memory...
9
10octave:2> help sin
11'sin' is a built-in function from the file libinterp/corefcn/mappers.cc
12 -- sin (X)
13 Compute the sine for each element of X in radians.
14 See also: asin, sind, sinh...
数据载入&保存
退出Octave的交互式环境的时候,你将丢失你所创建的变量。如果你需要在工作的中途退出 Octave,那么你可以保存当前会话的数据并在之后进行重新载入:
1octave:1> a = [1:10];
2octave:2> b = [1:0.5:10];
3octave:3> save anyname
4octave:4> exit
5
6zchanglin@mbp OctaveWK % ls
7anyname octave-workspace
8zchanglin@mbp OctaveWK % cat anyname
9# Created by Octave 6.4.0, Thu Jan 27 17:45:40 2022 CST <zchanglin@mbp.local>
10# name: a
11# type: matrix
12# rows: 1
13# columns: 10
14 1 2 3 4 5 6 7 8 9 10
15
16
17# name: b
18# type: matrix
19# rows: 1
20# columns: 19
21 1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8 8.5 9 9.5 10
这将整个工作空间上的变量存储到当前目录下一个名为 anyname 的文件中,这样你可以退出 Octave,之后重新启动 Octave 程序,通过输入:load anyname
1octave:1> load anyname
2octave:2> a
3a =
4
5 1 2 3 4 5 6 7 8 9 10
6
7octave:3> b
8b =
9
10 Columns 1 through 14:
11
12 1.0000 1.5000 2.0000 2.5000 3.0000 3.5000 4.0000 4.5000 5.0000 5.5000 6.0000 6.5000 7.0000 7.5000
13
14 Columns 15 through 19:
15
16 8.0000 8.5000 9.0000 9.5000 10.0000
17
18octave:4>
将重新载入之前保存的命名空间,并从你中断的地方重新开始工作。
同样的如果只是想保存某些变量而非全部保存,只需要在上述命令中加入变量名称即可:
1octave:1> a = 10; b = 20; c=30;
2octave:2> save anyname a b;
3octave:3> exit
4
5zchanglin@mbp OctaveWK % octave
6GNU Octave, version 6.4.0
7.....
8
9octave:1> load anyname;
10octave:2> a
11a = 10
12octave:3> b
13b = 20
14octave:4> c
15error: 'c' undefined near line 1, column 1
16octave:5>
在上述例子中,可能你注意到了结尾加不加分号的区别,加了分号表示不显示本条指令的结果,加了的话就会展示本条指令的结果,这与MATLAB是一致的。
通用计算
启动 & 执行简单计算
Octave 在 UNIX 环境下通过在终端中输入octave来启动,在启动octave 之后,程序一般会显现如下信息:
1zchanglin@mbp ~ % octave
2GNU Octave, version 6.4.0
3Copyright (C) 2021 The Octave Project Developers.
4This is free software; see the source code for copying conditions.
5There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
6FITNESS FOR A PARTICULAR PURPOSE. For details, type 'warranty'.
7
8Octave was configured for "x86_64-apple-darwin19.6.0".
9
10Additional information about Octave is available at https://www.octave.org.
11
12Please contribute if you find this software useful.
13For more information, visit https://www.octave.org/get-involved.html
14
15Read https://www.octave.org/bugs.html to learn how to submit bug reports.
16For information about changes from previous versions, type 'news'.
17
18octave:1> (2+8)/5 * 2 - 3
19ans = 1
20octave:2> a = (2+8)/5 * 2 - 3;
21octave:3> a
22a = 1
各种计算符号的优先级与常规的一致,比如括号有最大优先级,其次为乘方,其次为乘除运算,最后为加减运算。
内建函数与常量
Octave 提供了一系列的常用数学函数,其中的常用部分函数如下所示。像 C++ 中调用函数一样,Octave 通过输入函数名和括号中的输入参数来调用函数,其中自然对数e、圆周率pi 都是Octave已经定义的常量(ans代表上一次的计算结果,就像一个普通的科学计算器一样),例如:
1octave:1> sin(90 * pi/180)
2ans = 1
3octave:2> sin(30 * pi/180)
4ans = 0.5000
5octave:3> cos(60 * pi/180)
6ans = 0.5000
7octave:4> pi
8ans = 3.1416
9octave:5> e
10ans = 2.7183
11octave:6> log(e)
12ans = 1
13octave:7> 2 * ans
14ans = 2
15octave:8> abs(5) + abs(-5)
16ans = 10
17octave:9>
内建函数 | 描述 | 内建函数 | 描述 |
---|---|---|---|
cos | 余弦函数(弧度制) | abs | 绝对值函数 (复数取模 ) |
sin | 正弦函数(弧度制) | sign | 符号函数 |
tan | 正切函数(弧度制) | round | 四舍五入 |
cot | 余切函数(弧度制) | floor | 近似为比它小的最大整数 |
log | 以 e 为底的指数函数 | ceil | 近似为比它大的最小整数 |
log10 | 以 10 为底的指数函数 | fix | 向 0 方向近似 |
exp | 指数函数 | rem | 求余数 |
sinh | 双曲正弦函数 | cosh | 双曲余弦函数 |
tanh | 双曲正切函数 | coth | 双曲余切函数 |
asin | 反正弦函数 | acos | 反余弦函数 |
acosh | 反双曲余弦函数 | asinh | 反双曲正弦函数 |
向量相关的计算
定义向量
构造矩阵或者向量的方法有很多。其中最直接简单的方法就是在一个方括号 [] 中给出其元素,也可以通过已经定义的向量来定义新的向量,中间使用空格隔开或者逗号隔开:
1octave:1> a=[6 5 4]
2a =
3
4 6 5 4
5
6octave:2> b=[3 7];
7octave:3> c = [a 1 3 4 b]
8c =
9
10 6 5 4 1 3 4 3 7
11
12octave:4> d = [a,1,3,4]
13d =
14
15 6 5 4 1 3 4
16
17octave:5>
有时候我们需要快速的定义向量,可以使用冒号表达式来定义:
1octave:1> a = [5:10] # 定义元素为5-10的向量,默认步长为1
2a =
3
4 5 6 7 8 9 10
5
6octave:2> b = [1:0.5:3] # 中间的0.5表示步长
7b =
8
9 1.0000 1.5000 2.0000 2.5000 3.0000
10
11octave:3>
定义矩阵
常规方式定义矩阵:
1octave:1> a = [10, 20, 30; 1, 2, 3] # 定义矩阵a
2a =
3
4 10 20 30
5 1 2 3
6
7octave:2> b = 5:7 # 定义向量b
8b =
9
10 5 6 7
11
12octave:3> a = [10, 20, 30; 1, 2, 3; b] # 向量参与矩阵构建
13a =
14
15 10 20 30
16 1 2 3
17 5 6 7
18
19octave:4>
通过函数定义矩阵与向量
函数 | 描述 |
---|---|
zeros(M, N) | 创建一个 M×N 的零矩阵 |
ones(M, N) | 创建一个 M×N 的全1矩阵 |
rand(M, N) | 创建一个 M×N 的随机矩阵 |
linspace(x1, x2, N) | 创建一个 N 个元素的向量, 均匀分布于 x1 和 x2 |
logspace(x1, x2, N) | 创建一个 N 个元素的向量,指数分布与 10x1 和 10x2 之间 |
1octave:3> zeros(3, 3)
2ans =
3
4 0 0 0
5 0 0 0
6 0 0 0
7
8octave:4> ones(2, 2)
9ans =
10
11 1 1
12 1 1
13
14octave:5> linspace(10, 20, 3)
15ans =
16
17 10 15 20
18
19octave:6> logspace(10, 20, 3)
20ans =
21
22 1.0000e+10 1.0000e+15 1.0000e+20
23
24octave:7>
显示大矩阵/向量
Octave 无法用单屏显示一个元素超多的向量或者矩阵,有一种READ MORE的模式,通过 more off
命令关闭,
通过 more on
命令开启;比如现在查看一个1000个元素的向量:
1octave:1> v = 1:1000
2v =
3
4 Columns 1 through 21:
5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
6 Columns 22 through 42:
7 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
8 Columns 43 through 63:
9 ......
10-- less -- (f)orward, (b)ack, (q)uit
11# 按q退出
12octave:2> more off
操作向量元素
向量中的元素通过括号()
进行访问,而第一个元素的编号为 1, 而不是像其他编程语言那样从0开始。例如:
1octave:1> a = [1:10]
2octave:2> a(5)
3ans = 5
4octave:3> a(5) + a(8)
5ans = 13
6octave:4> b = a(4:6) # 冒号的表示法同样可以用于声明向量中的元素的范围
7b =
8
9 4 5 6
10
11octave:5> length(b) # 求向量b的长度
12ans = 3
向量/矩阵的计算
使用Octave来对向量或者矩阵进行计算非常方便,不用像C/C++那样写多重for循环,先来看看向量数乘,对向量中所有元素都除以一个数的操作与乘法类似:
1octave:1> a = 2:6
2a =
3
4 2 3 4 5 6
5
6octave:2> 2*a
7ans =
8
9 4 6 8 10 12
10
11octave:3> 2*a
12ans =
13
14 4 6 8 10 12
15
16octave:4> a - 1
17ans =
18
19 1 2 3 4 5
20
21octave:5> a + 2
22ans =
23
24 4 5 6 7 8
两个向量的相乘遵循矩阵的乘法法则,向量乘法并不是对应元素的相乘。如果要进行对应元素的乘除法, 你可以使用 .
运算符,每个算符前的 .
表示为一个元素对元素的计算,比如:
$$
\left(\begin{array}{l}
a_{1} \
a_{2} \
a_{3}
\end{array}\right) \cdot *\left(\begin{array}{l}
b_{1} \
b_{2} \
b_{3}
\end{array}\right)=\left(\begin{array}{l}
a_{1} b_{1} \
a_{2} b_{2} \
a_{3} b_{3}
\end{array}\right)
$$
1octave:1> a = 1:5
2a =
3
4 1 2 3 4 5
5
6octave:2> b = 6:10
7b =
8
9 6 7 8 9 10
10
11octave:3> a.*b # 向量元素相乘
12ans =
13
14 6 14 24 36 50
15octave:4> a.^2 # 向量元素二次方
16ans =
17
18 1 4 9 16 25
19
20octave:7> a = rand(3,4) # 创建3*4随机矩阵
21a =
22
23 0.592680 0.824473 0.504916 0.893063
24 0.976128 0.249468 0.866887 0.148853
25 0.933072 0.228585 0.069300 0.384276
26
27octave:8> b = rand(4,3) # 创建3*4随机矩阵
28b =
29
30 0.6803 0.1361 0.1822
31 0.5374 0.6651 0.6490
32 0.3831 0.5916 0.1651
33 0.2554 0.3941 0.9914
34
35octave:9> a * b # 矩阵乘法
36ans =
37
38 1.2678 1.2797 1.6118
39 1.1682 0.8703 0.6305
40 0.8823 0.4715 0.7108
41
42octave:10> a.*b # 矩阵元素相乘
43error: product: nonconformant arguments (op1 is 3x4, op2 is 4x3)
44octave:11> c = rand(3,4) # 创建3*4随机矩阵
45c =
46
47 0.042975 0.462963 0.221173 0.524956
48 0.694888 0.486257 0.416091 0.127658
49 0.042671 0.760997 0.251399 0.655109
50
51octave:12> a * c # 矩阵乘法
52error: operator *: nonconformant arguments (op1 is 3x4, op2 is 3x4)
53octave:13> a .* c # 矩阵元素相乘
54ans =
55
56 0.025471 0.381701 0.111674 0.468819
57 0.678300 0.121305 0.360704 0.019002
58 0.039815 0.173952 0.017422 0.251743
Octave绘图初阶
以创建一个以60度为间隔的角度值,并作为 sin 函数的输入作为例子,进行绘图:
1octave:1> x=[0:pi/3:2*pi]; # 定义X轴数据集
2octave:2> y = sin(x);
3octave:3> plot(x,y); # 绘制函数
4octave:5> xlabel('Angle'); # X轴标注
5octave:6> ylabel('Value'); # Y轴标注
6octave:7> title('Graph of y=sin(x)'); # 图表标题
7octave:8> grid on; # 开启网格
8octave:10> hold; # 图像保持(避免被下一个函数图形覆盖)
9octave:11> y2 = sin(x) + 0.5; # 定义另一个函数
10octave:12> plot(x,y2); # 绘制函数
11octave:13> legend('Sine','Sine+0.5'); # 绘制图例
12octave:14> hold;
13octave:15> print('./graph1.png','-dpng'); # 打印当前图表到工作区
plot函数其实有更多的选项,主要是设置线型和颜色。例如,使用红色和圆圈来画出之前的图片,输入:
1plot(x, y,'ro');
Octave提供了print 命令来将图片打印到默认的打印机上。
另外如果需要绘制多幅图表,则使用 figure
命令新建窗口即可。
下表中列出了plot 命令中的颜色和样式选项,可以通过 help plot 命令查看:
w 白色 |
m 品红 |
c 青色 |
r 红色 |
---|---|---|---|
g 绿色 |
b 蓝色 |
y 黄色 |
k 黑色 |
. 点 |
o 圆圈 |
x x 形 |
+ + 号 |
* 星号 |
s 正方形 |
d 菱形 |
v 下三角 |
< 左三角 |
> 右三角 |
p 五角星 |
p 五角星 |
- 实线 |
: 虚线 |
-. 点划线 |
– 虚线 |
Octave Script
对于一些重复输入的命令,完全可以将这一系列的命令存入一个 Octave 脚本之中。这种包含 Octave 命令的文本文件是 Octave 程序的基本形式,当在 Octave 中执行这样的脚本的时候,其效果与将这些命令一行行输入效果是一样的。而且 Octave 脚本是普通的文本文件,方便修改,脚本需要有 .m
作为后缀。需要执行脚本的时候向 Octave 终端输入脚本文件的名称即可,但是无需加上 .m
后缀。
还需要注意的点就是如果执行一个脚本,最好将脚本加入到当前的工作空间中,如果不想手动添加到工作空间,则需要使用 addpath 命令添加脚本所在的工作目录,然后使用 savepath 命令保存即可。
现在定义一个用于计算和绘制整流正弦波的脚本 abssin.m :
1%Script to calculate and plot a rectified sine wave
2t=linspace(0,10,100);
3y=abs(sin(t)); % get abs
4plot(t,y);
5title('Rectified Sine Wave');
6xlabel('t');
命令行执行此脚本如下,为了让使用者容易得到帮助信息,在每个脚本的头几行写上有关该脚本的注释是一个很好的习惯:
1>> abssin
2
3>> help abssin % 查看脚本的帮助文档,其实就是自己定义的注释内容
4'abssin' is a script from the file ...\OctaveWK\abssin.m
5
6Script to calculate and plot a rectified sine wave
7
8
9Additional help for built-in functions and operators is
10available in the online version of the manual. Use the command
11'doc <topic>' to search the manual index.
12
13Help and information about Octave is also available on the WWW at
14https://www.octave.org and via the help@octave.org mailing list.
假设你使用了很多的脚本之后你会对这些脚本混淆不清。要想知道你拥有哪些脚本,你可以输入 what 命令来获取一个你当前所有的脚本和数据的列表:
Octave 控制语句
使用 Octave 能非常方便地执行向量和矩阵的计算,但是如果要实现更加复杂的功能,还需要引入一些标准的程序语言特性。比如 if…else、if…elseif、switch、for、while 等控制语句。
符号 | 意义 | 例子 |
---|---|---|
== |
等于 | if x == y |
~= |
不等于(区别于常规) | if x ~= y |
> |
大于 | if x > y |
>= |
大于等于 | if x >= y |
< |
小于 | if x < y |
<= |
小于等于 | if x <= y |
& |
与 | if x== 1 & y>2 |
| | 或 | if x==1 | y>2 |
~ |
非 | x ~= y |
在命令行界面中执行 if 语句时,Octave 会等到你输入 end 语句之后才执行整个表达式。 |
控制语句中又很多的逻辑表达式——表达式根据相应条件具有真或者假的属性。在 Octave 中,逻辑表达式返回值根据真或者假分别返回 1 或者 0,这与C语言一样,0表示假,非0表示真。
下面是 switch 控制的用法,是不是很简单?
1>> switch a
2case 0
3disp('a is zero');
4case 1
5disp('a is one');
6otherwise
7disp('a is not a binary digit');
8end
9a is one
10>>
很多场景下都是向量或者矩阵运算,但是仍会存在少数情况需要 for 或者 while 来搞定:
1>> for n=1:5
2nf(n)=factorial(n);
3end
4>> disp(nf)
5 1 2 6 24 120
6
7>> n = 5;
8>> while n > 0
9n=n-1
10end
11n = 4
12n = 3
13n = 2
14n = 1
15n = 0
16>>
一下篇文章将介绍 Octave 的函数定义,多返回值的函数,矩阵与向量的常见计算方法以及更多的绘图技巧。