19.2 Вещание ¶
Вещание описывает поведение бинарных операторов и функций Octave, когда их матричные или массивно-векторные операнды или аргументы отличаются по размеру. С версии 3.6.0 Octave автоматически выполняет вещание векторов, матриц и массивов при использовании элементных бинарных операторов и функций. В общих чертах, меньшие массивы «вещаются» по большему массиву, пока не будут иметь совместимую форму. Правило заключается в том, что соответствующие размерности массивов должны
- быть равными, или
- одна из них должна быть равна 1.
В случае, если все размерности равны, вещание не выполняется, и происходит обычная поэлементная арифметика. Для массивов более высоких размерностей, если количество размерностей не совпадает, то отсутствующие размерности считаются равными 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 - это изображение RGB размером [m n 3], и мы хотим умножить каждый цвет на другой скаляр. Следующий код выполняет это с помощью вещания,
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
+= -= .*= ./= .\= .^= &= |= Вот реальный пример силы вещания. Алгоритм Флойда-Уоршелла используется для вычисления кратчайших путей между каждой парой вершин в графе. Примитивное реализация для матрицы смежности графа порядка 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);
Эта операция вещает две матрицы с переставленными размерностями друг на друга во время поэлементного умножения, чтобы получить больший 3-мерный массив, а затем этот массив суммируется по третьей размерности. Немного подумав, можно доказать, что эта операция просто намного более быстрая обычная матричная умножение, c = a*b;.
Примечание по терминологии: «вещание» - это термин, популяризированный числовой средой Numpy в языке программирования Python. В других языках программирования и средах вещание может также быть известно как двоичное расширение размерностей-единиц (BSX, в MATLAB, и истоки названия функции bsxfun), переиспользование (язык программирования R), многопотоковая обработка данных с одним инструкцией (SIMD) или дублирование.
19.2.1 Вещание и устаревший код ¶
Новые правила вещания практически никогда не влияют на код, который работал в предыдущих версиях Octave. Следовательно, весь код, унаследованный из MATLAB, который работал в предыдущих версиях Octave, должен по-прежнему работать без изменений в Octave. Единственное исключение — код, такой как
try c = a.*b; catch c = a.*a; end_try_catch
который, возможно, полагался на матрицы разных размеров, приводящих к ошибке. Поскольку такая операция теперь является допустимым синтаксисом Octave, это больше не приведет к ошибке. Вместо этого следует использовать следующий код:
if (isequal (size (a), size (b))) c = a .* b; else c = a .* a; endif
© 1996–2023 The Octave Project Developers
Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions.
https://docs.octave.org/v9.2.0/Broadcasting.html