18 Stimmen

Anhängen eines Vektors an eine leere MATLAB-Matrix

Ich habe MATLAB-Code, der n-dimensionale Punkte (n >1) in eine Matrix einfügt ( myPointMatrix ) und überlege gerade, wie ich den ersten Punkt einfügen soll.

Momentan prüft das Programm die Größe von myPointMatrix bevor Sie einen Punkt einfügen. Wenn er 1x1 ist, myPointMatrix wird gleich dem aktuellen Punkt gesetzt. Andernfalls wird der aktuelle Punkt angehängt. Diese if -Aussage ist nur einmal wahr, wird aber jedes Mal ausgewertet, wenn ich einen Punkt einfüge, was sehr, sehr oft der Fall ist.

Das Entfernen der if und versucht, an myPointMatrix führt dazu, dass MATLAB sich verständlicherweise darüber beschwert, dass die Matrixdimensionen nicht konsistent sind. Das Entfernen der beiden if -Statement und die Inialisierung von myPointMatrix = 0 veranlasst MATLAB zur Suche nach myPointMatrix undefiniert. Auch verständlich.

Wie initialisiere ich myPointMatrix so dass ich die if -Stellungnahme? Oder gibt es eine andere intelligente Lösung?

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

29voto

Es gibt verschiedene Möglichkeiten, eine Matrix oder einen Vektor an eine beliebige, auch leere, Matrix anzuhängen. Vieles hängt von der Größe der Matrix ab und davon, wie oft Sie die Anfügung vornehmen wollen. (Beachten Sie, dass dünnbesetzte Matrizen etwas ganz anderes sind. Sie müssen gesondert behandelt werden.)

Das einfache Schema würde die Verkettung verwenden. Ich werde zum Beispiel ein zufälliges Array erstellen. Ich weiß zwar, dass ein Aufruf von rand hier die richtige Lösung wäre, aber ich mache es nur zu Vergleichszwecken.

n = 10000;
tic
A = [];
for i = 1:n
  Ai = rand(1,3);
  A = [A;Ai];
end
toc

Elapsed time is 9.537194 seconds.

Ich sehe, dass der Zeitaufwand ziemlich hoch war, viel höher als wenn ich Rand direkt angerufen hätte.

tic,rand(n,3);toc
Elapsed time is 0.008036 seconds.

Andere Möglichkeiten des Anhängens sind zeitlich ähnlich. Zum Beispiel können Sie auch durch Indizierung anhängen.

A = [];
A(end+1,:) = rand(1,3);
A
A =
      0.91338      0.63236      0.09754

Dies ist vom Zeitaufwand her ähnlich wie das Anhängen durch Verkettung. Interessant ist die Tatsache, dass das Anhängen neuer Zeilen an ein Array sich von dem Anhängen neuer Spalten deutlich unterscheidet. Es dauert etwas länger, eine Zeile anzuhängen als eine Spalte. Dies liegt an der Art und Weise, wie Elemente in MATLAB gespeichert werden. Das Anhängen einer neuen Zeile bedeutet, dass die Elemente im Speicher tatsächlich neu gemischt werden müssen.

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.

Das Problem bei jeder Append-Operation ist, dass MATLAB den für A benötigten Speicher neu zuweisen muss, und zwar JEDES Mal, wenn die Matrix größer wird. Da die Größe von A linear wächst, steigt die benötigte Gesamtzeit quadratisch mit n. Würden wir also die Größe von n verdoppeln, bräuchte das dynamisch gewachsene A viermal so lange zum Aufbau. Dieses quadratische Verhalten ist der Grund, warum man Ihnen rät, Ihre MATLAB-Arrays vorab zuzuweisen, wenn sie dynamisch erweitert werden. Wenn Sie sich die mlint-Flags im Editor ansehen, warnt MATLAB Sie, wenn dies der Fall ist.

Eine bessere Lösung, wenn Sie die endgültige Größe von A kennen, ist die Vorabzuweisung von A auf seine endgültige Größe. Dann indexieren Sie einfach hinein.

tic
A = zeros(n,3);
for i = 1:n
  A(i,:) = rand(1,3);
end
toc

Elapsed time is 0.156826 seconds.

Dies ist zwar viel besser als die dynamisch gewachsenen Array, es ist immer noch viel schlechter als eine vektorisierte Verwendung von Rand. Also, wo immer möglich, verwenden Sie die vektorisierte Form von Funktionen wie diese.

Das Problem ist, dass man manchmal einfach nicht weiß, wie viele Elemente man am Ende haben wird. Es gibt noch einige Tricks, mit denen man das unangenehme quadratische Wachstum vermeiden kann.

