Объявление вектора в Matlab, размер которого мы не знаем

Предположим, мы запускаем бесконечный цикл for в MATLAB и хотим сохранить повторяющиеся значения в векторе. Как мы можем объявить вектор, не зная его размера?

z=??
for i=1:inf
    z(i,1)=i;
    if(condition)%%condition is met then break out of the loop
        break;
    end;
end;

person Kivtas    schedule 15.08.2018    source источник
comment
Помните, что все в Matlab считается матрицей. И инициализация не требуется (иногда необходима, но не обязательна). Так что просто используйте свою матрицу и продолжайте увеличивать индексы!   -  person PhoenixBlue    schedule 15.08.2018
comment
@Sardar: я бы сказал, что они рекомендуют против этого. «Устаревший» означает, что они удалят эту функцию в будущем. Они не объявляли о таких планах, и на самом деле несколько лет назад они сделали увеличение размера вектора в цикле более эффективным, разъединив размер хранилища и размер вектора — в настоящее время добавление одного элемента удваивает размер хранилища, так что последующее добавление не требует перераспределения.   -  person Cris Luengo    schedule 15.08.2018
comment
@Sardar: о, извините, вы написали «устарело», я неправильно прочитал это как «устарело» — извините! :)   -  person Cris Luengo    schedule 15.08.2018


Ответы (5)


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

При этом использование ключевого слова end — лучший вариант для расширения массивов одним элементом:

z = [];
for ii = 1:x
    z(end+1, 1) = ii; % Index to the (end+1)th position, extending the array
end

Вы также можете объединить результаты предыдущих итераций, это, как правило, медленнее, поскольку у вас есть переменная присваивания с обеих сторон оператора равенства.

z = [];
for ii = 1:x
    z = [z; ii];
end

Садар прокомментировал прямое индексирование границ (как предполагают другие ответы) обесценивается MathWorks, я не уверен в источнике для этого.


Если ваши вычисления condition отделены от выходных вычислений, вы можете сначала получить требуемый размер

k = 0;
while ~condition
    condition = true; % evaluate the condition here
    k = k + 1;
end

z = zeros( k, 1 ); % now we can pre-allocate
for ii = 1:k
    z(ii) = ii; % assign values
end
person Wolfie    schedule 15.08.2018
comment
Конкатенация, как в z = [z; ii], медленнее не потому, что она повторяет z, а потому, что она не позволяет MATLAB правильно управлять памятью. Вы получаете операцию O (n ^ 2), тогда как первый метод - O (n log n). - person Cris Luengo; 15.08.2018
comment
Спасибо @Chris, я думаю, что, вероятно, поленился объяснить. Я понял, что проблемы с управлением памятью связаны с тем, что переменная назначения была слева и справа от = - я не имел в виду, что z дублируется. Думаю, мы на одной странице (?) - person Wolfie; 15.08.2018
comment
Я думаю, что в этом случае MATLAB создает новый массив и копирует в него z и новые данные. Другая форма просто расширяет тот же массив, что в большинстве случаев можно сделать с нулевой стоимостью, потому что MATLAB внутренне удваивает размер массива, когда ему нужно расти. - person Cris Luengo; 15.08.2018

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

maxNumIterations = 12345;
myVector = zeros(maxNumIterations, 1);

for n = 1:maxNumIterations
    myVector(n) = someFunctionReturningTheDesiredValue(n);

    if(condition)
        vecLength = n;
        break;
    end
end

% Resize the vector to the length that has actually been filled
myVector = myVector(1:vecLength);

Кстати, я бы посоветовал вам НЕ привыкать использовать i в качестве индекса в программах Matlab, так как это будет маскировать воображаемую единицу i. При этом я столкнулся с некоторыми неприятными ошибками в сложных вычислениях внутри циклов, поэтому я бы посоветовал просто взять n или любую другую букву по вашему выбору в качестве имени переменной индекса цикла, даже если вы не имеете дело со сложными значениями в своем функции ;)

person PluginPenguin    schedule 15.08.2018

Вы можете просто объявить пустую матрицу с помощью

z = []

Это создаст матрицу 0x0, размер которой будет изменяться при записи в нее данных. В вашем случае он вырастет до вектора ix1.

Имейте в виду, что это намного медленнее, чем предварительная инициализация вашего вектора с помощью функции zeros(dim,dim). Поэтому, если есть какой-либо способ определить максимальное значение i, вы должны инициализировать его с помощью z = zeros(i,1)

привет, Саймон

person Simon    schedule 15.08.2018

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

z = [];
for i = 1:Inf
 z(i) = i;
 if (condition)
    break;
 end
end

Однако это выглядит неприятно (и выдает предупреждение: Предупреждение: индекс цикла FOR слишком велик. Усекается до 9223372036854775807), я бы сделал здесь некоторое время (правда) или само условие и увеличил его вручную.

z = [];
i = 0;
while !condition
 i=i+1;
 z[i]=i;
end

И/или если ваш пример действительно то, что вам нужно в конце, замените повторное создание массива чем-то вроде:

while !condition
 i=i+1;
end
z = 1:i;
person Christian Heigele    schedule 15.08.2018

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

Если время обработки не является проблемой:

Тогда что-то вроде упомянутого @Wolfie будет достаточно хорошим. На каждой итерации длина массива будет увеличиваться, а именно:

z = [];
for ii = 1:x
    %z = [z; ii];
    z(end+1) = ii % Best way
end

Если время обработки является проблемой:

Если время обработки является большим фактором, и вы хотите, чтобы оно работало как можно более плавно, вам необходимо предварительно выделить. Если у вас есть приблизительное представление о максимальном количестве итераций, которые будут выполняться, вы можете использовать предложение @PluginPenguin. Но все еще может быть изменение достижения этого заданного предела, что сломает (или сильно замедлит) программу.

Мое предложение:

Если ваш цикл работает бесконечно, пока вы его не остановите, вы можете время от времени изменять размер. По сути, увеличивайте размер по мере продвижения, но делайте это только время от времени. Например каждые 100 петель:

z = zeros(100,1);
for i=1:inf
    z(i,1)=i;

    fprintf("%d,\t%d\n",i,length(z)); % See it working

    if i+1 >= length(z)  %The array as run out of space
        %z = [z; zeros(100,1)];   % Extend this array (note the semi-colon)
        z((length(z)+100),1) = 0; % Seems twice as fast as the commented method
    end

    if(condition)%%condition is met then break out of the loop
        break;
    end;
end

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

Изменить:

Как любезно упомянул @Cris, MATLAB уже делает то, что я предложил внутри. Это делает два моих комментария совершенно неверными. Так что лучше всего будет следовать тому, что сказали @Wolfie и @Cris:

z(end+1) = i

Надеюсь это поможет!

person Hein Wessels    schedule 15.08.2018
comment
Не делайте z = [z; ii], вместо этого делайте z(end+1) = ii. Это позволит MATLAB осуществлять правильное управление памятью и, следовательно, работать намного быстрее — O(n log n) вместо O(n^2). - person Cris Luengo; 15.08.2018
comment
Спасибо за чаевые! Я обновил свой ответ. А также изменить способ расширения массива с произвольным количеством элементов на z((length(z)+100),1) = 0. - person Hein Wessels; 15.08.2018
comment
Это именно то, что MATLAB делает внутри, когда вы делаете z(end+1)=ii, за исключением того, что он удваивает размер массива. См. stackoverflow.com/q/48351041/7328782 эксперимент, демонстрирующий это. - person Cris Luengo; 15.08.2018