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界面,可以参考如下方式:

brew reinstall octave--with-qt

我们通常在命令行界面运行Octave,每个命令依次输入命令行中,并以回车结束。Octave是一门解释型语言,即每个命令通过解释器转化为机器语言。

Octave工作空间

命令行所处的ENV,也就是本次打开Octave的上下文ENV。所以对于Octave有工作空间的概念,一切的函数文件、脚本文件都只对当前的工作空间生效。

通过who命令可以查看本次所处于的工作空间的变量,clear用于清除已经存在的变量,也可以指定变量清除:

octave:1> who
octave:2> a = 10; b = 20; c = a + b;
octave:3> who
Variables visible from the current scope:

a  b  c

octave:4> d = 200;
octave:5> who
Variables visible from the current scope:

a  b  c  d

octave:6> clear
octave:7> who
octave:8> d = 200;
octave:9> clear d
octave:10> who
octave:11> clear
octave:12> a = 10; b = 20; c = a + b;
octave:13> who
Variables visible from the current scope:

a  b  c

octave:14> clear b
octave:15> who
Variables visible from the current scope:

a  c

octave:16> 

帮助命令

如果你不了解某个 Octave 命令的功能或者是你需要找一个特定的函数,Octave 本身强大的帮助系统会很有用。最基本的使用帮助系统的方式就是:help commandname,比如查看clear命令、查看sin函数:

octave:1> help clear
'clear' is a built-in function from the file libinterp/corefcn/variables.cc

 -- clear
 -- clear PATTERN ...
 -- clear OPTIONS PATTERN ...
     Delete the names matching the given PATTERNs thereby freeing
     memory...

octave:2> help sin
'sin' is a built-in function from the file libinterp/corefcn/mappers.cc
 -- sin (X)
     Compute the sine for each element of X in radians.
     See also: asin, sind, sinh...

数据载入&保存

退出Octave的交互式环境的时候,你将丢失你所创建的变量。如果你需要在工作的中途退出 Octave,那么你可以保存当前会话的数据并在之后进行重新载入:

octave:1> a = [1:10];
octave:2> b = [1:0.5:10];
octave:3> save anyname
octave:4> exit

zchanglin@mbp OctaveWK % ls
anyname   octave-workspace
zchanglin@mbp OctaveWK % cat anyname 
# Created by Octave 6.4.0, Thu Jan 27 17:45:40 2022 CST <zchanglin@mbp.local>
# name: a
# type: matrix
# rows: 1
# columns: 10
 1 2 3 4 5 6 7 8 9 10


# name: b
# type: matrix
# rows: 1
# columns: 19
 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

octave:1> load anyname
octave:2> a
a =

    1    2    3    4    5    6    7    8    9   10

octave:3> b
b =

 Columns 1 through 14:

    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

 Columns 15 through 19:

    8.0000    8.5000    9.0000    9.5000   10.0000

octave:4> 

将重新载入之前保存的命名空间,并从你中断的地方重新开始工作。

同样的如果只是想保存某些变量而非全部保存,只需要在上述命令中加入变量名称即可:

octave:1> a = 10; b = 20; c=30;
octave:2> save anyname a b;
octave:3> exit

zchanglin@mbp OctaveWK % octave
GNU Octave, version 6.4.0
.....

octave:1> load anyname;
octave:2> a
a = 10
octave:3> b
b = 20
octave:4> c
error: 'c' undefined near line 1, column 1
octave:5> 

在上述例子中,可能你注意到了结尾加不加分号的区别,加了分号表示不显示本条指令的结果,加了的话就会展示本条指令的结果,这与MATLAB是一致的。

通用计算

启动 & 执行简单计算

Octave 在 UNIX 环境下通过在终端中输入octave来启动,在启动octave 之后,程序一般会显现如下信息:

zchanglin@mbp ~ % octave
GNU Octave, version 6.4.0
Copyright (C) 2021 The Octave Project Developers.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  For details, type 'warranty'.

Octave was configured for "x86_64-apple-darwin19.6.0".

Additional information about Octave is available at https://www.octave.org.

Please contribute if you find this software useful.
For more information, visit https://www.octave.org/get-involved.html

Read https://www.octave.org/bugs.html to learn how to submit bug reports.
For information about changes from previous versions, type 'news'.

octave:1> (2+8)/5 * 2 - 3
ans = 1
octave:2> a = (2+8)/5 * 2 - 3;
octave:3> a
a = 1

各种计算符号的优先级与常规的一致,比如括号有最大优先级,其次为乘方,其次为乘除运算,最后为加减运算。

内建函数与常量

Octave 提供了一系列的常用数学函数,其中的常用部分函数如下所示。像 C++ 中调用函数一样,Octave 通过输入函数名和括号中的输入参数来调用函数,其中自然对数e、圆周率pi 都是Octave已经定义的常量(ans代表上一次的计算结果,就像一个普通的科学计算器一样),例如:

octave:1> sin(90 * pi/180)
ans = 1
octave:2> sin(30 * pi/180)
ans = 0.5000
octave:3> cos(60 * pi/180)
ans = 0.5000
octave:4> pi
ans = 3.1416
octave:5> e
ans = 2.7183
octave:6> log(e)
ans = 1
octave:7> 2 * ans
ans = 2
octave:8> abs(5) + abs(-5)
ans = 10
octave: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 反双曲正弦函数

向量相关的计算

定义向量

构造矩阵或者向量的方法有很多。其中最直接简单的方法就是在一个方括号 [] 中给出其元素,也可以通过已经定义的向量来定义新的向量,中间使用空格隔开或者逗号隔开:

octave:1> a=[6 5 4]
a =

   6   5   4

octave:2> b=[3 7];
octave:3> c = [a 1 3 4 b]
c =

   6   5   4   1   3   4   3   7

octave:4> d = [a,1,3,4]
d =

   6   5   4   1   3   4

octave:5> 

有时候我们需要快速的定义向量,可以使用冒号表达式来定义:

octave:1> a = [5:10] # 定义元素为5-10的向量,默认步长为1
a =

    5    6    7    8    9   10

octave:2> b = [1:0.5:3] # 中间的0.5表示步长
b =

   1.0000   1.5000   2.0000   2.5000   3.0000

octave:3> 

定义矩阵

常规方式定义矩阵:

octave:1> a = [10, 20, 30; 1, 2, 3] # 定义矩阵a
a =

   10   20   30
    1    2    3

octave:2> b = 5:7 # 定义向量b
b =

   5   6   7

octave:3> a = [10, 20, 30; 1, 2, 3; b] # 向量参与矩阵构建
a =

   10   20   30
    1    2    3
    5    6    7

octave: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 之间
octave:3> zeros(3, 3)
ans =

   0   0   0
   0   0   0
   0   0   0

octave:4> ones(2, 2)
ans =

   1   1
   1   1

octave:5> linspace(10, 20, 3)
ans =

   10   15   20

octave:6> logspace(10, 20, 3)
ans =

   1.0000e+10   1.0000e+15   1.0000e+20

octave:7> 

显示大矩阵/向量

Octave 无法用单屏显示一个元素超多的向量或者矩阵,有一种READ MORE的模式,通过 more off 命令关闭,

通过 more on 命令开启;比如现在查看一个1000个元素的向量:

octave:1> v = 1:1000
v =

 Columns 1 through 21:
      1      2      3      4      5      6      7      8      9     10     11     12     13     14     15     16     17     18     19     20     21
 Columns 22 through 42:
     22     23     24     25     26     27     28     29     30     31     32     33     34     35     36     37     38     39     40     41     42
 Columns 43 through 63:
 ......
-- less -- (f)orward, (b)ack, (q)uit
# 按q退出
octave:2> more off

操作向量元素

向量中的元素通过括号() 进行访问,而第一个元素的编号为 1, 而不是像其他编程语言那样从0开始。例如:

octave:1> a = [1:10]
octave:2> a(5)
ans = 5
octave:3> a(5) + a(8)
ans = 13
octave:4> b = a(4:6) # 冒号的表示法同样可以用于声明向量中的元素的范围
b =

   4   5   6

octave:5> length(b) # 求向量b的长度
ans = 3

向量/矩阵的计算

使用Octave来对向量或者矩阵进行计算非常方便,不用像C/C++那样写多重for循环,先来看看向量数乘,对向量中所有元素都除以一个数的操作与乘法类似:

octave:1> a = 2:6
a =

   2   3   4   5   6

octave:2> 2*a
ans =

    4    6    8   10   12

octave:3> 2*a
ans =

    4    6    8   10   12

octave:4> a - 1
ans =

   1   2   3   4   5

octave:5> a + 2
ans =

   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) $$

octave:1> a = 1:5
a =

   1   2   3   4   5

octave:2> b = 6:10
b =

    6    7    8    9   10

octave:3> a.*b # 向量元素相乘
ans =

    6   14   24   36   50
octave:4> a.^2 # 向量元素二次方
ans =

    1    4    9   16   25