Ein Trick besteht darin, die endgültige Größe von A zu schätzen. Verwenden Sie nun die Indizierung, um neue Werte in A einzufügen, aber achten Sie genau darauf, wann die neuen Einträge die Grenzen von A überschreiten werden. Kehren Sie nun zur Indizierung neuer Elemente in A zurück. Führen Sie eine separate Zählung durch, wie viele Elemente "angehängt" wurden. Am Ende dieses Prozesses löschen Sie die nicht verwendeten Elemente. Auf diese Weise lässt sich ein Großteil des unangenehmen quadratischen Verhaltens vermeiden, da nur wenige Anhängevorgänge durchgeführt werden. (Denken Sie daran, dass Sie die Größe von A verdoppeln, wenn Sie eine Anfügung vornehmen müssen).

Ein zweiter Trick ist die Verwendung von Zeigern. MATLAB bietet zwar nicht wirklich viele Möglichkeiten für Zeiger, aber ein Zell-Array ist ein Schritt in diese Richtung.

tic
C = {};
for i = 1:n
  C{end+1} = rand(1,3);
end
A = cat(1,C{:});
toc

Elapsed time is 3.042742 seconds.

Dies nahm weniger Zeit in Anspruch als die gewachsene Anordnung. Warum? Wir haben nur ein Array mit Zeigern auf die Zellen erstellt. Das Schöne daran ist, dass es auch dann noch gut funktioniert, wenn jeder Append-Schritt eine variable Anzahl von Zeilen hat.

Ein Problem mit der Zelle Array, ist es nicht furchtbar effizient, wenn es MILLIONEN von Elementen, um anzuhängen sind. Es ist immer noch eine quadratische Operation, weil wir das Array von Zeigern bei jedem Schritt wachsen.

Eine Lösung für dieses Problem ist die Verwendung einer Mischung aus den beiden oben gezeigten Stilen. Definieren Sie also jede Zelle des Zellenfeldes als mäßig groß. Verwenden Sie nun die Indizierung, um neue Zeilen von A in die Zelle zu füllen. Wenn die aktuelle Zelle durch den nächsten Append-Schritt vergrößert werden muss, fügen Sie einfach eine neue Zelle zum Zellen-Array hinzu.

Vor einigen Jahren kam diese Diskussion in der MATLAB-Newsgroup auf, und es wurden mehrere Lösungen in dieser Richtung vorgeschlagen. Ich habe die Lösungen gepostet growdata & growdata2 als Dateien auf dem MATLAB Central File Exchange. Growdata2 verwendet Funktionshandles, um das Problem zu lösen:

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.

Zu dieser Zeit war es ein etwas schnellerer Ansatz, persistente Variablen zu verwenden.

tic
growdata
for i = 1:n
  growdata(rand(1,3))
end
A = growdata;
toc

Elapsed time is 2.048584 seconds.

Seitdem hat sich die Implementierung von Funktionshandles in MATLAB deutlich verbessert, so dass das Funktionshandle nun schneller ist.

Ein Vorteil dieser Verfahren ist, dass sie keine quadratischen Leistungseinbußen mit sich bringen und gleichzeitig Millionen von Append-Schritten ermöglichen.

Nun gut, das sind sicherlich mehr Informationen, als ursprünglich bei der Fragestellung verlangt wurden. Aber vielleicht hat ja jemand etwas davon.

14voto

AnnaR Punkte 3136

Verwenden Sie myPointMatrix = []; um die Matrix zu initialisieren.

Die größere myPointMatrix ist, desto langsamer wird das Anhängen sein. Es wird immer langsamer, da Matlab jedes Mal, wenn Sie einen Punkt anhängen, eine neue Matrix mit der neuen Größe erstellt und die Informationen aus Ihrer alten Matrix und Ihrem neuen Punkt in die neue Matrix kopiert.

Es ist dann besser, die MyPointMatrix mit seiner endgültigen Größe und dem Einfügen der Punkte an den vorgegebenen Positionen in der Matrix.

2 Stimmen

+1 für Ihren letzten Satz. Das ist der effizienteste Weg, eine Matrix in MATLAB zu initialisieren.

3voto

KennyMorton Punkte 671

Am besten ist es, die Matrix im Voraus zu lokalisieren und eine Schleifenvariable zu verwenden. Dies sollte deutlich schneller sein.

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

0voto

Fanfan Punkte 1086

Ich glaube, die Lösung, die Sie suchen, ist die Initialisierung der myPointMatrix zu einer Matrix mit 0 Zeilen und 3 Spalten, d. h.

myPointMatrix = zeros(0, 3);

Dann die erste Aufgabe

myPointMatrix = [myPointMatrix; tempPoint];

und auch die nachfolgenden korrekt funktionieren. Eine äquivalente Möglichkeit, die Aufgabe zu schreiben, ist

myPointMatrix(end+1,:) = tempPoint;

Denken Sie jedoch daran, dass das Wachstum einer solchen Matrix nicht effizient ist und, wie AnnaR sagt, die Initialisierung myPointMatrix mit ifs endgültiger Größe, falls bekannt, ist eine bessere Lösung.

0voto

Nima_k2 Punkte 21
%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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X