Поиск…


Элементные операции

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. два массива имеют одинаковые размеры, кроме одного
  2. и несогласованный размер является одноточечным (т.е. имеет размер 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 неявное расширение обеспечивает более быструю скорость выполнения, лучшее использование памяти и улучшенную читаемость кода.


Связанные чтения:

Получить значение функции из двух или более аргументов

Во многих приложениях необходимо вычислить функцию двух или более аргументов.

Традиционно мы используем 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 . Таким образом, мы должны выбрать либо память, либо скорость.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow