18

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

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

を削除しifて追加しようとするとmyPointMatrix、MATLABは行列の次元が一貫していないことについて当然のことながら文句を言います。if-ステートメントとの初期化の両方を削除すると、 myPointMatrix = 0MATLABは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
4

6 に答える 6

29

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

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

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のサイズを2倍にすると、動的に成長するAの構築に4倍の時間がかかります。この2次の振る舞いは、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.

これは動的に成長する配列よりもはるかに優れていますが、それでも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をファイルとしてMATLABCentralFileExchangeに投稿しました。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次のパフォーマンスペナルティがないことです。

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

于 2009-04-23T19:12:24.497 に答える
14

myPointMatrix = [];マトリックスを初期化するために使用します。

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

次に、最終的なサイズで初期化MyPointMatrixし、マトリックス内の指定された位置にポイントを挿入することをお勧めします。

于 2009-04-23T12:49:45.490 に答える
3

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

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
于 2009-04-23T12:49:38.767 に答える
0
%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
于 2013-02-17T18:12:14.343 に答える
0

あなたが探している解決策は、myPointMatrixを0行3列の行列に初期化することだと思います。

myPointMatrix = zeros(0, 3);

次に、最初の割り当て

myPointMatrix = [myPointMatrix; tempPoint];

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

myPointMatrix(end+1,:) = tempPoint;

ただし、そのようなマトリックスを成長させることは効率的ではなく、AnnaRが言うように、myPointMatrixifsの最終サイズで初期化することは、わかっている場合はより良い解決策であることを覚えておいてください。

于 2009-04-23T13:04:02.850 に答える
0

これはあなたが必要なものです

myPointMatrix=[];
for x=0:limit
for y=0:limit
for x=0:limit
  myPointMatrix(:,end+1)=[x y z];
end
end
end

ただし、割り当てる前に、[xyz]を使用して非線形演算を実行する場合に限ります。そうでない場合は、上記の行を次のように書くことができます。

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に置き換えたい場合もありますが、自分でそれを行うことができます...:Dfindlogical

于 2012-04-02T16:44:13.000 に答える