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 может быть полезен для принудительного вещания.
- : 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-D, а затем этот массив суммируется по третьей размерности. Последовательное размышление покажет, что эта операция — это просто гораздо более быстрое обычное матричное умножение, 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–2022 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/v7.2.0/Broadcasting.html