web-dev-qa-db-ja.com

空のMATLAB行列へのベクトルの追加

N次元の点(n> 1)を行列(myPointMatrix)に挿入するMATLABコードがあり、最初の点を挿入する方法について考えています。

現在、プログラムはポイントを挿入する前にmyPointMatrixのサイズをチェックしています。 1x1の場合、myPointMatrixは現在のポイントと等しく設定されます。それ以外の場合は、現在のポイントが追加されます。このif- statementは1回だけ真ですが、ポイントを挿入するたびに評価されますが、これは非常に頻繁です。

ifを削除してmyPointMatrixに追加しようとすると、MATLABはマトリックスの次元が一貫していないことについて不満を言うようになります。 if-ステートメントとmyPointMatrix = 0の初期化の両方を削除すると、MATLABはmyPointMatrixを未定義として検出します。また理解できる。

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
16
AnnaR

使用する myPointMatrix = [];は、行列を初期化します。

myPointMatrixが大きいほど、追加が遅くなります。ポイントを追加するたびに、MATLABは新しいサイズの新しいマトリックスを割り当て、古いマトリックス+新しいポイントから新しいマトリックスに情報をコピーするため、処理速度は遅くなります。

次に、MyPointMatrixをその最終的なサイズで初期化し、行列の所定の位置にポイントを挿入することをお勧めします。

14
AnnaR

空かどうかにかかわらず、行列またはベクトルを行列に追加する方法はいくつかあります。マトリックスのサイズ、および追加を行う頻度に大きく依存します。 (スパース行列はまったく異なる動物であることに注意してください。それらは個別に処理する必要があります。)

単純なスキームでは連結を使用します。たとえば、ランダムな配列を作成します。ここではRandへの1つの呼び出しが適切な解決策になることを知っていますが、比較の目的でのみ実行しています。

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倍の時間がかかります。この2次の振る舞いが、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のベクトル化された使用よりもはるかに劣っています。したがって、可能な限り、このような関数のベクトル化された形式を使用してください。

問題は、最終的に何個の要素になるかわからない場合があることです。厄介な二次成長を回避するために使用できるいくつかのトリックがまだあります。

トリックの1つは、Aの最終サイズを推測することです。今度は、インデックスを使用して新しい値をAに挿入しますが、新しいエントリがAの境界を超える場合に注意してください。 Aのサイズ。ゼロの1つの大きなブロックを最後に追加します。新しい要素のAへのインデックス付けに戻ります。「追加」された要素の数を個別にカウントします。このプロセスの最後に、未使用の要素を削除します。これにより、ほんの少しの追加ステップしか実行されないため、厄介な2次の動作の多くが回避されます。 (追加する必要がある場合は、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次演算です。

この問題の解決策は、上記の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では関数ハンドルの実装が明らかに改善され、関数ハンドルがより高速になりました。

これらのスキームの利点は、数百万の追加ステップを許可する一方で、2次的なパフォーマンスのペナルティがないことです。

ええと、これは確かに、質問されたときに最初に要求された情報よりも多くの情報です。多分誰かがそれから何かを得るでしょう。

28
user85109

最善のオプションは、行列を事前に割り当て、ループ変数を使用することです。これはかなり速くなるはずです。

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
3
KennyMorton

私が探している解決策は、myPointMatrixを0行3列の行列に初期化することです。

myPointMatrix = zeros(0, 3);

その後、最初の割り当て

myPointMatrix = [myPointMatrix; tempPoint];

後続のものと同様に正しく動作します。割り当てを記述する同等の方法は、

myPointMatrix(end+1,:) = tempPoint;

ただし、そのような行列を成長させることは効率的ではないことを覚えておいてください。AnnaRが言うように、myPointMatrixを初期化する場合は、ifsの最終サイズがわかっている場合は、それを使用する方が適切です。

0
Fanfan
%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
Nima_k2

これが必要です

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といくつかのfindlogicalに置き換えます...しかし、自分でそれを行うことができると思います...:D

0
user677656