Schnelle Trigonometrie-Berechnungen
Ihre Aufgabe ist es, ein Programm zu erstellen, das den Sinus, den Cosinus und den Tangens eines Winkels in Grad berechnet.
Regeln
- Keine eingebauten Trigonometriefunktionen (nicht einmal Sekant, Kosekant und Kotangens, wenn Ihre Sprache sie hat).
- Sie können Nachschlagetabellen verwenden, aber ihre Gesamtgröße darf 3000 Mitglieder nicht überschreiten (für alle drei Operationen zusammen). Bitte lassen Sie die Tabellen aus einer Datei (zB
trig.lookup
) lesen, damit sie den Code nicht verwechseln. - Kein Netzwerkzugriff.
- Sie müssen Ihre Ausgabe wie unten erläutert richtig runden. Verwenden Sie keinen Boden oder Decke.
- Sie können eine beliebige Methode verwenden, um die Werte zu berechnen, z. B. fortgesetzte Brüche , sofern diese auf 7 signifikante Stellen korrekt sind.
- Ihr Code muss in der Lage sein, sich selbst zu messen. Schließen Sie die Datei-E / A-Vorgänge aus Ihrer Zeit aus - also messen Sie einfach die Funktion (en), die den Trigger ausführen, und runden Sie sie.
- Ich muss in der Lage sein, Ihren Code auszuführen. Bitte posten Sie einen Link zu einem frei verfügbaren Compiler / Interpreter und geben Sie die Anweisungen an, die zum Kompilieren / Ausführen des Codes erforderlich sind (z. B. welche Optionen an GCC übergeben werden sollen).
- Es gelten Standardlücken .
Eingabeformat
- Lesen Sie aus einer aufgerufenen Datei, es
trig.in
sei denn, Ihre Sprache unterstützt keine Datei-E / A. - Die Winkel liegen zwischen 0 und 360 einschließlich.
- Die Eingabe besteht aus Winkeln zu zehn signifikanten Stellen in Dezimalstellen, die durch neue Linien getrennt sind. Beispielsweise:
90.00000000
74.54390000
175.5000000
Ausgabeformat
- Für jeden Winkel, den Sie angeben, müssen Sie seinen Sinus, Cosinus und Tangens an 7 signifikante Stellen ausgeben, die durch Leerzeichen voneinander getrennt sind. Verwenden Sie "wissenschaftliche Notation", zB
1.745329E-5
fürtan 0.001
oder1.000000E+0
fürsin 90
. - Bezeichnen Sie Unendlich oder NaN mit
n
, zum Beispiel sollte die Ausgabe für90.00000000
sein1.000000 0.000000 n
. - Wenn die Eingabe aus drei durch Zeilenumbrüche getrennten Winkeln besteht, sollte die Ausgabe aus drei Zeilen bestehen, die jeweils den Sinus, den Cosinus und den Tangens enthalten.
- Sie dürfen nichts anderes ausgeben.
- Ausgabe in eine Datei mit dem Namen, es
trig.out
sei denn, Ihre Sprache unterstützt keine Datei-E / A.
Wertung
- schnellste Code . Die Herausforderung besteht darin, ein Programm zu schreiben, das diese drei Werte so schnell wie möglich berechnet. Die schnellste Zeit gewinnt.
- Jeder erhält die gleiche Testeingabe aus vielen Blickwinkeln.
- Die Zeiten werden auf meinem Computer aufgezeichnet.
- Ihre Punktzahl ist der Durchschnitt von drei Läufen mit derselben Eingabe (Sie können offensichtlich zwischen den Läufen nichts speichern).
- Kompilierzeit nicht enthalten. Bei dieser Herausforderung geht es mehr um die verwendete Methode als um die Sprache. (Wenn mich jemand darauf hinweisen könnte, wie ich die Kompilierungszeit für Sprachen wie Java ausschließen würde, wäre ich sehr dankbar.)
- Mein Computer ist eine Ubuntu 14.04-Installation. Die Statistiken des Prozessors befinden sich in Pastebin (erhalten durch Ausführen
cat /proc/cpuinfo
). - Ich werde deine Zeit in deine Antwort ändern, wenn ich sie getestet habe.
math
fastest-code
trigonometry
Geobits
quelle
quelle
sin
,cos
undtan
ist auf eine neue Zeile. Muss ich es ändern, um die Antworten in einer einzelnen Zeile auszugeben?Antworten:
Fortran 90
Ich verwende die CORDIC- Methode mit einem vorab tabellierten Array von 60 arctan-Werten (Einzelheiten dazu finden Sie im Wiki-Artikel).
Für diesen Code ist eine Datei erforderlich
trig.in
, in der alle Werte in Zeilenumbrüchen im selben Ordner wie die Fortran-Programmdatei gespeichert sind. Kompilieren dies ist,Wo
file
ist der Dateiname, den Sie vergeben (wahrscheinlichSinCosTan.f90
am einfachsten, obwohl es nicht erforderlich ist, Programmname und Dateiname abzugleichen). Wenn Sie den Intel-Compiler haben, würde ich die Verwendung empfehlenda die
-xHost
(die es für gfortran nicht gibt) Ihrem prozessor optimierungen auf höherer ebene zur verfügung stellt.Meine Testläufe ergaben ungefähr 10 Mikrosekunden pro Berechnung, wenn 1000 Zufallswinkel mit Gfortran 4.4 (4.7 oder 4.8 sind in Ubuntu Repos verfügbar) und ungefähr 9,5 Mikrosekunden mit Ifort 12.1 getestet wurden. Das Testen von nur 10 zufälligen Winkeln führt zu einer unbestimmbaren Zeit unter Verwendung von Fortran-Routinen, da die Timing-Routine auf die Millisekunde genau ist und einfache Mathematik besagt, dass es 0,100 Millisekunden dauern sollte, um alle 10 Zahlen auszuführen.
BEARBEITEN Anscheinend war ich beim Timing I / O, was (a) das Timing länger als nötig gemacht hat und (b) dem Punkt 6 widerspricht. Ich habe den Code aktualisiert, um dies widerzuspiegeln. Ich habe auch herausgefunden, dass die Verwendung einer
kind=8
Ganzzahl mit der intrinsischen Subroutinesystem_clock
eine Genauigkeit von Mikrosekunden ergibt.Mit diesem aktualisierten Code berechne ich jetzt jeden Wertesatz der trigonometrischen Funktionen in ungefähr 0,3 Mikrosekunden (die signifikanten Stellen am Ende variieren von Lauf zu Lauf, aber sie schweben konstant in der Nähe von 0,31 us), eine signifikante Verringerung gegenüber dem vorherigen Iteration, die IO zeitlich festgelegt.
quelle
Python 2.7.x oder Java (Treffen Sie Ihre Wahl)
Ein kostenloser Python-Interpreter kann hier heruntergeladen werden .
Ein kostenloser Java-Interpreter kann hier heruntergeladen werden .
Das Programm kann beide Eingaben aus einer Datei übernehmen, die
trig.in
sich im selben Verzeichnis wie die Programmdatei befindet. Die Eingabe wird durch Zeilenumbrüche getrennt.Ich habe das ursprünglich in Python gemacht, weil - nun, ich liebe Python. Da ich aber auch versuchen will zu gewinnen, habe ich es später in Java umgeschrieben ...
Python-Version: Ich habe ungefähr 21µs pro Lauf auf meinem Computer. Ich habe ungefähr 32µs, als ich es auf IDEone laufen ließ .
Java-Version: Ich erhalte ungefähr 0,4 µs pro Lauf auf meinem Computer und 1,8 µs auf IDEone .
Computerspezifikationen:
Prüfung:
sin
,cos
undtan
alle Eingangswinkel.Der für beide verwendete Testeingang lautet wie folgt:
Über den Code:
Die Grundvoraussetzung für dieses Programm war die Schätzung
sin
undcos
Verwendung ihrer Taylor-Polynome mit 14 Termen, was ich berechnete, um eine Fehlerschätzung von weniger als 1e-8 zu erhalten. Allerdings fand ich es schneller war zu berechnensin
alscos
, so stattdessen entschieden zu berechnen ,cos
indem Siecos=sqrt(1-sin^2)
Python-Version:
Java-Version:
quelle
cos
Berechnung ist übertrieben, ich würde nur tunsin(x+90degrees)
sin
sowohl als Funktion als auch als Variable. Ich dachte, es wäre schnellersin()
, wenn ich nicht ein zweites Mal etwas übergeben müsste , aber ich werde die beiden vergleichen, um zu sehen, ob das wirklich der Fall ist. War Ihr Eindruck, dass diecopySign()
Funktion langsamer ist als das Addieren von Dingen wie in meinersin()
Funktion?Oktave (oder Matlab) & C
Ein bisschen komplizierter Erstellungsprozess, aber eine Art neuer Ansatz und die Ergebnisse waren ermutigend.
Der Ansatz besteht darin, für jeden Grad angenäherte quadratische Polynome zu erzeugen. Grad = [0, 1), Grad = [1, 2), ..., Grad = [359, 360) hat also jeweils ein anderes Polynom.
Oktave - Gebäudeteil
Octave ist öffentlich verfügbar - Google
download octave
.Dies bestimmt das quadratische Polynom mit der besten Anpassung für jeden Grad.
Speichern als
build-fast-trig.m
:C - Gebäudeteil
Dadurch werden Doppeltexte im Textformat in das native Binärformat Ihres Systems konvertiert.
Speichern als
build-fast-trig.c
:Kompilieren:
Generierung der Koeffizientendatei
Lauf:
Jetzt müssen wir
qcoeffs.dat
als Datendatei für das eigentliche Programm verwenden.C - Schnellauslöseteil
Speichern als
fast-trig.c
:Kompilieren:
Lauf:
Es liest
trig.in
, speicherttrig.out
und druckt, um die verstrichene Zeit millisekundengenau zu trösten.Abhängig von den verwendeten Testmethoden kann es bei bestimmten Eingaben zu Fehlern kommen, zB:
Die richtige Ausgabe sollte sein
0.000000e+00 1.000000e+00 0.000000e+00
. Wenn die Ergebnisse mit Zeichenfolgen überprüft werden, schlägt die Eingabe fehl, wenn sie mit einem absoluten Fehler überprüft werden, z. B.fabs(actual - result) < 1e-06
wird die Eingabe übergeben.Der maximale absolute Fehler für
sin
undcos
war ≤ 3e-07. Denntan
, da das Ergebnis nicht auf ± 1 beschränkt ist , und Sie können eine relativ große Zahl von einer relativ kleinen Zahl, der absolute Fehler unterteilen könnte größer sein. Von -1 ≤ tan (x) ≤ +1 war der maximale absolute Fehler ≤ 4e-07. Für tan (x)> 1 und tan (x) <-1 war der maximale relative Fehler z. B.fabs((actual - result) / actual)
normalerweise <1e-06, bis Sie in den Bereich von (90 ± 5) oder (270 ± 5) Grad gelangen Fehler wird schlimmer.Im Test war die durchschnittliche Zeit pro Einzel Eingang (1,053 ± 0,007) & mgr; s, die auf meiner Maschine etwa 0.070 & mgr; s war schneller als nativer
sin
undcos
, auftan
die gleiche Weise definiert werden.quelle
Kobra
Kompiliere es mit
cobra filename -turbo
Tests: AMD FX6300 bei 5,1 GHz
Der von der C-Antwort verwendete 360 * 10000-Test dauert 365 ms (gegenüber 190 ms).
Der von den Python- und Java-Antworten verwendete Test mit 4 Einträgen läuft in 0,32 µs (gegenüber 30 µs, 3 µs).
Der von der Fortran-Antwort verwendete 1000-Random-Angle-Test läuft mit 100 ns pro Winkel (gegenüber 10 us).
quelle
C
Hier ist mein Versuch. Es funktioniert so:
Erstellen Sie eine Tabelle mit allen Werten von sin (x) von 0 bis 450 Grad. Dies sind entsprechend alle Werte von cos (x) von -90 bis 360 Grad. Bei 2926 Elementen ist alle 1 / 6,5 Grad ausreichend Platz für einen Wert. Die Programmeinheit ist daher 1 / 6,5 Grad, und es gibt 585 Einheiten in einer Vierteldrehung.
Eingangsgrade in Programmeinheiten umrechnen (Multiplizieren mit
6.5==110.1 binary.
) Die nächsten Werte für sin und cos finden Sie in der Tabelle. wandle dann den restlichen Teil der Eingabe (dx) in Bogenmaß um.wende die formel an,
sin(x+dx) == sin x +(d(sin x)/dx)*dx.
beachte das(d(sin x)/dx)==cos x,
aber nur, wenn wir radiant verwenden.Leider ist das allein nicht genau genug, so dass ein anderer Term erforderlich ist, der auf der nächsten Ableitung basiert.
d2(sin x)/dx2 == -sin x.
Dies muss mit multipliziert werdendx*dx/2
(nicht sicher, woher der Faktor 2 kommt, aber es funktioniert).Folgen Sie der analogen Prozedur für
cos x
und berechnen Sie danntan x == sin x / cos x
.Code
Es gibt hier ungefähr 17 Gleitkommaoperationen. Das kann etwas verbessert werden. Das Programm enthält Tabellenerstellungs- und Testausgaben unter Verwendung der nativen Triggerfunktionen, der Algorithmus jedoch nicht. Ich werde das Timing hinzufügen und es bearbeiten, um die E / A-Anforderungen später zu erfüllen (hoffentlich an diesem Wochenende). Es entspricht der Ausgabe der nativen Funktionen, mit Ausnahme von sehr kleinen Werten für sin x und cos x, die besser verbessert werden sollten als die Ausgabe der nativen Funktionen mit einige Optimierungen.
quelle