2016-03-30 12 views
3

Gibt es eine Möglichkeit, Schleifen zu vermeiden, um diesen Code schneller zu machen?Gibt es eine Möglichkeit, Schleifen zu vermeiden, um diesen Code schneller zu machen?

"var" ist das gewünschte Ergebnis.

"AA" und "BB" sind Vektoren, deren Werte bekannt sind.

Die vier Hauptlinien dieses Codes sind auf der Logik basiert: 00, 01, 10, 11 (0 für <= und 1 für >)

for i=3:1:2000 

    for j=1:50011 
     if AA(i) == j && BB(i)<=BB(i-1) && BB(i-1)<=BB(i-2) 
      var(i)=j; 
     else 
      if AA(i) == j && BB(i)<=BB(i-1) && BB(i-1)>BB(i-2) 
       var(i)=j+50011; 
      else 
       if AA(i) == j && BB(i)>BB(i-1) && BB(i-1)<=BB(i-2) 
        var(i)=j+2*50011; 
       else 
        if AA(i) == j && BB(i)>BB(i-1) && BB(i-1)>BB(i-2) 
         var(i)=j+3*50011; 
        end 
       end 
      end 
     end 
    end 

end 

Antwort

3

Ich habe es geschafft, Ihren Code in eine Zeile Code zu vektorisieren! Wenn man von ein paar Sekunden bis zu unter einer Millisekunde:

out = [0 0 AA(3:end) + ([1 2] * (diff(BB(hankel(1:3, 3:numel(BB)))) > 0)).*50011]; 

Um zu verstehen, wie man dorthin kommt, wollen wir schrittweise den ursprünglichen Code verbessern.


1)

Zuerst beginnen wir mit dem Doppel-Loop Sie haben:

tic 
var0 = zeros(size(AA)); 
for i=3:numel(AA) 
    for j=1:N 
     if AA(i) == j && BB(i)<=BB(i-1) && BB(i-1)<=BB(i-2) 
      var0(i)=j; 
     else 
      if AA(i) == j && BB(i)<=BB(i-1) && BB(i-1)>BB(i-2) 
       var0(i)=j+50011; 
      else 
       if AA(i) == j && BB(i)>BB(i-1) && BB(i-1)<=BB(i-2) 
        var0(i)=j+2*50011; 
       else 
        if AA(i) == j && BB(i)>BB(i-1) && BB(i-1)>BB(i-2) 
         var0(i)=j+3*50011; 
        end 
       end 
      end 
     end 
    end 
end 
toc 

2)

Wie @SpamBot erwähnt, die verschachtelte if/else-Anweisungen sein kann vereinfacht, indem man sie stattdessen verkettet. Außerdem werten Sie denselben Test AA(i)==j mehrmals aus. Wenn der Test falsch ist, wird die gesamte for-Schleife übersprungen. So können wir diese zweite For-Schleife eliminieren und direkt j=AA(i) verwenden.

Hier ist neu der Code:

tic 
var1 = zeros(size(AA)); 
for i=3:numel(AA) 
    j = AA(i); 
    if BB(i)<=BB(i-1) && BB(i-1)<=BB(i-2) 
     var1(i) = j; 
    elseif BB(i)<=BB(i-1) && BB(i-1)>BB(i-2) 
     var1(i) = j + 50011; 
    elseif BB(i)>BB(i-1) && BB(i-1)<=BB(i-2) 
     var1(i) = j + 2*50011; 
    elseif BB(i)>BB(i-1) && BB(i-1)>BB(i-2) 
     var1(i) = j + 3*50011; 
    end 
end 
toc 

Dies ist eine große Verbesserung, und der Code wird in einem Bruchteil der ursprünglichen Zeit. Noch können wir es besser machen ...

3)

Wie Sie in Ihrer Frage erwähnt, ist die if/else Bedingungen entsprechen dem Muster 00, 01, 10, 11 wo 0/1 oder falsch/wahr die Ergebnisse der Durchführung eines binär x> y teste über benachbarte Zahlen.

diese Idee verwenden, erhalten wir die folgenden Code:

tic 
var2 = zeros(size(AA)); 
for i=3:numel(AA) 
    val = (BB(i) > BB(i-1)) * 10 + (BB(i-1) > BB(i-2)); 
    switch (val) 
     case 00 
      k = 0; 
     case 01 
      k = 50011; 
     case 10 
      k = 2*50011; 
     case 11 
      k = 3*50011; 
    end 
    var2(i) = AA(i) + k; 

end 
toc 

4)

Beginnen wir mit einem Tisch-Lookup-Operation, dass die Switch-Anweisung ersetzen.Dies gibt uns diese neue Version:

