Объявление вектора в 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;

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

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

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 или любую другую букву по вашему выбору в качестве имени переменной индекса цикла перехода, даже если вы не имеете дело со сложными значениями в вашем функции;)

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

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

Тогда что-то вроде упомянутого @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

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

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

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

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

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

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

while !condition
 i=i+1;
end
z = 1:i;

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

z = []

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

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

ура, Саймон

Другие вопросы по тегам