广播(Broadcasting)是指 Octave 的二元运算符和函数在其矩阵或数组操作数(参数)大小不同时的行为方式。自 3.6.0 版本起,Octave 在使用逐元素二元运算符和函数时,会自动广播向量、矩阵和数组。广义而言,较小的数组会被"广播"到较大的数组上,直到它们具有兼容的形状。规则是:对应的数组维度必须满足以下条件之一:
如果所有维度都相等,则不发生广播,执行普通的逐元素算术运算。对于高维数组,如果维度数量不同,则缺失的尾部维度被视为 1。当某个维度为 1 时,具有该单一维度的数组会沿该维度复制,直到与另一个数组的维度匹配。例如,考虑
x = [1 2 3;
4 5 6;
7 8 9];
y = [10 20 30];
x + y
如果没有广播,x + y 会因维度不匹配而产生错误。然而,有了广播,其效果相当于执行了以下操作:
x = [1 2 3
4 5 6
7 8 9];
y = [10 20 30
10 20 30
10 20 30];
x + y
⇒ 11 22 33
14 25 36
17 28 39
也就是说,较小的数组(大小为 [1 3])沿单一维度(行数)被复制,直到成为 [3 3]。然而,实际并未发生真正的复制。内部实现通过沿必要维度重用元素来达到预期效果,而无需在内存中复制数据。
两个数组可以相互广播,例如,计算向量元素与其自身的所有成对差值:
y - y'
⇒ 0 10 20
-10 0 10
-20 -10 0
这里,大小为 [1 3] 和 [3 1] 的两个向量都被广播为 [3 3] 的矩阵,然后执行普通的矩阵减法。
广播的一种特殊情况可能为人熟知:当被广播数组的所有维度都为 1 时,即该数组是一个标量。因此,例如,x - 42 和 max (x, 2) 这样的操作就是广播的基本示例。
再看一个高维示例:假设 img 是一幅大小为 [m n 3] 的 RGB 图像,我们希望将每种颜色乘以不同的标量。以下代码通过广播实现这一操作:
img .*= permute ([0.8, 0.9, 1.2], [1, 3, 2]);
注意此处使用了 permute 来匹配 [0.8, 0.9, 1.2] 向量与 img 的维度。
对于未按广播语义编写的函数,可以使用 bsxfun 来强制它们进行广播。
C = bsxfun (f, A, B) ¶将二元函数 f 逐元素应用于两个数组参数 A 和 B,必要时扩展任一输入参数中的单一维度。
f 是一个函数句柄、内联函数或包含要计算的函数名称的字符串。函数 f 必须能够接受两个等长的列向量参数,或一个列向量参数和一个标量。
A 和 B 的维度必须相等或为单一维度。数组的单一维度将被扩展到与另一个数组相同的维度。
广播仅在满足两个广播条件之一时应用。但和通常一样,当两个维度不同且都不为 1 时,广播不适用:
x = [1 2 3
4 5 6];
y = [10 20
30 40];
x + y
这将产生参数不兼容的错误。
除了常见的算术运算外,一些接受两个参数的函数也会进行广播。支持广播的函数和运算符的完整列表如下:
plus +
minus -
times .*
rdivide ./
ldivide .\
power .^
lt <
le <=
eq ==
gt >
ge >=
ne != ~=
and &
or |
atan2
hypot
max
min
mod
rem
xor
+= -= .*= ./= .\= .^= &= |=
下面是一个展示广播威力的实际例子。Floyd-Warshall 算法用于计算图中每对顶点之间的最短路径长度。对于 n 阶图的邻接矩阵,朴素的实现可能如下所示:
for k = 1:n
for i = 1:n
for j = 1:n
dist(i,j) = min (dist(i,j), dist(i,k) + dist(k,j));
endfor
endfor
endfor
对最内层循环进行向量化后,可能变成这样:
for k = 1:n
for i = 1:n
dist(i,:) = min (dist(i,:), dist(i,k) + dist(k,:));
endfor
endfor
使用双向广播后,变成这样:
for k = 1:n dist = min (dist, dist(:,k) + dist(k,:)); endfor
对于一个包含 100 个顶点的图,三种技术的相对时间性能为:朴素代码 7.3 秒,单向量化代码 87 毫秒,完全广播代码 1.3 毫秒。对于一个包含 1000 个顶点的图,向量化需要 11.7 秒,而广播仅需 1.15 秒。因此,通常值得编写具有广播语义的代码以获得更好的性能。
然而,请注意:如果简单的操作即可满足需求,不要盲目使用广播。对于矩阵 a 和 b,考虑以下代码:
c = sum (permute (a, [1, 3, 2]) .* permute (b, [3, 2, 1]), 3);
该操作在逐元素乘法过程中,将两个经过维度置换的矩阵相互广播,从而得到一个更大的三维数组,然后沿第三维对该数组求和。稍加思考便会发现,该操作其实就是快得多的普通矩阵乘法:c = a*b;。
术语说明:"广播"(broadcasting)这个术语是由 Python 编程语言中的 Numpy 数值环境推广开来的。在其他编程语言和环境中,广播也可能被称为二元单例扩展(Binary Singleton Expansion,简称 BSX,在 MATLAB 中使用,也是 bsxfun 函数名称的由来)、回收(Recycling,R 编程语言)、单指令多数据(Single-Instruction Multiple Data,SIMD)或复制(Replication)。
版权所有 © 2024-2026 Octave中文网
ICP备案/许可证号:黑ICP备2024030411号-2