行列やベクトルを任意の行列に追加する方法はいくつかありますが、空でもそうでもありません。多くは、マトリックスのサイズと追加回数によって異なります。 (希薄な行列はまったく別の動物であり、別々に扱う必要があることに注意してください。)
単純な方式では連結を使用します。たとえば、ランダムな配列を作成します。私は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.
任意の追加操作に問題は全くMATLABは、Aのために必要なメモリを再割り当てし、その行列のサイズが大きくなるたびに、しなければならないということです。 Aのサイズは線形に増加するので、必要な全体の時間はnで2次的に増加します。私たちはnのサイズを2倍にしました。動的に成長したAは、構築するのに4倍の時間がかかります。この二次的な振る舞いは、MATLAB配列が動的に拡張されるときに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.
でちょうどインデックスが、それはまだランドのベクトル化の使用よりもはるかに悪いです。可能な限り、このような関数のベクトル化された形式を使用してください。
問題は、最終的にいくつの要素が残っているか分かりません。不快な二次的な成長を避けるために使用できるいくつかのトリックはまだあります。
最後に、Aの最終サイズを推測することです。インデックス作成を使用して新しい値をAに挿入しますが、新しいエントリがAの境界を超えて流出するときは注意してください。起こるには、Aのサイズを倍にして、最後に1つの大きなブロックのゼロを付け加えます。今度は、新しい要素をAに索引付けすることに戻ります。追加された要素の数を別々にカウントしてください。このプロセスの最後に、未使用の要素を削除します。これにより、いくつかの追加ステップしか実行されないので、不快な二次的な振る舞いの多くを回避します。 (あなたが追加をしなければならないときは、Aのサイズを2倍にしていることを覚えておいてください)。
2番目のトリックはポインタを使用することです。 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.
これは、成長したアレイよりも短い時間で完了しました。どうして?私たちは、細胞へのポインタの配列を構築していました。これについての良いことは、各追加ステップに行数が可変であれば、それでもうまくいきます。
セル配列に問題があります。追加する要素が何百万個ある場合は、それほど効率的ではありませんか?なぜなら、各ステップでポインタの配列を増やしているからです。
この問題の解決策は、上記の2つのスタイルのアマルガムを使用することです。したがって、セルアレイの各セルを適度に大きく定義する。今度はインデックス作成を使ってAの新しい行をセルに入れる。現在のセルを次の追加ステップで大きくしなければならない場合は、新しいセルをセル配列に追加するだけです。
数年前、この議論が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では明らかに改善されているため、関数ハンドルがより高速になりました。
これらのスキームの利点は、数百万の追加ステップを可能にしながら、二次的なパフォーマンスのペナルティを持たないことです。
まあ、これは、質問がされたときに最初に要求されたよりも確かに多くの情報です。多分誰かがそれから何かを得るでしょう。
+1最後の文章です。それがMATLABの行列を初期化する最も効率的な方法です。 –