Быстрый и эффективный способ создать матрицу из серии продуктов

Ax, Ay, Az: [N-by-N]

B=AA (диадический продукт)

Это значит :

B(i,j)= [Ax(i,j);Ay(i,j);Az(i,j)]*[Ax(i,j) Ay(i,j) Az(i,j)]

B(i,j): матрица 3x3. Один из способов построить B:

N=2;
Ax=rand(N); Ay=rand(N); Az=rand(N);     %# [N-by-N]
t=1;
F=zeros(3,3,N^2);
for  i=1:N
    for j=1:N
        F(:,:,t)= [Ax(i,j);Ay(i,j);Az(i,j)]*[Ax(i,j) Ay(i,j) Az(i,j)];
        t=t+1;                          %# t is just a counter
    end
end
%# then we can write
B = mat2cell(F,3,3,ones(N^2,1));
B = reshape(B,N,N)'; 
B = cell2mat(B);

Есть ли более быстрый способ, когда N велико.

Редактировать:

Спасибо за Ваш ответ. (так быстрее) Положим: N=2; Ах=[1 2;3 4]; Ау=[5 6;7 8]; Аз=[9 10;11 12];

B =

 1     5     9     4    12    20
 5    25    45    12    36    60
 9    45    81    20    60   100
 9    21    33    16    32    48
21    49    77    32    64    96
33    77   121    48    96   144

Выполнить:
??? Ошибка при использовании ==> mtimes Внутренние размеры матрицы должны совпадать.

Если я напишу :P = Ai*Aj; то

B  =

 7    19    31    15    43    71
23    67   111    31    91   151
39   115   191    47   139   231
10    22    34    22    50    78
34    78   122    46   106   166
58   134   210    70   162   254

Это отличается от указанного выше A(:,:,1) отличается от [Ax(1,1) Ay(1,1) Az(1,1)]

Редактировать:

N=100;
Me      :Elapsed time is 1.614244 seconds.
gnovice :Elapsed time is 0.056575 seconds.
N=200;
Me      :Elapsed time is 6.044628 seconds.
gnovice :Elapsed time is 0.182455 seconds.
N=400;
Me      :Elapsed time is 23.775540 seconds.
gnovice :Elapsed time is 0.756682 seconds.
Fast!

rwong: B was not the same.

Редактировать:

После некоторой модификации для моего приложения: кодами gnovice

 1st code :  19.303310 seconds
 2nd code:  23.128920  seconds
 3rd code:  13.363585  seconds

Кажется, что любой вызов функции, такой как ceil, ind2sub ..., замедляет циклы и их следует избегать, если это возможно.

symIndex было интересно! Спасибо.


person Abolfazl    schedule 05.06.2011    source источник


Ответы (2)


Вот довольно простая и общая реализация, в которой используется один цикл for для выполнения линейное индексирование и позволяет избежать работы с трехмерными переменными или изменениями:

%# General solution:
%# ----------------
B = cell(N);
for index = 1:N^2
  A = [Ax(index) Ay(index) Az(index)];
  B{index} = A(:)*A;
end
B = cell2mat(B);

РЕДАКТИРОВАНИЕ №1:

В ответ на дополнительный вопрос о том, как уменьшить количество вычислений, выполняемых для симметричной матрицы B, вы можете использовать следующую модифицированную версию приведенного выше кода:

%# Symmetric solution #1:
%# ---------------------
B = cell(N);
for index = find(tril(ones(N))).'  %'# Loop over the lower triangular part of B
  A = [Ax(index) Ay(index) Az(index)];
  B{index} = A(:)*A;
  symIndex = N*rem(index-1,N)+ceil(index/N);  %# Find the linear index for the
                                              %#   symmetric element
  if symIndex ~= index       %# If we're not on the main diagonal
    B{symIndex} = B{index};  %#   then copy the symmetric element
  end
end
B = cell2mat(B);

Однако в таком случае вы можете получить лучшую производительность (или, по крайней мере, более простой код), отказавшись от линейной индексации и используя два цикла for с индексацией с индексами, например так:

%# Symmetric solution #2:
%# ---------------------
B = cell(N);
for c = 1:N    %# Loop over the columns
  for r = c:N  %# Loop over a subset of the rows
    A = [Ax(r,c) Ay(r,c) Az(r,c)];
    B{r,c} = A(:)*A;
    if r ~= c           %# If we're not on the main diagonal
      B{c,r} = B{r,c};  %#   then copy the symmetric element
    end
  end
end
B = cell2mat(B);

РЕДАКТИРОВАНИЕ № 2:

Второе симметричное решение можно еще больше ускорить, переместив вычисление диагонали за пределы внутреннего цикла (удалив необходимость в условном операторе) и перезаписав A результатом A(:)*A, чтобы мы могли обновить запись симметричной ячейки B{c,r}, используя A вместо B{r,c}. (т.е. обновление одной ячейки содержимым другой, по-видимому, влечет за собой дополнительные накладные расходы):

%# Symmetric solution #3:
%# ---------------------
B = cell(N);
for c = 1:N    %# Loop over the columns
  A = [Ax(c,c) Ay(c,c) Az(c,c)];
  B{c,c} = A(:)*A;
  for r = c+1:N  %# Loop over a subset of the rows
    A = [Ax(r,c) Ay(r,c) Az(r,c)];
    A = A(:)*A;
    B{r,c} = A;
    B{c,r} = A;
  end
end
B = cell2mat(B);

А вот некоторые результаты синхронизации для трех симметричных решений с использованием следующих образцов симметричных матриц Ax, Ay и Az:

N = 400;
Ax = tril(rand(N));     %# Lower triangular matrix
Ax = Ax+triu(Ax.',1);  %'# Add transpose to fill upper triangle
Ay = tril(rand(N));     %# Lower triangular matrix
Ay = Ay+triu(Ay.',1);  %'# Add transpose to fill upper triangle
Az = tril(rand(N));     %# Lower triangular matrix
Az = Az+triu(Az.',1);  %'# Add transpose to fill upper triangle

%# Timing results:
%# --------------
%# Solution #1 = 0.779415 seconds
%# Solution #2 = 0.704830 seconds
%# Solution #3 = 0.325920 seconds

Большое ускорение для решения 3 происходит в первую очередь за счет обновления содержимого ячейки B локальной переменной A вместо обновления одной ячейки содержимым другой. Другими словами, копирование содержимого ячейки с помощью таких операций, как B{c,r} = B{r,c};, влечет за собой больше накладных расходов, чем я ожидал.

person gnovice    schedule 06.06.2011
comment
Большое спасибо, gnovice. Это превосходно! Возможно ли при линейной индексации, чтобы алгоритм вычислял только половину матрицы, когда B симметричен? - person Abolfazl; 07.06.2011

person    schedule
comment
@ user784433: исправлено. Он должен быть поэлементно умножен, в результате чего получится вектор [N^2, 1, 1]. Благодарю. - person rwong; 05.06.2011