Есть несколько способов добавить матрицу или вектор к любой матрице, пустой или нет. Многое зависит от размера матрицы и от того, как часто вы будете делать добавление. (Обратите внимание, что разреженные матрицы - это совершенно другое животное. С ними нужно разбираться отдельно.)
Простая схема будет использовать конкатенацию. Например, я создам случайный массив. Хотя я знаю, что один вызов rand будет правильным решением, я делаю это только для сравнения.
n = 10000;
tic
A = [];
for i = 1:n
Ai = rand(1,3);
A = [A;Ai];
end
toc
Elapsed time is 9.537194 seconds.
Понимаете, необходимое время было достаточно высоким, гораздо больше, чем я только что позвонил Ранду напрямую.
tic,rand(n,3);toc
Elapsed time is 0.008036 seconds.
Другие способы добавления аналогичны во времени. Например, вы можете добавить индексирование тоже.
A = [];
A(end+1,:) = rand(1,3);
A
A =
0.91338 0.63236 0.09754
По времени это будет похоже на добавление через конкатенацию. Интересный факт, который нужно понять, заключается в том, что добавление новых строк в массив несколько отличается от добавления новых столбцов. Добавление строки занимает немного больше времени, чем столбца. Это связано с тем, как элементы хранятся в MATLAB. Добавление новой строки означает, что элементы фактически должны быть перемешаны в памяти.
A = zeros(10000,3);
B = zeros(3,10000);
tic,for i = 1:100,A(end+1,:) = rand(1,3);end,toc
Elapsed time is 0.124814 seconds.
tic,for i = 1:100,B(:,end+1) = rand(3,1);end,toc
Elapsed time is 0.116209 seconds.
Проблема с любой операцией добавления заключается в том, что MATLAB должен перераспределять память, необходимую для A, и делать это КАЖДЫЙ раз, когда матрица увеличивается в размере. Так как размер A растет линейно, общее требуемое время растет квадратично с n. Таким образом, если бы мы удвоили размер n, то для динамически растущего A потребуется четыре раза больше времени. Именно из-за этого квадратичного поведения люди советуют вам предварительно распределять массивы MATLAB, когда они будут динамически расти. Фактически, если вы посмотрите на флаги mlint в редакторе, MATLAB предупредит вас, когда увидит, что это происходит.
Лучшим решением, если вы знаете конечный размер A, является предварительное выделение A для его окончательного размера. Тогда просто индексируйте.
tic
A = zeros(n,3);
for i = 1:n
A(i,:) = rand(1,3);
end
toc
Elapsed time is 0.156826 seconds.
Хотя это намного лучше, чем динамически растущий массив, он все же на порядок хуже, чем векторизованное использование rand. Поэтому, где это возможно, используйте векторизованную форму таких функций.
Проблема в том, что иногда вы просто не знаете, сколько элементов у вас будет в итоге. Есть еще несколько приемов, которые можно использовать, чтобы избежать неприятного квадратичного роста.
Один трюк состоит в том, чтобы угадать окончательный размер A. Теперь, используйте индексирование, чтобы вставить новые значения в A, но внимательно следите за тем, когда новые записи выйдут за границы A. Когда это только собирается случиться, удвоить размер A, добавив один большой блок нулей в конец. Теперь вернитесь к индексированию новых элементов в A. ведите отдельный подсчет того, сколько элементов было «добавлено». В самом конце этого процесса удалите неиспользуемые элементы. Это позволяет избежать большей части неприятного квадратичного поведения, поскольку только несколько шагов добавления будут выполнены. (Помните, что вы удваиваете размер A, когда должны сделать добавление.)
Второй трюк заключается в использовании указателей. Хотя MATLAB на самом деле не предлагает больших возможностей в отношении указателей, массив ячеек является шагом в этом направлении.
tic
C = {};
for i = 1:n
C{end+1} = rand(1,3);
end
A = cat(1,C{:});
toc
Elapsed time is 3.042742 seconds.
Это заняло меньше времени, чем выращенный массив. Зачем? Мы только строили массив указателей на клетки. Хорошая вещь об этом в том, что если каждый шаг добавления имеет переменное число строк, он все равно работает хорошо.
Проблема с массивом ячеек в том, что он не очень эффективен, когда нужно добавить МИЛЛИОНЫ элементов. В конце концов, это все еще квадратичная операция, потому что мы увеличиваем массив указателей на каждом шаге.
Решением этой проблемы является использование амальгамы двух стилей, показанных выше. Таким образом, определите, что каждая ячейка массива ячеек имеет умеренно большой размер. Теперь используйте индексацию, чтобы вставить новые строки в ячейку. Когда текущая ячейка должна быть увеличена к следующему шагу добавления, просто добавьте новую ячейку в массив ячеек.
Несколько лет назад эта дискуссия возникла в группе новостей MATLAB, и было предложено несколько решений в этом направлении. Я разместил решения growdata & growdata2 в виде файлов на MATLAB Central File Exchange. Growdata2 использовал функциональные маркеры для решения проблемы:
tic
Ahandle = growdata2;
for i = 1:n
Ahandle(rand(1,3))
end
% unpack the object into a normal array
A = Ahandle();
toc
Elapsed time is 1.572798 seconds.
В то время это был несколько более быстрый подход к использованию постоянных переменных.
tic
growdata
for i = 1:n
growdata(rand(1,3))
end
A = growdata;
toc
Elapsed time is 2.048584 seconds.
С тех пор реализация MATLAB значительно улучшила реализацию дескрипторов функций, поэтому дескриптор функций теперь стал быстрее.
Достоинство этих схем в том, что они не будут иметь квадратичного снижения производительности, хотя допускают миллионы шагов добавления.
Ну да, это, безусловно, больше информации, чем первоначально запрашивалось, когда задавался вопрос. Возможно, кто-то что-то из этого получит.