So verbessern Sie die Leistung mit F # Idiomen

8

Ich benutze diesen Kurs über maschinelles Lernen , um F # zur gleichen Zeit zu lernen. Ich habe die folgende Hausaufgabe Übung gemacht, die die erste Übung der zweiten Woche ist:

  

Führen Sie eine Computersimulation durch, um 1.000 virtuelle faire Münzen zu werfen. Flip   jede Münze unabhängig 10 mal. Konzentriere dich wie folgt auf 3 Münzen: c1   Ist die erste Münze umgedreht, ist crand eine zufällig ausgewählte Münze   die 1.000 und cmin ist die Münze, die die Mindesthäufigkeit von   Köpfe (wählen Sie die frühere im Falle einer Krawatte).

     

Sei ν1 , Ñrand   und νmin ist der Anteil an Köpfen, die für die jeweiligen 3 erhalten wurden   Münzen aus den 10 Würfen. Führen Sie das Experiment 100.000 Mal hintereinander durch   um eine vollständige Verteilung von ν1, Ñrand und νmin zu erhalten (beachte, dass c rand   und c min wird von run zu run wechseln.

     

Was ist der Durchschnittswert von νmin ?

Ich habe den folgenden Code erzeugt, der gut funktioniert und die richtige Antwort gibt:

%Vor%

Es dauert jedoch ungefähr 4 Minuten, um in meiner Maschine zu laufen. Ich weiß, dass es eine Menge Arbeit macht, aber ich frage mich, ob es einige Modifikationen gibt, die vorgenommen werden könnten, um es zu optimieren.

Wenn ich versuche, F # zu lernen, frage ich nach Optimierungen, die F # -Idiome verwenden, nicht den Code in einen C-Stil zu ändern.

Fühlen Sie sich frei, irgendeine Art von Verbesserung zu empfehlen, in Stil, guten Praktiken usw.

[UPDATE]

Ich habe einen Code geschrieben, um die vorgeschlagenen Lösungen zu vergleichen, es ist hier zugänglich.

Dies sind die Ergebnisse:

  

Basis - Ergebnis: 0.037510, verstrichene Zeit: 00: 00: 55.1274883, Verbesserung:   0,99 x

     

Matthew Mcveigh - Ergebnis: 0.037497, verstrichene Zeit: 00: 00: 15.1682052, Verbesserung: 3.61 x

     

Fjodor Soikin - Ergebnis: 0,037524, verstrichene Zeit: 00: 01: 29,7168787, Verbesserung: 0,61 x

     

GuyCoder - Ergebnis: 0.037645, verstrichene Zeit: 00: 00: 02.0883482, Verbesserung: 26.25 x

     

GuyCoder MathNet- Ergebnis: 0.037666, verstrichene Zeit:   00: 00: 24.7596117, Verbesserung: 2.21 x

     

TheQuickBrownFox - Ergebnis:   0.037494, verstrichene Zeit: 00: 00: 34.2831239, Verbesserung: 1.60 x

Der Gewinner bezüglich der Verbesserung der Zeit ist der GuyCoder, also werde ich seine Antwort akzeptieren. Ich finde jedoch, dass sein Code schwieriger zu verstehen ist.

    
Tao Gómez Gil 04.06.2016, 17:16
quelle

3 Antworten

4

Wenn Sie Ihren Code auf meinem Computer und Timing ausführen, bekomme ich:

%Vor%

Wenn ich meinen Code auf meinem Computer und Timing laufe, bekomme ich:

%Vor%

wobei vMin der korrekten Antwort von b am ähnlichsten ist 0.01

Das ist fast 5x schneller.

Ich habe nicht an jeder Methode und Datenstruktur herumgebastelt, um herauszufinden, warum und was funktioniert hat. Ich habe einfach viele Jahrzehnte an Erfahrung benutzt, um mich zu leiten. Die Zwischenwerte nicht eindeutig zu speichern, sondern nur die Ergebnisse sind eine große Verbesserung. Speziell coinTest gibt nur die Anzahl der Köpfe zurück, die ein int ist und keine Liste der Ergebnisse. Auch ist es vorteilhaft, anstelle einer Zufallszahl für jeden Münzwurf eine Zufallszahl für jede Münze zu erhalten und dann jeden Teil dieser Zufallszahl als Münzwurf zu verwenden. Das speichert number of flips - 1 Aufrufe an eine Funktion. Außerdem habe ich es vermieden, float -Werte bis zum Ende zu verwenden; Ich denke nicht, dass das Zeit auf der CPU spart, aber es hat den Denkprozess des Denkens nur in int vereinfacht, was mir erlaubt hat, mich auf andere Effizienzen zu konzentrieren. Ich weiß, dass das komisch klingen mag, aber je weniger ich darüber nachdenken muss, desto besser die Antworten, die ich bekomme. Ich habe auch nur coinTest ausgeführt, wenn es notwendig war, z. nur die erste Münze, nur die zufällige Münze, und alle Schwänze als Ausgangsbedingung gesucht.

%Vor%

================================================== ==========================

Dies ist nur eine Zwischenantwort, weil ich nachgefragt habe, ob das OP in Betracht zieht, MathNet Numerics idiomatische F # zu verwenden, und das OP wollte sehen, wie das aussah. Nach dem Ausführen seiner Version und dieser ersten geschnittenen Version auf meiner Maschine ist die OP-Version schneller. OP: 75 Sekunden, meins: 84 Sekunden

%Vor%

In der Mitte der Programmierung erkannte ich, dass ich alle Berechnungen mit int durchführen konnte. Es waren nur die letzten Berechnungen, die die Prozentsätze erzeugten, die ein float oder double sein mussten, und selbst dann ist das nur weil die Liste der Antworten ein Prozentsatz ist; Theoretisch können die Zahlen als int verglichen werden, um das gleiche Verständnis zu erhalten. Wenn ich nur int verwende, müsste ich einen int -Matrix-Typ erstellen und das ist mehr Arbeit, als ich machen möchte. Wenn ich Zeit bekomme, schalte ich die MathNet Matrix auf eine F # Array2D oder etwas Ähnliches und überprüfe das. Beachten Sie, wenn Sie dies mit MathNet markieren, dann könnte der Betreuer von MathNet antworten ( Christoph Rüegg )

Ich habe diese Methode geändert und sie ist um 5 Sekunden schneller.

%Vor%     
Guy Coder 04.06.2016, 21:06
quelle
6

Die Zuteilung einer großen Anzahl von Listen im Voraus ist eine schwere Arbeit, der Algorithmus kann online, z.B. über Sequenzen oder Rekursion. Ich habe die ganze Arbeit in tail rekursive Funktionen für eine rohe Geschwindigkeit umgewandelt (wird vom Compiler in Schleifen umgewandelt)

nicht garantiert zu 100% korrekt, aber hoffentlich gibt Ihnen einen Grund, wo ich mit ihm ging :

%Vor%     
Matthew Mcveigh 04.06.2016 22:15
quelle
3

Ich habe versucht, den Code so klein wie möglich zu halten, um ihn schneller zu machen.

Die größte Leistungsverbesserung, die ich gefunden habe, bestand darin, die Funktion ObtainFrequencyOfHeads so zu ändern, dass sie true -Werte in der Sammlung zählt, anstatt eine gefilterte Zwischengruppe zu erstellen und diese dann zu zählen. Ich habe dies mit fold :

getan %Vor%

Eine weitere Verbesserung ergab sich aus der Änderung aller Listen in Arrays. Das war so einfach wie das Ersetzen aller Instanzen von List. durch Array. (einschließlich der neuen Funktion oben).

Manche mögen sagen, dass dies weniger funktional ist, weil es eine veränderbare Sammlung anstelle einer unveränderlichen Sammlung verwendet. Wir mutieren jedoch keine Arrays, verwenden nur die Tatsache, dass sie kostengünstig zu erstellen sind, überprüfen die Länge von und suchen nach Index. Wir haben eine Beschränkung der Mutation entfernt, aber wir verwenden immer noch keine Mutation. Es ist sicherlich idiomatische F #, bei Bedarf Arrays für die Performance zu verwenden.

Mit diesen beiden Änderungen habe ich eine fast zweifache Verbesserung der FSI erreicht.

    
TheQuickBrownFox 06.06.2016 07:43
quelle

Tags und Links