tic 
v = [0 1 2 3] * 50011; % 00 01 10 11 
var3 = zeros(size(AA)); 
for i=3:numel(AA) 
    var3(i) = AA(i) + v((BB(i) > BB(i-1))*2 + (BB(i-1) > BB(i-2)) + 1); 
end 
toc 

5)

In dieser letzten Version, können wir mit der Feststellung ganz aus der Schleife loszuwerden, dass jede Iteration greift auf die Schicht BB(i-2:i) in einer verschiebbares Fenster Weise. Wir können sauber use the hankel function to create a sliding window über BB (jeweils als eine Spalte zurückgegeben).

Als nächstes verwenden wir diff, um vektorisierte Vergleiche durchzuführen, dann kartieren Sie das Ergebnis 0/1 der beiden Tests als [0 1 2 3]*50011 Werte. Zuletzt fügen wir den Vektor AA passend hinzu.

Dies gibt uns unseren letzten Einzeiler, vollständig vektorisiert:

tic 
var4 = [0, 0, AA(3:end) + ([1 2] * (diff(BB(hankel(1:3, 3:numel(BB)))) > 0)).*50011]; 
toc 

Vergleich

die oben genannten Lösungen, um zu überprüfen, habe ich die folgenden Zufallsvektoren als Testdaten:

N = 50011; 
AA = randi(N, [1 2000]); 
BB = randi(N, [1 2000]); 

assert(isequal(var0,var1,var2,var3,var4)) 

Ich bekomme die folgenden Zeiten zu den Lösungen in der angegebenen Reihenfolge:

Ich hoffe, dieser Beitrag war nicht zu lang, ich wollte nur zeigen, wie man diese Art von Problemen anpackt, indem man inkrementelle Änderungen vornimmt.

Nun nehmen Sie Ihre Wahl, bei der Lösung, die Sie am besten gefällt :)

+0

Vielen Dank für Ihre unswer! Aber wenn ich deinen Vergleich verstanden habe (außer der ersten Version), benötigt die letzte Version mehr Zeit als die drei anderen !!! – bzak

+0

Wir sprechen hier in der Größenordnung von unter einer Millisekunde, es gibt keinen signifikanten Unterschied ... Ich schlage vor, dass Sie mit der Lösung gehen, mit der Sie sich am wohlsten fühlen (Nummer 2 oben ist völlig in Ordnung). Ich gebe zu, in den Bereichen 3 bis 5 lag der Fokus eher auf einer kürzeren und vollständig vektorisierten Lösung als auf einer dramatischen Leistungsverbesserung. – Amro

+0

Sie sehen hier im MATLAB-Tag, dass wir diese Art von Fragen oft als Herausforderung betrachten, um den kürzestmöglichen vektorisierten Code zu schreiben, der nicht immer den am besten lesbaren oder schnellsten bedeutet :) – Amro

2

ich die wichtigste Verbesserung denken wäre zu bewerten AA(i)==j nur einmal beim Schleifen über j.

Außerdem überschreiben Sie möglicherweise sehr oft var(i), obwohl nur das letzte Überschreiben relevant ist. Erwäge, nur ein j == AA (i) zu nehmen und tue das if-else nur dafür. Im Wesentlichen suchen Sie innerhalb von AA nach j, verwenden Sie stattdessen find(AA == j, 1).

Als Randnotiz haben Sie unnötige If/End-Blöcke, die direkt in den elterlichen Else-Block gehen könnten.

1

Hier ist ein vektorisiert Ansatz hauptsächlich logical indexing mit -

%// Get size parameter 
N = numel(AA)-2; 
limit = 50011; 

%// Get differentiation across BB 
dB = diff(BB); 

%// Construct an ID array with different valus for various conditions 
id = ones(N,1); 
id((dB(2:end) <= 0) & (dB(1:end-1) > 0)) = 2; 
id((dB(2:end) > 0) & (dB(1:end-1) <= 0)) = 3; 
id((dB(2:end) > 0) & (dB(1:end-1) > 0)) = 4; 

%// Get scaled values as used under various IF statements 
vals = ((id-1)*50011) + AA(3:end); 

%// Get a valid mask that would be used to set values from vals into output 
valid_mask = AA(3:end) <= limit; 

%// Setup output array and selectively set values from vals using valid_mask 
var_out = zeros(1,N); 
var_out(valid_mask) = vals(valid_mask); 

Bitte beachten Sie, dass die ursprüngliche Ausgabe, die ersten beiden Elemente als Nullen haben würde immer . Die Ausgabe mit der vorgeschlagenen Lösung überspringt die ersten beiden Elemente, um diese Redundanz zu vermeiden. Wenn zur Konsistenz mit dem alten Paradigma benötigt, Pad mit zwei Nullen am Start -

final_out = [0 0 var_out]; 
Verwandte Themen