Calculs rapides de trigonométrie
Votre tâche consiste à créer un programme qui peut calculer le sinus, le cosinus et la tangente d'un angle en degrés.
Règles
- Pas de fonctions de trigonométrie intégrées (même pas sécantes, cosécantes et cotangentes si votre langue les possède).
- Vous pouvez utiliser des tables de recherche, mais leur taille totale ne doit pas dépasser 3 000 membres (pour les trois opérations réunies). Veuillez lui faire lire les tableaux d'un fichier (par exemple
trig.lookup
) afin qu'ils ne confondent pas le code. - Pas d'accès au réseau.
- Vous devez arrondir correctement votre sortie comme expliqué ci-dessous. N'utilisez pas de plancher ou de plafond.
- Vous pouvez utiliser n'importe quelle méthode pour calculer les valeurs, par exemple des fractions continues , tant qu'elle est correcte à 7 chiffres significatifs.
- Votre code doit pouvoir se chronométrer. Excluez les opérations d'E / S de fichiers de votre temps - il suffit donc de chronométrer la ou les fonctions qui effectuent le trig et tout arrondi.
- Je dois pouvoir exécuter votre code. Veuillez poster un lien vers un compilateur / interprète disponible gratuitement et donner les instructions nécessaires pour compiler / exécuter le code (par exemple quelles options passer à GCC).
- Des échappatoires standard s'appliquent.
Format d'entrée
- Lisez à partir d'un fichier appelé,
trig.in
sauf si votre langue ne prend pas en charge les E / S de fichiers. - Les angles sont compris entre 0 et 360 inclus.
- L'entrée consistera en angles de dix chiffres significatifs en chiffres décimaux, séparés par de nouvelles lignes. Par exemple:
90,00000000
74,54390000
175,5000000
Format de sortie
- Pour chaque angle fourni, vous devez afficher ses sinus, cosinus et tangentes sur 7 chiffres significatifs, séparés par des espaces, sur une seule ligne. Utilisez la «notation scientifique», par exemple
1.745329E-5
pourtan 0.001
ou1.000000E+0
poursin 90
. - Désignez l'infini ou NaN par
n
, par exemple, la sortie de90.00000000
devrait être1.000000 0.000000 n
. - Si l'entrée est composée de trois angles séparés par des retours à la ligne, votre sortie doit être composée de trois lignes contenant chacune le sinus, le cosinus et la tangente.
- Vous ne pouvez rien produire d'autre.
- Sortie vers un fichier appelé
trig.out
sauf si votre langue ne prend pas en charge les E / S de fichiers.
Notation
- code le plus rapide . Le défi est d'écrire un programme qui calcule ces trois valeurs le plus rapidement possible. Le temps le plus rapide l'emporte.
- Tout le monde recevra la même entrée de test sous plusieurs angles.
- Les temps seront enregistrés sur ma machine.
- Votre score est la moyenne de trois runs sur la même entrée (vous ne pouvez évidemment rien enregistrer entre les runs).
- Temps de compilation non inclus. Ce défi concerne plus la méthode utilisée que la langue. (Si quelqu'un pouvait me montrer comment j'exclurais le temps de compilation pour des langages tels que Java, je serais très reconnaissant)
- Ma machine est une installation d'Ubuntu 14.04. Les statistiques du processeur sont sur Pastebin (obtenues en exécutant
cat /proc/cpuinfo
). - Je modifierai votre temps dans votre réponse lorsque je l'aurai testé.
math
fastest-code
trigonometry
Géobits
la source
la source
sin
,cos
ettan
est sur une nouvelle ligne. Dois-je le modifier pour afficher les réponses sur une seule ligne?Réponses:
Fortran 90
J'utilise la méthode CORDIC avec un tableau pré-tabulé de 60 valeurs d'arctan (voir l'article Wiki pour plus de détails sur la raison pour laquelle cela est nécessaire).
Ce code nécessite un fichier,
trig.in
avec toutes les valeurs des sauts de ligne à stocker dans le même dossier que l'exécutable Fortran. Compiler ceci est,où
file
est le nom de fichier que vous lui donnez (ceSinCosTan.f90
serait probablement plus simple, bien qu'il ne soit pas nécessaire de faire correspondre le nom du programme et le nom du fichier). Si vous avez le compilateur Intel, je vous recommande d'utilisercar le
-xHost
(qui n'existe pas pour gfortran) fournit des optimisations de niveau supérieur disponibles pour votre processeur.Mes tests me donnaient environ 10 microsecondes par calcul lors du test de 1000 angles aléatoires en utilisant gfortran 4.4 (4.7 ou 4.8 est disponible dans les dépôts Ubuntu) et environ 9.5 microsecondes en utilisant ifort 12.1. Le test de seulement 10 angles aléatoires se traduira par un temps indéterminable à l'aide des routines Fortran, car la routine de synchronisation est précise à la milliseconde et les calculs simples indiquent qu'il devrait prendre 0,100 millisecondes pour exécuter les 10 nombres.
EDIT Apparemment, je chronométrais IO, ce qui (a) rendait le chronométrage plus long que nécessaire et (b) est contraire au point n ° 6. J'ai mis à jour le code pour refléter cela. J'ai également découvert que l'utilisation d'un
kind=8
entier avec le sous-programme intrinsèquesystem_clock
donne une précision en microsecondes.Avec ce code mis à jour, je calcule maintenant chaque ensemble de valeurs des fonctions trigonométriques en environ 0,3 microsecondes (les chiffres significatifs à la fin varient d'un cycle à l'autre, mais il oscille constamment près de 0,31 us), une réduction significative par rapport à la précédente. itération qui chronomètre IO.
la source
Python 2.7.x ou Java (faites votre choix)
Un interpréteur Python gratuit peut être téléchargé à partir d' ici .
Un interpréteur Java gratuit peut être téléchargé à partir d' ici .
Le programme peut prendre des entrées à la fois à partir d'un fichier nommé
trig.in
situé dans le même répertoire que le fichier programme. L'entrée est séparée par des retours à la ligne.J'ai fait cela à l'origine en python parce que - eh bien, j'adore le python. Mais comme je veux aussi essayer de gagner, je l'ai réécrit en java après ...
Version Python: j'ai obtenu environ 21µs par exécution sur mon ordinateur. J'ai obtenu environ 32µs en l'exécutant sur IDEone .
Version Java: j'obtiens environ 0,4 µs par exécution sur mon ordinateur et 1,8 µs sur IDEone .
Spécifications informatiques:
Tester:
sin
,cos
ettan
tous les angles d'entrée.L'entrée de test utilisée pour les deux est la suivante:
À propos du code:
La prémisse de ce programme était d'estimer
sin
et d'cos
utiliser leurs polynômes de Taylor avec 14 termes, ce qui, selon moi, était nécessaire pour avoir une estimation d'erreur inférieure à 1e-8. Cependant, j'ai trouvé qu'il était plus rapide à calculersin
quecos
, alors j'ai décidé de calculer à la placecos
en utilisantcos=sqrt(1-sin^2)
Version Python:
Version Java:
la source
cos
calcul est exagéré, je ferais justesin(x+90degrees)
sin
à la fois comme fonction et comme variable. Je pensais que ce serait plus rapide de ne pas avoir à passer quelque chose à lasin()
deuxième fois, mais je vais comparer les deux pour voir si c'est vraiment le cas. Avez-vous l'impression que lacopySign()
fonction est plus lente que l'addition de choses comme dans masin()
fonction?Octave (ou Matlab) & C
Un processus de construction un peu compliqué, mais une nouvelle approche et les résultats étaient encourageants.
L'approche consiste à générer des polynômes quadratiques approximatifs pour chaque degré. Donc degré = [0, 1), degré = [1, 2), ..., degré = [359, 360) auront chacun un polynôme différent.
Octave - partie bâtiment
Octave est accessible au public - Google
download octave
.Cela détermine le polynôme quadratique le mieux adapté à chaque degré.
Enregistrer sous
build-fast-trig.m
:C - partie bâtiment
Cela convertit les doubles au format texte en format binaire natif sur votre système.
Enregistrer sous
build-fast-trig.c
:Compiler:
Génération du fichier de coefficients
Courir:
Maintenant nous avons
qcoeffs.dat
comme fichier de données à utiliser pour le programme réel.C - partie à déclenchement rapide
Enregistrer sous
fast-trig.c
:Compiler:
Courir:
Il lira
trig.in
, enregistreratrig.out
et imprimera pour consolider le temps écoulé avec une précision en millisecondes.Selon les méthodes de test utilisées, il peut échouer sur certaines entrées, par exemple:
La sortie correcte devrait être
0.000000e+00 1.000000e+00 0.000000e+00
. Si les résultats sont validés à l'aide de chaînes, l'entrée échouera, s'ils sont validés à l'aide d'une erreur absolue, par exemplefabs(actual - result) < 1e-06
, l'entrée passera.L'erreur absolue maximale pour
sin
etcos
était ≤ 3e-07. Cartan
, comme le résultat n'est pas limité à ± 1 et que vous pouvez diviser un nombre relativement grand par un nombre relativement petit, l'erreur absolue pourrait être plus grande. De -1 ≤ tan (x) ≤ +1, l'erreur absolue maximale était ≤ 4e-07. Pour tan (x)> 1 et tan (x) <-1, l' erreur relative maximale , par exemplefabs((actual - result) / actual)
était généralement <1e-06 jusqu'à ce que vous atteigniez la zone de (90 ± 5) ou (270 ± 5) degrés, puis le l'erreur s'aggrave.Lors des tests, le temps moyen par entrée unique était de (1,053 ± 0,007) µs, ce qui sur ma machine était environ 0,070 µs plus rapide que le natif
sin
etcos
,tan
étant défini de la même manière.la source
Cobra
Compilez-le avec
cobra filename -turbo
Tests: AMD FX6300 à 5,1 GHz
Le test 360 * 10000 utilisé par la réponse C s'exécute en 365 ms (vs 190 ms)
Le test à 4 entrées utilisé par les réponses Python et Java s'exécute en 0,32µs (vs 30µs, 3µs)
Le test de 1000 angles aléatoires utilisé par la réponse Fortran fonctionne à 100 ns par angle (vs 10 µs)
la source
C
Voici ma tentative. Cela fonctionne comme ceci:
Construisez un tableau de toutes les valeurs de sin (x) de 0 à 450 degrés. De manière équivalente, il s'agit de toutes les valeurs de cos (x) de -90 à 360 degrés. Avec 2926 éléments, il y a suffisamment d'espace pour une valeur tous les 1 / 6,5 degrés. L'unité de programme est donc de 1 / 6,5 degrés, et il y a 585 unités en un quart de tour.
Convertissez les degrés d'entrée en unités de programme (multipliez par
6.5==110.1 binary.
) Trouvez dans le tableau les valeurs les plus proches pour sin et cos. puis convertissez la partie restante de l'entrée (dx) en radians.appliquer la formule
sin(x+dx) == sin x +(d(sin x)/dx)*dx.
noter que(d(sin x)/dx)==cos x,
mais seulement si nous utilisons des radians.malheureusement, ce n'est pas assez précis en soi, donc un autre terme est requis, basé sur la dérivée suivante
d2(sin x)/dx2 == -sin x.
Cela doit être multiplié pardx*dx/2
(je ne sais pas d'où vient le facteur 2, mais cela fonctionne.)Suivez la procédure analogue pour
cos x
, puis calculeztan x == sin x / cos x
.Code
Il y a environ 17 opérations en virgule flottante ici. Cela peut être quelque peu amélioré. Le programme contient la création de tables et la sortie de test en utilisant les fonctions trig natives, mais pas l'algorithme. J'ajouterai le timing et éditerai pour me conformer aux exigences d'E / S plus tard (j'espère ce week-end.) Il correspond à la sortie de la fonction native, sauf pour les très petites valeurs de sin x et cos x, qui devraient être améliorables à mieux que la sortie de la fonction native avec quelques ajustements.
la source