MATLAB Language
Векторизация
Поиск…
Элементные операции
MATLAB поддерживает (и поощряет) векторизованные операции над векторами и матрицами.
Например, предположим, что мы имеем A
и B
, две n
m
матрицы и хотим, чтобы C
являлось элементарным произведением соответствующих элементов (т. Е. C(i,j) = A(i,j)*B(i,j)
).
Не-векторизованный способ с использованием вложенных циклов выглядит следующим образом:
C = zeros(n,m);
for ii=1:n
for jj=1:m
C(ii,jj) = A(ii,jj)*B(ii,jj);
end
end
Однако векторизованный способ выполнения этого заключается в использовании элементарного оператора .*
:
C = A.*B;
- Для получения дополнительной информации об умножении элемента в MATLAB см. Документацию о
times
. - Дополнительные сведения о различиях между операциями массива и матрицы см. В разделе « Массивные и матричные операции» в документации MATLAB.
Сумма, средняя, prod & co
Для случайного вектора
v = rand(10,1);
если вы хотите получить сумму своих элементов, НЕ используйте цикл
s = 0;
for ii = 1:10
s = s + v(ii);
end
но использовать векторизованную способность функции sum()
s = sum(v);
Функции, такие как sum()
, mean()
, prod()
и другие, имеют возможность работать непосредственно по строкам, столбцам или другим измерениям.
Например, учитывая случайную матрицу
A = rand(10,10);
среднее значение для каждого столбца равно
m = mean(A,1);
среднее значение для каждой строки равно
m = mean(A,2)
Все вышеперечисленные функции работают только с одним измерением, но что, если вы хотите суммировать всю матрицу? Вы можете использовать:
s = sum(sum(A))
Но что, если есть ND-массив? применяя sum
по sum
на sum
... не похоже на лучший вариант, вместо этого используйте оператор :
vectorize для вашего массива:
s = sum(A(:))
и это приведет к одному числу, которое является суммой всего вашего массива, не имеет значения, сколько у него размеров.
Использование bsxfun
Довольно часто причина, по которой код был написан в цикле for
заключается в вычислении значений из «ближайших». Функция bsxfun
часто может использоваться для этого более сжатым способом.
Например, предположим, что вы хотите выполнить операцию по столбцу на матрице B
, вычитая из нее среднее значение каждого столбца:
B = round(randn(5)*10); % Generate random data
A = zeros(size(B)); % Preallocate array
for col = 1:size(B,2); % Loop over columns
A(:,col) = B(:,col) - mean(B(:,col)); % Subtract means
end
Этот метод неэффективен, если B
большой, часто из-за того, что MATLAB должен перемещать содержимое переменных в памяти. Используя bsxfun
, можно выполнить одну и ту же работу аккуратно и легко только в одной строке:
A = bsxfun(@minus, B, mean(B));
Здесь @minus
является дескриптором функции для оператора minus
( -
) и будет применяться между элементами двух матриц B
и mean(B)
. Возможны и другие функции, даже пользовательские.
Затем предположим, что вы хотите добавить вектор строки v
в каждую строку в матрице A
:
v = [1, 2, 3];
A = [8, 1, 6
3, 5, 7
4, 9, 2];
Наивный подход - использовать цикл ( не делайте этого ):
B = zeros(3);
for row = 1:3
B(row,:) = A(row,:) + v;
end
Другим вариантом будет репликация v
с помощью repmat
( не делайте этого ):
>> v = repmat(v,3,1)
v =
1 2 3
1 2 3
1 2 3
>> B = A + v;
Вместо этого используйте bsxfun
для этой задачи:
>> B = bsxfun(@plus, A, v);
B =
9 3 9
4 7 10
5 11 5
Синтаксис
bsxfun(@fun, A, B)
где @fun
- одна из поддерживаемых функций, а два массива A
и B
соответствуют двум условиям ниже.
Имя bsxfun
помогает понять, как работает эта функция, и это означает, что B inary FUN ction с S ingleton e X pansion. Другими словами, если:
- два массива имеют одинаковые размеры, кроме одного
- и несогласованный размер является одноточечным (т.е. имеет размер
1
) в любом из двух массивов
то массив с размером Singleton будет расширен, чтобы соответствовать размерности другого массива. После расширения двоичная функция применяется по-разному на двух массивах.
Например, пусть A
- массив M
-by- N
-by K
а B
- массив M
-by- N
. Во-первых, их первые два измерения имеют соответствующие размеры. Во-вторых, A
имеет K
слоев, в то время как B
имеет неявно только 1
, поэтому он является одиночным. Все условия выполнены, и B
будет реплицироваться в соответствии с 3-м измерением A
На других языках это обычно называют вещанием и происходит автоматически в Python (numpy) и Octave.
Функция @fun
должна быть двоичной функцией, означающей, что она должна принимать ровно два входа.
замечания
Внутри bsxfun
не реплицирует массив и выполняет эффективный цикл.
Логическая маскировка
MATLAB поддерживает использование логической маскировки для выполнения выбора по матрице без использования для циклов или операторов if.
Логическая маска определяется как матрица, состоящая только из 1
и 0
.
Например:
mask = [1 0 0; 0 1 0; 0 0 1];
является логической матрицей, представляющей единичную матрицу.
Мы можем создать логическую маску, используя предикат для запроса матрицы.
A = [1 2 3; 4 5 6; 7 8 9];
B = A > 4;
Сначала мы создаем матрицу 3х3, A
, содержащие цифры от 1 до 9. Затем запрос для значений, которые больше , чем 4 и сохранить результат в виде новой матрицы под названием A
B
.
B
- логическая матрица формы:
B = [0 0 0
0 1 1
1 1 1]
Или 1
когда предикат A > 4
был истинным. И 0
когда это было ложно.
Мы можем использовать логические матрицы для доступа к элементам матрицы. Если для выбора элементов используется логическая матрица, в матрице, которую вы выбираете, будут выбраны индексы, в которых 1
появляется в логической матрице.
Используя тот же B
сверху, мы могли бы сделать следующее:
C = [0 0 0; 0 0 0; 0 0 0];
C(B) = 5;
Это выберет все элементы C
где B
имеет 1
в этом индексе. Затем эти индексы в C
устанавливаются в 5
.
Наш C
теперь выглядит так:
C = [0 0 0
0 5 5
5 5 5]
Мы можем уменьшить сложные кодовые блоки, содержащие if
и for
, используя логические маски.
Возьмите не-векторизованный код:
A = [1 3 5; 7 9 11; 11 9 7];
for j = 1:length(A)
if A(j) > 5
A(j) = A(j) - 2;
end
end
Это можно сократить с помощью логической маскировки до следующего кода:
A = [1 3 5; 7 9 11; 11 9 7];
B = A > 5;
A(B) = A(B) - 2;
Или даже короче:
A = [1 3 5; 7 9 11; 11 9 7];
A(A > 5) = A(A > 5) - 2;
Неявное расширение массива (широковещательная передача) [R2016b]
MATLAB R2016b характеризовался обобщением своего механизма скалярного расширения 1 , 2 , чтобы также поддерживать определенные элементарные операции между массивами разных размеров, если их размерность совместима. Операторы, которые поддерживают неявное расширение, равны 1 :
- Элементно-арифметические операторы:
+
,-
.^
.*
.^
,./
.^
.\
. - Операторы отношения:
<
,<=
,>
,>=
,==
,~=
. - Логические операторы:
&
,|
,xor
. - Битовые функции:
bitand
,bitor
,bitxor
. - Элементарные математические функции:
max
,min
,mod
,rem
,hypot
,atan2
,atan2d
.
Вышеупомянутые двоичные операции разрешены между массивами, если они имеют «совместимые размеры». Размеры считаются «совместимыми», когда каждое измерение в одном массиве либо точно равно одной и той же размерности в другом массиве, либо равно 1
. Обратите внимание, что конечные синглтоны (т. Е. Размера 1
) не учитываются MATLAB, хотя теоретически их бесконечное количество. Другими словами - размеры, которые появляются в одном массиве и не отображаются в другом, неявно подходят для автоматического расширения.
Например, в версиях MATLAB до R2016b это произойдет:
>> magic(3) + (1:3)
Error using +
Matrix dimensions must agree.
Принимая во внимание, что начиная с R2016b предыдущая операция будет успешной:
>> magic(3) + (1:3)
ans =
9 3 9
4 7 10
5 11 5
Примеры совместимых размеров:
Описание | 1- й размер массива | 2- й размер массива | Размер результата |
---|---|---|---|
Вектор и скаляр | [3x1] | [1x1] | [3x1] |
Векторы строк и столбцов | [1x3] | [2x1] | [2x3] |
Векторная и 2D матрица | [1x3] | [5x3] | [5x3] |
Матрицы ND и KD | [1x3x3] | [5x3x1x4x2] | [5x3x3x4x2] |
Примеры несовместимых размеров:
Описание | 1- й размер массива | 2- й размер массива | Возможное обходное решение |
---|---|---|---|
Векторы, где размерность является кратной одной и той же размерности в другом массиве. | [1x2] | [1x8] | transpose |
Массивы с размерами, кратными друг другу. | [2x2] | [8x8] | repmat , reshape |
ND, которые имеют правильное количество одноэлементных измерений, но они находятся в неправильном порядке (№ 1). | [2x3x4] | [2x4x3] | permute |
ND, которые имеют правильное количество одноэлементных измерений, но они находятся в неправильном порядке (# 2). | [2x3x4x5] | [5x2] | permute |
ВАЖНЫЙ:
Код, основанный на этом соглашении, НЕ обратно совместим с любыми более старыми версиями MATLAB. Поэтому явный вызов bsxfun
1 , 2 (который достигает такого же эффекта) должен использоваться, если код должен запускаться на старых версиях MATLAB. Если такой проблемы не существует, заметки о выпуске MATLAB R2016 побуждают пользователей переключаться с bsxfun
:
По сравнению с использованием
bsxfun
неявное расширение обеспечивает более быструю скорость выполнения, лучшее использование памяти и улучшенную читаемость кода.
Связанные чтения:
- Документация MATLAB на « Совместимые размеры массивов для основных операций ».
- Широковещательная передача NumPy 1 , 2 .
- Сравнение скорости вычислений с использованием
bsxfun
против неявного расширения массива .
Получить значение функции из двух или более аргументов
Во многих приложениях необходимо вычислить функцию двух или более аргументов.
Традиционно мы используем for
-loops. Например, если нам нужно вычислить f = exp(-x^2-y^2)
(не используйте это, если вам нужно быстрое моделирование ):
% code1
x = -1.2:0.2:1.4;
y = -2:0.25:3;
for nx=1:lenght(x)
for ny=1:lenght(y)
f(nx,ny) = exp(-x(nx)^2-y(ny)^2);
end
end
Но векторная версия более элегантная и быстрая:
% code2
[x,y] = ndgrid(-1.2:0.2:1.4, -2:0.25:3);
f = exp(-x.^2-y.^2);
чем мы можем это визуализировать:
surf(x,y,f)
Примечание1 - Сетки: Обычно хранилище матриц организовано по очереди . Но в MATLAB это столбцовое хранилище, как в FORTRAN. Таким образом, есть два Двойники функции ndgrid
и meshgrid
в MATLAB для реализации двух вышеупомянутых моделей. Чтобы визуализировать функцию в случае meshgrid
, мы можем использовать:
surf(y,x,f)
Примечание2 - Потребление памяти: пусть размер x
или y
равен 1000. Таким образом, нам нужно хранить 1000*1000+2*1000 ~ 1e6
элементов для не-векторизованного кода1 . Но нам нужно 3*(1000*1000) = 3e6
элементов в случае векторизованного кода2 . В 3D-случае (пусть z
имеет тот же размер, что и x
или y
), потребление памяти резко возрастает: 4*(1000*1000*1000)
(~ 32 ГБ для удвоений) в случае векторизованного кода2 против ~1000*1000*1000
(всего ~ 8 ГБ) в случае кода1 . Таким образом, мы должны выбрать либо память, либо скорость.