Как выполнять векторизацию, не зная, как это делать:
Во-первых, я расскажу только о векторизации первого двойного цикла, вы можете следовать той же логике для второго цикла.
Я попытался показать процесс мышления с нуля, поэтому, хотя окончательный ответ состоит всего из 2 строк, стоит посмотреть, как новичок может попытаться его получить.
Во-первых, я рекомендую «массировать» код в простых случаях, чтобы почувствовать его. Например, я использовал n=3
и v=1:6
и запустил первый цикл один раз, вот как выглядит B
:
[N M]=size(B)
N =
9
M =
27
imagesc(B);
![введите здесь описание изображения](https://i.stack.imgur.com/n0Roh.png)
Итак, вы можете видеть, что мы получаем матрицу, похожую на лестницу, которая довольно регулярна! Все, что нам нужно, это присвоить правильный индекс матрицы правильному значению v
и все.
Есть много способов добиться этого, некоторые из них более элегантны, чем другие. Один из самых простых способов — использовать функцию find
:
pos=[find(B==v(1)),find(B==v(2)),find(B==v(3)),...
find(B==v(4)),find(B==v(5)),find(B==v(6))]
pos =
1 10 19 28 37 46
29 38 47 56 65 74
57 66 75 84 93 102
85 94 103 112 121 130
113 122 131 140 149 158
141 150 159 168 177 186
169 178 187 196 205 214
197 206 215 224 233 242
Приведенные выше значения являются линейными индексами матрицы B
, где были найдены значения v
. Каждый столбец представляет собой линейный индекс конкретное значение v
в B
. Например, все индексы [1 29 57 ...]
содержат значение v(1)
и т. д. Каждая строка полностью содержит v
, поэтому индексы [29 38 47 56 65 74] содержат v=[v(1) v(2) ... v(6)]
. Вы можете заметить, что для каждой строки разница между индексами равна 9, или каждый индекс разделен размером шага N
, а их 6, что равно длине вектора v
(тоже полученного numel(v)
). Для столбцов разница между соседними элементами равна 28, или размер шага равен M+1
.
Нам просто нужно будет присвоить значения v
в соответствующих индексах в соответствии с этой логикой. один из способов - написать каждую "строку":
B([1:N:numel(v)*N]+(M+1)*0)=v;
B([1:N:numel(v)*N]+(M+1)*1)=v;
...
B([1:N:numel(v)*N]+(M+1)*(N-2))=v;
Но это нецелесообразно для больших N-2
, поэтому вы можете сделать это в цикле for, если хотите:
for kk=0:N-2;
B([1:N:numel(v)*N]+(M+1)*kk)=v;
end
Matlab предлагает более эффективный способ получить все индексы сразу, используя bsxfun
(это заменяет цикл for), например:
ind=bsxfun(@plus,1:N:N*numel(v),[0:(M+1):M*(N-1)+1]')
так что теперь мы можем использовать ind
для присвоения v
матрице N-1
раз. Для этого нам нужно «сгладить» ind
в вектор-строку:
ind=reshape(ind.',1,[]);
и соедините v
с самим собой N-1 раз (или сделайте еще N-1 копий v
):
vec=repmat(v,[1 N-1]);
наконец получаем ответ:
B(ind)=vec;
Короче говоря, и записывая все это компактно, мы получаем решение из 2 строк (данный размер B
уже известен: [N M]=size(B)
):
ind=bsxfun(@plus,1:N:N*numel(v),[0:(M+1):M*(N-1)+1]');
B(reshape(ind.',1,[]))=repmat(v,[1 N-1]);
Для n=9
векторизованный код на моей машине на ~850 быстрее. (маленький n
будет менее значимым)
Поскольку полученная матрица в основном состоит из нулей, вам не нужно назначать их полной матрице, вместо этого используйте разреженную матрицу. Вот полный код для этого (довольно похоже):
N=3^(n-1);
M=3^n;
S=sparse([],[],[],N,M);
ind=bsxfun(@plus,1:N:N*numel(v),[0:(M+1):M*(N-1)+1]');
S(reshape(ind.',1,[]))=repmat(v,[1 N-1]);
Для n=10
я мог запустить только код разреженной матрицы (в противном случае нехватка памяти), и на моей машине это заняло ~ 6 секунд.
Теперь попробуйте применить это ко второму циклу...
person
bla
schedule
11.07.2013
j
в качестве имени переменной в Matlab. - person Shai   schedule 11.07.2013