octave:7> a = rand(3,4) # 创建3*4随机矩阵
a =

   0.592680   0.824473   0.504916   0.893063
   0.976128   0.249468   0.866887   0.148853
   0.933072   0.228585   0.069300   0.384276

octave:8> b = rand(4,3) # 创建3*4随机矩阵
b =

   0.6803   0.1361   0.1822
   0.5374   0.6651   0.6490
   0.3831   0.5916   0.1651
   0.2554   0.3941   0.9914

octave:9> a * b # 矩阵乘法
ans =

   1.2678   1.2797   1.6118
   1.1682   0.8703   0.6305
   0.8823   0.4715   0.7108

octave:10> a.*b # 矩阵元素相乘
error: product: nonconformant arguments (op1 is 3x4, op2 is 4x3)
octave:11> c = rand(3,4) # 创建3*4随机矩阵
c =

   0.042975   0.462963   0.221173   0.524956
   0.694888   0.486257   0.416091   0.127658
   0.042671   0.760997   0.251399   0.655109

octave:12> a * c # 矩阵乘法
error: operator *: nonconformant arguments (op1 is 3x4, op2 is 3x4)
octave:13> a .* c # 矩阵元素相乘
ans =

   0.025471   0.381701   0.111674   0.468819
   0.678300   0.121305   0.360704   0.019002
   0.039815   0.173952   0.017422   0.251743

Octave绘图初阶

以创建一个以60度为间隔的角度值,并作为 sin 函数的输入作为例子,进行绘图:

octave:1> x=[0:pi/3:2*pi]; # 定义X轴数据集
octave:2> y = sin(x);
octave:3> plot(x,y); # 绘制函数
octave:5> xlabel('Angle'); # X轴标注
octave:6> ylabel('Value'); # Y轴标注
octave:7> title('Graph of y=sin(x)'); # 图表标题
octave:8> grid on; # 开启网格
octave:10> hold; # 图像保持(避免被下一个函数图形覆盖)
octave:11> y2 = sin(x) + 0.5; # 定义另一个函数
octave:12> plot(x,y2); # 绘制函数
octave:13> legend('Sine','Sine+0.5'); # 绘制图例
octave:14> hold;
octave:15> print('./graph1.png','-dpng'); # 打印当前图表到工作区

plot函数其实有更多的选项,主要是设置线型和颜色。例如,使用红色和圆圈来画出之前的图片,输入:

plot(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 :

%Script to calculate and plot a rectified sine wave
t=linspace(0,10,100);
y=abs(sin(t)); % get abs
plot(t,y);
title('Rectified Sine Wave');
xlabel('t');

命令行执行此脚本如下,为了让使用者容易得到帮助信息,在每个脚本的头几行写上有关该脚本的注释是一个很好的习惯:

>> abssin

>> help abssin % 查看脚本的帮助文档,其实就是自己定义的注释内容
'abssin' is a script from the file ...\OctaveWK\abssin.m

Script to calculate and plot a rectified sine wave


Additional help for built-in functions and operators is
available in the online version of the manual.  Use the command
'doc <topic>' to search the manual index.

Help and information about Octave is also available on the WWW at 
https://www.octave.org and via the help@octave.org mailing list.

这是一个经过拖拽形成的3D图 假设你使用了很多的脚本之后你会对这些脚本混淆不清。要想知道你拥有哪些脚本,你可以输入 what 命令来获取一个你当前所有的脚本和数据的列表:

>> what
M-files in directory xxx\Desktop\OctaveWK:

   abssin.m

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 语句之后才执行整个表达式。
>> a = 10; b = 20;
>> if a > b
c = 3
else
c = 4
end
c = 4

控制语句中又很多的逻辑表达式——表达式根据相应条件具有真或者假的属性。在 Octave 中,逻辑表达式返回值根据真或者假分别返回 1 或者 0,这与C语言一样,0表示假,非0表示真。

下面是 switch 控制的用法,是不是很简单?

>>  switch a
case 0
disp('a is zero');
case 1
disp('a is one');
otherwise
disp('a is not a binary digit');
end
a is one
>>

很多场景下都是向量或者矩阵运算,但是仍会存在少数情况需要 for 或者 while 来搞定:

>> for n=1:5
nf(n)=factorial(n);
end
>> disp(nf)
     1     2     6    24   120

>> n = 5;
>> while n > 0
n=n-1
end
n = 4
n = 3
n = 2
n = 1
n = 0
>>

一下篇文章将介绍 Octave 的函数定义,多返回值的函数,矩阵与向量的常见计算方法以及更多的绘图技巧。