Добавление вектора к пустой матрице MATLAB - PullRequest
16 голосов
/ 23 апреля 2009

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

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

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

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

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

Ответы [ 6 ]

28 голосов
/ 23 апреля 2009

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

Простая схема будет использовать конкатенацию. Например, я создам случайный массив. Хотя я знаю, что один вызов 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 значительно улучшила реализацию дескрипторов функций, поэтому дескриптор функций теперь стал быстрее.

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

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

14 голосов
/ 23 апреля 2009

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

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

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

3 голосов
/ 23 апреля 2009

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

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
0 голосов
/ 17 февраля 2013
%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
0 голосов
/ 02 апреля 2012

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

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

0 голосов
/ 23 апреля 2009

Я считаю, что решение, которое вы ищете, состоит в инициализации myPointMatrix в матрицу с 0 строками и 3 столбцами, т.е.

myPointMatrix = zeros(0, 3);

Тогда первое назначение

myPointMatrix = [myPointMatrix; tempPoint];

будет работать правильно, как и последующие. Эквивалентный способ написания задания -

myPointMatrix(end+1,:) = tempPoint;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...