Добавление вектора к пустой матрице MATLAB

У меня есть код MATLAB, вставляющий n-мерные точки (n > 1) в матрицу (myPointMatrix), и у меня есть мысли о том, как вставить первую точку.

В настоящий момент программа проверяет размер myPointMatrix перед вставкой точки. Если он равен 1x1, myPointMatrix устанавливается равным текущей точке. В противном случае добавляется текущая точка. Это if -statement имеет значение true только один раз, но оценивается каждый раз, когда я вставляю точку, что очень часто.

Удаление if и попытка добавления к myPointMatrix делает MATLAB понятным образом жалуется на несогласованность размеров матрицы. Удаление и if -statement и инициализация myPointMatrix = 0 заставляет MATLAB находить myPointMatrix undefined. Также понятно.

Как инициализировать myPointMatrix, чтобы удалить элемент if -statement? Или есть другое умное решение?

myPointMatrix = 0;
for x=0:limit
    for y=0:limit
        for z=0:limit
            tempPoint = [x y z];
            if (length(myPointMatrix) == 1)
                myPointMatrix = tempPoint;
            else
                myPointMatrix = [myPointMatrix; tempPoint];
            end
        end
    end
end

Ответ 1

Используйте myPointMatrix = []; для инициализации матрицы.

Чем больше myPointMatrix, тем медленнее будет добавление. Он становится медленнее и медленнее, поскольку каждый раз, когда вы добавляете точку, matlab выделяет новую матрицу нового размера и копирует информацию из вашей старой матрицы + вашу новую точку в новую матрицу.

Затем лучше инициализировать myPointMatrix с его окончательным размером и вставить точки в заданные позиции в матрице.

Ответ 2

Существует несколько способов добавления матрицы или вектора к любой матрице, пустой или нет. Многое зависит от размера матрицы и того, как часто вы будете делать добавление. (Обратите внимание, что разреженные матрицы - совершенно другое животное. Их нужно иметь дело отдельно.)

Простая схема будет использовать конкатенацию. Например, я создам случайный массив. Хотя я знаю, что один вызов rand будет правильным решением здесь, я делаю это только для сравнения.

n = 10000;
tic
A = [];
for i = 1:n
  Ai = rand(1,3);
  A = [A;Ai];
end
toc

Elapsed time is 9.537194 seconds.

Посмотрите, что требуемое время было достаточно высоким, гораздо больше, чем если бы я просто позвонил rand напрямую.

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.

Проблема с любой операцией append вообще заключается в том, что 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.

Хотя это намного лучше, чем динамически выращенный массив, он все еще FAR хуже, чем векторное использование rand. Поэтому, когда это возможно, используйте векторизованную форму таких функций.

Проблема в том, что иногда вы просто не знаете, сколько элементов вы получите. Есть еще несколько трюков, которые можно использовать, чтобы избежать неприятного квадратичного роста.

Один трюк заключается в том, чтобы угадать конечный размер A. Теперь используйте индексирование для вставки новых значений в A, но внимательно следите за тем, когда новые записи будут разливаться по границам A. Когда это будет произойдет, DOUBLE размер A, добавив один большой блок нулей до конца. Теперь вернемся к индексированию новых элементов в A. Держите отдельное количество того, сколько элементов было добавлено. В самом конце этого процесса удалите неиспользуемые элементы. Это позволяет избежать значительного негативного квадратичного поведения, поскольку только несколько шагов добавления будут выполнены. (Помните, что вы удваиваете размер A, когда вы должны добавить append.)

Второй трюк - использовать указатели. В то время как 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.

Для этого потребовалось меньше времени, чем увеличенный массив. Зачем? Мы только строили массив указателей на ячейки. Хорошая вещь об этом заключается в том, что каждый шаг добавления имеет переменное количество строк, он по-прежнему хорошо работает.

Проблема с массивом ячеек, не слишком ли эффективна при добавлении MILLIONS элементов. Это все-таки квадратичная операция, потому что мы увеличиваем массив указателей на каждом шаге.

Решение этой проблемы заключается в использовании амальгамы двух стилей, показанных выше. Таким образом, определите каждую ячейку массива ячеек, размер которой будет умеренно большим. Теперь используйте индексирование для заполнения новых строк A в ячейке. Когда текущая ячейка должна быть увеличена на следующий шаг добавления, просто добавьте новую ячейку в массив ячеек.

Несколько лет назад эта дискуссия возникла в группе новостей MATLAB, и было предложено несколько решений в этом направлении. Я разместил решения growdata и growdata2 как файлы на центральном файловом Exchange MATLAB. 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, поэтому дескриптор функции теперь быстрее.

Достоинством этих схем является то, что они не будут обладать квадратичным штрафом за производительность, одновременно допуская миллионы шагов добавления.

Хорошо, это, безусловно, больше информации, чем было первоначально запрошено, когда был задан вопрос. Возможно, кто-то получит что-то из этого, хотя.

Ответ 3

Ваш лучший вариант - предварительно выделить матрицу и использовать переменную цикла. Это должно быть значительно быстрее.

limit = 9;
myPointMatrix = nan((limit+1)^3,3);

loopVar = 1;
for x=0:limit
    for y=0:limit
        for z=0:limit
            myPointMatrix(loopVar,:) = [x y z];
            loopVar = loopVar + 1;
        end
    end
end

Ответ 4

Я считаю, что решение, которое вы ищете, - это инициализировать myPointMatrix матрицей с 0 строками и тремя столбцами, т.е.

myPointMatrix = zeros(0, 3);

Тогда первое присваивание

myPointMatrix = [myPointMatrix; tempPoint];

будет работать правильно, а также последующие. Аналогичным способом записи присваивания является

myPointMatrix(end+1,:) = tempPoint;

Однако имейте в виду, что рост такой матрицы неэффективен, и, как говорит АннаР, инициализация myPointMatrix с окончательным размером ifs, если известна, является лучшим решением.

Ответ 5

Это то, что вам нужно

myPointMatrix=[];
for x=0:limit
for y=0:limit
for x=0:limit
  myPointMatrix(:,end+1)=[x y z];
end
end
end

но только в том случае, если вы выполняете некоторую нелинейную операцию с [x y z], прежде чем назначать ее. Если нет, вы можете написать вышеприведенные строки следующим образом:

myPointMatrix=[];
myPointMatrix(1,:)=kron([1:limit],ones(1,limit^2));
myPointMatrix(2,:)=kron([1:limit^2],ones(1,limit));
myPointMatrix(3,:)=kron(ones(1,limit^2),[1:limit]);

Вышеописанное полностью векторизовано, хотя можно хотеть edit kron.m и заменить некоторый find на logical... но вы можете сделать это сами, я полагаю...: D

Ответ 6

%appending to matlab array "f":

lfg=[697 770 852 941];
hfg=[1209 1336 1477];
f=[];
for i=1:4,
    for j=1:3,
        %f = [ f [lfg(i);hfg(j)] ];
        append( f , [lfg(i);hfg(j)] );
    end
end
f