#matlab
#matlab
Вопрос:
Допустим, у меня есть матрица 18×2, в которой строки разделены на блоки. Каждый блок содержит три строки, т.Е. матрица состоит из шести блоков. Первый столбец содержит индексы блоков, которые расположены в порядке возрастания от 1 до 6. Второй столбец содержит реальные данные, которые составляют от 1 до 18 для иллюстративных примеров. Матрица выглядит следующим образом:
mat = [ ...
1, 1;
1, 2;
1, 3;
2, 4;
2, 5;
2, 6;
3, 7;
3, 8;
3, 9;
4, 10;
4, 11;
4, 12;
5, 13;
5, 14;
5, 15;
6, 16;
6, 17;
6, 18]
У меня также есть случайный вектор, состоящий из шести целых элементов в диапазоне от 1 до 6, таких как
perm = [2;4;1;2;4;6]
Теперь мне нужно изменить порядок матрицы в соответствии с порядком перестановки. Новая матрица должна выглядеть следующим образом:
matNew = [ ...
2, 4;
2, 5;
2, 6;
4, 10;
4, 11;
4, 12;
1, 1;
1, 2;
1, 3;
2, 4;
2, 5;
2, 6;
4, 10;
4, 11;
4, 12;
6, 16;
6, 17;
6, 18]
Я получил результат, используя цикл for, в котором я последовательно копировал отдельные блоки в matNew. Однако реальная матрица может быть размером до 10 000 x 15, и мне нужно выполнить эту перестановку от 1000 до 10000 раз и сохранить ее в массиве 3D-array / struct / cell, который лучше всего подходит с точки зрения производительности.
Комментарии:
1. Нет, длина блока может быть установлена пользователем, т. Е. Она не всегда равна 3. Второй столбец может содержать любое плавающее число. Я просто добавляю целые числа по возрастанию для иллюстрации.
2. Пожалуйста, подумайте о принятии одного из ответов, если они решили ваш вопрос. Это флажок в левой части ответа. Спасибо! 🙂
Ответ №1:
Вы можете использовать разреженную матрицу для хранения индексов строк . затем используйте пермь для извлечения индексов нужных элементов . Этот метод работает с разными размерами блоков:
mat = [ ...
1, 1;
1, 2;
1, 3;
2, 4;
2, 5;
2, 6;
3, 7;
3, 8;
3, 9;
4, 10;
4, 11;
4, 12;
5, 13;
5, 14;
5, 15;
6, 16;
6, 17;
6, 18];
perm = [2;4;1;2;4;6];
spr = sparse(1:size(mat,1), mat(:,1),1:size(mat,1));
out = mat(nonzeros(spr(:,perm)),:)
Ответ №2:
Поскольку вы хотите применить много перестановок (хотя на самом деле это не перестановка), самый быстрый способ — разделить каждый блок и объединить их в соответствии с perm
:
% in RESHAPE: 3: # of rows per block, 2: # of columns
matblock = permute(reshape(mat.', 2, 3, []), [2, 3, 1]);
% perm = [2;4;1;2;4;6];
matNew = reshape(matblock(:,perm,:), [], 2, 1); % 2: same as above
Другой способ сделать это выглядит проще:
id = reshape(1:18, 3,[]);
matNew = mat(reshape(id(:, perm), [], 1),:);
Обратите внимание, что в обоих решениях первая команда выполняется один раз в начале, а вторая выполняется при каждом обновлении perm
.
Сравнительный анализ:
Поскольку OP хочет применить несколько перестановок к одному и тому же mat
, я делаю сравнительный анализ таким образом:
Все части кода, которые не имеют ничего общего с perm
, выполняются один раз. Задействованные части perm
выполняются 100000 раз для предоставленных mat
:
------------------- With Erfan's first solution:
Elapsed time is 3.979285 seconds.
------------------- With Erfan's second solution:
Elapsed time is 4.245531 seconds.
------------------- With Stewie's solution:
Elapsed time is 6.998606 seconds.
------------------- With rahnema1's solution
Elapsed time is 8.278994 seconds.
Обновление по бенчмаркингу:
Я попробовал другой бенчмаркинг, на этот раз с 10000 x 15 mat
, с 10000 запусками таким образом:
%%%%%%%%%%%%%%%% common code which runs once
mat = rand(10000, 15);
mat(:, 1) = reshape(repmat(1:2000, 5, 1), [], 1);
n = 5; % rows each block
perm = randi(2000, 6, 1);
spr = sparse(1:size(mat,1), mat(:,1),1:size(mat,1));
matblock = permute(reshape(mat.', 15, n, []), [2, 3, 1]);
id = reshape(1:10000, n, []);
%%%%%%%%%%%%%%%%
turn = 10000;
disp('------------------- With Erfan''s first solution:')
tic, for ii = 1:turn, Native; end, toc
disp('------------------- With Erfan''s second solution:')
tic, for ii = 1:turn, Native2; end, toc
disp('------------------- With Stewie''s solution:')
tic, for ii = 1:turn, Alter; end, toc
disp('------------------- With rahnema1''s solution:')
tic, for ii = 1:turn, Alter2; end, toc
И это сценарии:
%%% Native
matNew1 = reshape(matblock(:,perm,:), [], 15, 1);
%%% Native2
matNew2 = mat(reshape(id(:, perm), [], 1),:);
%%% Alter
perm_rep = bsxfun(@plus, n*(perm-1), 1:n).';
matNew3 = mat(perm_rep, :);
%%% Alter2
matNew4 = mat(nonzeros(spr(:,perm)),:);
И вот результаты моего сравнительного анализа. На самом деле здесь не видно большой разницы, если только не нужно постоянно выполнять это вычисление. В противном случае это просто вопрос предпочтения, какой код использовать.
------------------- With Erfan's first solution:
Elapsed time is 0.502261 seconds.
------------------- With Erfan's second solution:
Elapsed time is 0.495179 seconds.
------------------- With Stewie's solution:
Elapsed time is 0.805442 seconds.
------------------- With rahnema1's solution:
Elapsed time is 0.529585 seconds.
Комментарии:
1. Мне наконец-то удалось правильно написать свой бенчмаркинг. Пожалуйста, проверьте и дайте мне знать, если я делаю что-то не так.
2. Приятно 🙂 Обратите внимание, что это лучше и намного точнее в использовании
timeit
. Вы можете сделать это следующим образом:f = @() myfun(matblock, perm); timeit(f)
. Обратите внимание, что скрипты должны быть функциями, а входные данные вводятся непосредственно при созданииf
. Анонимная функция сама не может принимать какие-либо входные данные.3. Насколько я вижу, ваши решения работают правильно. Однако у меня есть две проблемы, которые замедляют все вычисления: 1. Когда вы повторяете вычисление 10000 раз, вектор ‘perm’ должен случайным образом меняться при каждом запуске. 2. Мне нужно сохранить все 10000 матриц для дальнейшей обработки. Это требует значительного времени, по крайней мере, если вы помещаете его в 3D-массив при запуске цикла for. Итак, каков наиболее эффективный способ хранения данных? Я думаю, что не имеет значения, в каком формате (3D, массив ячеек, структура).
4. 1. Для генерации
perm
s вы можете использоватьrandi
, как я. Конечно, вам нужно будет поместить это в цикл. То, что мы сделали, было просто сравнительным анализом. 2. Зависит от того, что вы хотите сделать с результатом. Взгляните на этот ответ.
Ответ №3:
Примечание: этот подход быстрее на милю, если у вас каждый раз разные матрицы, но занимает примерно в два раза больше времени, чем код Эрфана, если только perm
изменяется.
Вы можете сделать это, создав список индексов для каждого из perm
элементов, используя bsxfun
. n
— количество элементов в индексе. Затем вы используете это для создания нужного списка:
perm_rep = bsxfun(@plus, n*(perm-1), 1:n).'
matNew = mat(perm_rep, :)
matNew =
2 4
2 5
2 6
4 10
4 11
4 12
1 1
1 2
1 3
2 4
2 5
2 6
4 10
4 11
4 12
6 16
6 17
6 18
Сравнительный анализ:
Следующий сравнительный анализ выполняется со следующими данными:
sz_mat = size(mat)
num_perms = numel(perm)
sz_mat =
18000 20
num_perms =
1000
Функции адаптированы для размещения в функции с различными размерами матрицы и рассчитаны по времени timeit
. Описанный выше подход примерно в 200 раз быстрее, чем подход Эрфана, и в 3 раза быстрее, чем у rahnema1.
f = @() erfan(mat, perm);
g = @() stewie(mat, perm);
h = @() rahnema1(mat, perm);
isequal(f(), g(), h());
fprintf('Erfan: %f snStewie: %f snrahnema1: %f sn',timeit(f), timeit(g), timeit(h));
Erfan: 0.003353 s
Stewie: 0.000139 s
rahnema1: 0.000620 s