2016-08-02 9 views
2

Betrachten Sie den folgenden Befehl ein:Wie mache ich Modulo und logische Indizierung schneller?

c(c>A | c<1) = mod(c(c>A | c<1),A); 

wo c ein Spaltenvektor ist, und A ist ein Skalar.

In Kürze: Gibt es eine Möglichkeit, diese Arbeit schneller zu machen?

Erläuterung:

c(i) stellt eine Spaltennummer in einer A -by- A Matrix. Es kann jedoch Werte größer als A oder kleiner als 1 haben, und dieser Befehl sollte es in einer Art "PAC-MAN" Art und Weise beheben. Wenn c(i) größer als A ist, dann, wenn Sie A erreichen, fangen Sie an, von 1 zurück zu zählen, wenn Sie wieder A erreichen, beginnen Sie wieder von 1, und so weiter, bis Sie den Wert von c(i) zählen. Dies sollte für c(i)<1 genauso funktionieren, also wird die Zählung umgekehrt.

Beispiele:

  • Wenn A = 10 und c(i) = 17, dann c(i) nach diesem Befehl sollen 7.

  • Wenn A = 10 und c(i) = -8 sein soll, dann c(i) nach diesem Befehl sein 2.

  • Wenn A = 10 und c(i) = 213, dann c(i) nach diesem Befehl sollte 3.

Die Motivation sein: Dieser Befehl ein Teil eines Simulationsmodells ist, ich habe, und zur Zeit ist es das langsamste daran teil. Diese spezifische Reihe wird in jeder Realisierung des Modells Millionen (!) Von Zeiten genannt, und es gibt eine Menge, so dass jede Verbesserung hilfreich sein wird. BTW, die typische Größe von c ist ungefähr 10K-by-1.

p.s .: Wenn Sie einen besseren Vorschlag für den Titel haben, werde ich glücklich sein, es zu ändern, ich konnte keinen guten finden.

Antwort

4

Sie brauchen eigentlich keine logische Indizierung hier zu tun, weil alle von c > A | c < 1 ausgeschlossen Werte werden nicht von mod und es wird wahrscheinlich schneller zu einfach passieren alles-mod anstatt tun Vergleiche und Indizierung berührt werden, um zu bestimmen, welche Werte an mod übergeben werden.

c = [17 -8 213, 7]; 

c = mod(c, A); 
% 7 2 3 7 

Im Allgemeinen aber für andere Funktionen, in denen müssen Sie logische Indizierung auf der Eingabe und Ausgabe einer Funktion, werden Sie das logische Array in einer temporären Variablen gespeichert werden sollen, anstatt sie zweimal Berechnung:

Hier
touse = c < 1 | c > A; 
c(touse) = mod(c(touse), A); 

ist ein schneller kleiner Maßstab die relative Leistung jeden Verfahrens zeigt:

function timemod() 

    sizes = round(linspace(100, 100000, 10)); 

    [times1, times2, times3] = deal(zeros(numel(sizes), 1)); 

    A = 10; 

    for k = 1:numel(sizes) 

     data = round(rand(sizes(k), 1) * A * 100); 
     times1(k) = timeit(@()indexing(data, A)); 

     data = round(rand(sizes(k), 1) * A * 100); 
     times2(k) = timeit(@()indexing_temp(data, A)); 

     data = round(rand(sizes(k), 1) * A * 100); 
     times3(k) = timeit(@()mod(data, A)); 
    end 

    figure 
    plot(sizes, 1000 * cat(2, times1, times2, times3)) 
    legend({'Indexing', 'Indexing w/ temp', 'No Indexing'}) 

    xlabel('Number of Elements') 
    ylabel('Execution Time (ms)') 

    fprintf('Indexing:    %0.2f ms\n', mean(times1 * 1000)) 
    fprintf('Indexing with temp: %0.2f ms\n', mean(times2 * 1000)) 
    fprintf('No Indexing or temp: %0.2f ms\n', mean(times3 * 1000)) 
end 

function data = indexing(data, A) 
    data(data > A | data < 1) = mod(data(data > A | data < 1), A); 
end 

function data = indexing_temp(data, A) 
    inds = data > A | data < 1; 
    data(inds) = mod(data(inds), A); 
end 

enter image description here

+0

Das war brilliant! Danke vielmals. Ich kann nicht glauben, dass ich diese Option verpasst habe ... jetzt beende ich meine Doktorarbeit etwas früher;) – EBH

+0

Können Sie der Antwort eine vierte Option hinzufügen, wobei 'c1 = mod (c, A)' (Zuweisung zu einem anderen) Variable)? In meiner Maschine ist der schnellste. – EBH

+0

@EBH Haben Sie das tatsächlich mit dem Benchmark-Code getestet? Es sollte keinen wirklichen Unterschied zu "c = mod (c, A)" geben, wenn man es richtig profiliert (mit 'timeit') – Suever