Trigonométrie de la boîte noire

29

Écrire un programme ou une fonction qui permet de distinguer les 12 fonctions trigonométriques suivantes: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh.

Votre programme reçoit l'une des fonctions ci-dessus sous forme de boîte noire et doit afficher le nom de la fonction comme indiqué ci-dessus ou la façon dont il est nommé dans votre langue.

C'est le , donc la réponse la plus courte dans chaque langue l'emporte. Vous devez montrer que votre code fonctionne correctement en incluant des cas de test avec les 12 entrées possibles. Si le langage de votre choix n'inclut pas de build-ins pour toutes les fonctions ci-dessus, vous devez fournir vos propres implémentations sensées des manquantes.

Précisions supplémentaires

  • L'utilisation de nombres complexes pour interroger la boîte noire est autorisée si les versions sous-jacentes peuvent les gérer.
  • Comme lorsque vous utilisez uniquement des nombres réels, les requêtes à la fonction de boîte noire peuvent donner des erreurs de domaine. Dans ce cas, vous devez supposer que la boîte noire ne communique que l'existence d'une erreur, mais pas de quelle fonction elle provient.dom acoshdom atanh=
  • Si au lieu d'une erreur, une autre valeur, par exemple NaNou null, est renvoyée, votre soumission devrait être en mesure de les gérer.

Merci pour les commentaires utiles du bac à sable !

Laikoni
la source
1
Mathematica peut gérer les entrées symboliques afin que la sortie de la fonction ne soit évaluée que partiellement, voire pas du tout. La différence que cela fait est que je pourrais utiliser une correspondance de modèle au lieu de calculs.
JungHwan Min
1
@JungHwanMin Si cela signifie que vous pouvez accéder aux noms de fonction à partir de la sortie symbolique, je crains que ce ne soit pas autorisé.
Laikoni

Réponses:

22

Python 3.6.4 sous Linux, 99 octets

Réponse idiote, mais:

lambda f:"asinh acos cos cosh atan atanh tan sin asin tanh sinh acosh".split()[hash(f(.029))%19%12]

Nécessite que les fonctions trigonométriques fassent partie du cmathmodule intégré pour les entrées / sorties complexes.

orlp
la source
2
@JungHwanMin Je crois que vous êtes confus. Je prends très certainement une fonction réelle. Notez que ma seule référence à l'entrée fest f(.029)- appeler la fonction avec une valeur.
orlp
1
L'avez-vous brutalisé?
mbomb007
4
@ mbomb007 Si par force brute vous voulez dire une boucle qui fait quelques centaines d'itérations en un clin d'œil, oui.
orlp
3
C'est à la fois incroyable et idiot.
Nit
1
Pertinent?
mbomb007
6

Perl 6 , 75 octets

->&f {([X~] ("","a"),<sin cos tan>,("","h")).min({abs(f(2i)-&::($_)(2i))})}

Essayez-le en ligne!

Il se trouve que les douze fonctions à discriminer sont intégrées et prennent toutes des arguments complexes.

[X~] ("", "a"), <sin cos tan>, ("", "h")génère les douze noms de fonction en réduisant les trois listes d'entrée avec une concaténation entre produits. Compte tenu de ceux-ci, .min(...)trouve celui qui présente la plus petite différence par rapport à la fonction d'entrée 2i.

Sean
la source
59 octets X peuvent être utilisés pour plusieurs termes, et quelques autres astuces pour jouer au golf
Jo King
6

C (gcc) , 178 172 octets

double d;_;f(double(*x)(double)){d=x(0.9247);_=*(int*)&d%12;puts((char*[]){"acosh","sinh","asinh","atanh","tan","cosh","asin","sin","cos","atan","tanh","acos"}[_<0?-_:_]);}

Essayez-le en ligne!

Vieux mais cool: C (gcc) , 194 octets

double d;_;f(double(*x)(double)){char n[]="asinhacoshatanh";d=x(0.9247);_=*(int*)&d%12;_=(_<0?-_:_);n[(int[]){10,5,5,0,14,10,4,4,9,14,0,9}[_]]=0;puts(n+(int[]){5,1,0,10,11,6,0,1,6,10,11,5}[_]);}

Essayez-le en ligne!

Le -lmcommutateur dans TIO est simplement à tester. Si vous pouviez écrire une implémentation parfaite des fonctions trigonométriques standard, vous obtiendriez la bonne réponse.

Explication

L'idée était de trouver une valeur d'entrée telle que lorsque j'interprète les sorties de chacune des fonctions trigonométriques comme des nombres entiers, elles aient différents restes modulo 12. Cela leur permettra d'être utilisées comme indices de tableau.

Afin de trouver une telle valeur d'entrée, j'ai écrit l'extrait suivant:

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

// Names of trig functions
char *names[12] = {"sin","cos","tan","asin","acos","atan","sinh","cosh","tanh","asinh","acosh","atanh"};

// Pre-computed values of trig functions
double data[12] = {0};

#define ABS(X) ((X) > 0 ? (X) : -(X))

// Performs the "interpret as abs int and modulo by" operation on x and i
int tmod(double x, int i) {
    return ABS((*(int*)&x)%i);
}

// Tests whether m produces unique divisors of each trig function
// If it does, it returns m, otherwise it returns -1
int test(int m) {
    int i,j;
    int h[12] = {0}; // stores the modulos

    // Load the values
    for (i = 0; i < 12; ++i)
        h[i] = tmod(data[i],m);

    // Check for duplicates
    for (i = 0; i < 12; ++i)
        for (j = 0; j < i; ++j)
            if (h[i] == h[j])
                return -1;

    return m;
}

// Prints a nicely formatted table of results
#define TEST(val,i) printf("Value: %9f\n\tsin      \tcos      \ttan      \n  \t%9f\t%9f\t%9f\na \t%9f\t%9f\t%9f\n h\t%9f\t%9f\t%9f\nah\t%9f\t%9f\t%9f\n\n\tsin      \tcos      \ttan      \n  \t%9d\t%9d\t%9d\na \t%9d\t%9d\t%9d\n h\t%9d\t%9d\t%9d\nah\t%9d\t%9d\t%9d\n\n",\
        val,\
        sin(val), cos(val), tan(val), \
        asin(val), acos(val), atan(val),\
        sinh(val), cosh(val), tanh(val),\
        asinh(val), acosh(val), atanh(val),\
        tmod(sin(val),i), tmod(cos(val),i), tmod(tan(val),i), \
        tmod(asin(val),i), tmod(acos(val),i), tmod(atan(val),i),\
        tmod(sinh(val),i), tmod(cosh(val),i), tmod(tanh(val),i),\
        tmod(asinh(val),i), tmod(acosh(val),i), tmod(atanh(val),i))

// Initializes the data array to the trig functions evaluated at val
void initdata(double val) {
    data[0] = sin(val);
    data[1] = cos(val);
    data[2] = tan(val);
    data[3] = asin(val);
    data[4] = acos(val);
    data[5] = atan(val);
    data[6] = sinh(val);
    data[7] = cosh(val);
    data[8] = tanh(val);
    data[9] = asinh(val);
    data[10] = acosh(val);
    data[11] = atanh(val);
}

int main(int argc, char *argv[]) {
    srand(time(0));

    // Loop until we only get 0->11
    for (;;) {
        // Generate a random double near 1.0 but less than it
        // (experimentally this produced good results)
        double val = 1.0 - ((double)(((rand()%1000)+1)))/10000.0;
        initdata(val);
        int i = 0;
        int m;

        // Find the smallest m that works
        do {
            m = test(++i);
        } while (m < 0 && i < 15);

        // We got there!
        if (m == 12) {
            TEST(val,m);
            break;
        }
    }

    return 0;
}

Si vous exécutez cela (qui doit être compilé avec -lm), il crachera qu'avec une valeur de 0,9247, vous obtenez des valeurs uniques.

Ensuite, j'ai réinterprété comme des nombres entiers, appliqué le module par 12 et pris la valeur absolue. Cela a donné à chaque fonction un indice. Ils étaient (de 0 à> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, sin, cos, atan, tanh, acos.

Maintenant, je pourrais simplement indexer dans un tableau de chaînes, mais les noms sont très longs et très similaires, donc je les retire à la place des tranches d'une chaîne.

Pour ce faire, je construis la chaîne "asinhacoshatanh" et deux tableaux. Le premier tableau indique le caractère de la chaîne à définir sur le terminateur nul, tandis que le second indique quel caractère de la chaîne doit être le premier. Ces tableaux contiennent: 10,5,5,0,14,10,4,4,9,14,0,9 et 5,1,0,10,11,6,0,1,6,10,11, 5 respectivement.

Enfin, il s'agissait simplement d'implémenter efficacement l'algorithme de réinterprétation en C. Malheureusement, je devais utiliser le double type, et avec exactement 3 utilisations, il était plus rapide de n'utiliser que doubletrois fois puis d'utiliser #define D double\nDDD seulement 2 caractères. Le résultat est au-dessus, une description est ci-dessous:

double d;_;                                 // declare d as a double and _ as an int
f(double(*x)(double)){                      // f takes a function from double to double
    char n[]="asinhacoshatanh";             // n is the string we will manipulate
    int a[]={10,5,5,0,14,10,4,4,9,14,0,9};  // a is the truncation index
    int b[]={5,1,0,10,11,6,0,1,6,10,11,5};  // b is the start index
    d=x(0.9247);                            // d is the value of x at 0.9247
    _=*(int*)&d%12;                         // _ is the remainder of reinterpreting d as an int and dividing by 12
    _=(_<0?-_:_);                           // make _ non-negative
    n[a[_]]=0;                              // truncate the string
    puts(n+b[_]);}                          // print the string starting from the correct location

Edit: Malheureusement, l'utilisation d'un tableau brut est en fait plus courte, donc le code devient beaucoup plus simple. Néanmoins, le tranchage des cordes était amusant. En théorie, un argument approprié pourrait en fait produire les bonnes tranches avec des mathématiques.

LambdaBeta
la source
Vous pouvez économiser 20 octets en remplaçant puts(...)parprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel
Vous pouvez économiser 5 octets en compilant -DD=doubleet en remplaçant tous les doubles de votre code par D. Notez que l'indicateur doit être compté pour le nombre total d'octets.
Trois octets supplémentaires peuvent être éliminés en remplaçant char*[]par int*[]et en changeant l'opérateur ternaire (? :) enabs(_)
6

Python 3.6.5 sous Linux, 90 85 octets

h=hash;lambda f:h(f(.0869))%3%2*"a"+"tscaionns"[h(f(.14864))%3::3]+h(f(.511))%5%2*"h"

Cela s'appuie sur la réponse de orlp ; mais au lieu de trouver 1 chiffre magique, nous en trouvons 3! Cela permet simplement d'économiser des octets en évitant de placer plusieurs fois les littéraux de chaîne pour "sin", "cos" et "tan", au lieu de cela de construire la réponse une partie à la fois.

Le premier nombre magique est utilisé pour déterminer s'il s'agit d'une des fonctions trigonométriques "arc", en ajoutant un "a" en conséquence, le second pour savoir s'il s'agit d'une des fonctions basées sur "sin", "cos" ou "tan", en sélectionnant la chaîne appropriée, et la troisième pour savoir s'il s'agit d'une des fonctions hyperboliques, en ajoutant un "h" en conséquence.

Comme la réponse d'orlp, il utilise les fonctions du cmathmodule intégré de Python en entrée.

Enregistré 5 octets en utilisant l'indexation de tranche dans la chaîne du milieu

Trouver les nombres magiques

Pour être complet, voici (plus ou moins) le script que j'ai utilisé pour trouver ces nombres magiques. J'ai surtout travaillé directement dans un terminal python, donc le code est en désordre, mais il fait le travail.

import cmath
fns = [(fn, getattr(cmath, fn)) for fn in ["sin","cos","tan","asin","acos","atan","sinh","cosh","tanh","asinh","acosh","atanh"]]

count_length = lambda num, modulus, base_modulus : len(str(num).rstrip('0').lstrip('0')) + (1 + len(str(modulus)) if modulus != base_modulus else 0)

min_length = float("inf")
min_choice = None
for modulus in range(2,10):
   for i in range(1,100000):
      num = i/100000.
      is_valid = True
      for fn in fns:
         val = hash(fn[1](num))%modulus%2
         if (val == 0 and fn[0][0]=="a") or (val == 1 and fn[0][0]!="a"):
            is_valid = False
      if is_valid:
         length = count_length(num, modulus, 2)
         if length < min_length:
            min_length = length
            min_choice = (modulus,num)
print(min_choice)

min_length = float("inf")
min_choice = None
for modulus in range(3,10):
   for i in range(100000):
      num = i/100000.
      mapping = {}
      is_valid = True
      for fn in fns:
         fn_type = "sin" if "sin" in fn[0] else "cos" if "cos" in fn[0] else "tan"
         val = hash(fn[1](num))%modulus%3
         if val in mapping and mapping[val] != fn_type:
            is_valid = False
            break
         mapping[val] = fn_type
      if is_valid:
         length = count_length(num, modulus, 3)
         if length < min_length:
            min_length = length
            min_choice = (modulus, num, mapping)
print(min_choice)

min_length = float("inf")
min_choice = None
for modulus in range(2,10):
   for i in range(1,100000):
      num = i/100000.
      is_valid = True
      for fn in fns:
         val = hash(fn[1](num))%modulus%2
         if (val == 0 and fn[0][-1]=="a") or (val == 1 and fn[0][-1]!="a"):
            is_valid = False
      if is_valid:
         length = count_length(num, modulus, 2)
         if length < min_length:
            min_length = length
            min_choice = (modulus,num)
print(min_choice)
nthistle
la source
1
Grande deuxième réponse! Pourriez-vous partager le programme que vous avez utilisé pour trouver les nombres magiques?
mbomb007
Merci! J'ai juste ajouté du code pour trouver les nombres magiques à la réponse, bien que ce ne soit pas terriblement joli.
nthistle
4

Python , 108 94 90 octets

Compare le résultat de la fonction d'entrée aux résultats de toutes les fonctions de la valeur .2.

from cmath import*
lambda f:[w for w in globals()if w[-1]in'shn'and eval(w)(.2)==f(.2)][0]

Essayez-le en ligne

-14 octets par Jonathan Allen
-4 octets par Rod

mbomb007
la source
Pas besoin de re, obtenez simplement ceux dont vous avez besoin avec le découpage: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](réécrit pour fonctionner sur TIO car l'importation doit avoir lieu avant d=dir(cmath)mais F=doit être dans l'en-tête pour ne pas être comptée).
Jonathan Allan
Très agréable! Merci
mbomb007
4

Dyalog APL , 25 21 19 octets

(8-(2○⍨8-⍳15)⍳⎕2)∘○

Essayez-le en ligne!

-3 grâce à H.PWiz
-2 grâce à ngn

Va à travers toutes les fonctions trigonométriques requises (qui sont dans APL 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) plus quelques autres choses (cela va à travers -7..7), trouve celle qui correspond input○2, et sort celle qui "avec" , qui sort commenum∘○

dzaima
la source
3

C (gcc) avec -lm, 374 346 324 octets

Merci à Giacomo Garabello pour les suggestions.

J'ai pu économiser un peu plus d'espace en faisant en sorte qu'une macro d'aide effectue le collage de jetons en plus de ma macro d'origine qui effectue la chaîne.

Dans les tests, j'ai utilisé quelques fonctions trig non-bibliothèque pour confirmer la validité des résultats. Comme les résultats entre les fonctions bibliothèque et non bibliothèque n'étaient pas exactement la même valeur en virgule flottante, j'ai comparé la différence des résultats avec une petite valeur ε au lieu d'utiliser l'égalité.

#include <math.h>
#define q(f)f,#f,
#define _(f,g)q(f##sin##g)q(f##cos##g)q(f##tan##g)
#define p for(i=0;i<24;i+=2)
typedef double(*z)(double);*y[]={_(,)_(a,)_(,h)_(a,h)};i,x;*f(z g){int j[24]={0};char*c;double w;for(x=0;x++<9;)p!j[i]&isnan(w=((z)y[i])(x))-isnan(g(x))|fabs(w-g(x))>1E-9?j[i]=1:0;p!j[i]?c=y[i+1]:0;return c;}

Essayez-le en ligne!

ErikF
la source
J'ai réussi à supprimer 14 octets. Dans le TIO, vous pouvez trouver les détails. Essayez-le en ligne!
Giacomo Garabello
+1 de ma part, mais j'ai trouvé une solution inférieure à 200 en utilisant une stratégie différente :)
LambdaBeta
3

JavaScript, 76 67 66 octets

Pas joli mais je suis allé beaucoup trop loin dans le terrier du lapin avec ceci sur quelques bières pour ne pas le poster. Dérivé indépendamment de la solution de Nit.

b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)

Essayez-le en ligne

  • 6 octets enregistrés grâce à Neil
  • 1 bye sauvé grâce à l4m2
Hirsute
la source
b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (bien que je ne sache pas vraiment pourquoi convertir en String pour comparer)
l4m2
Je ne sais pas pourquoi je n'y ai pas pensé. Merci, @ l4m2.
Shaggy
@ l4m2 Nous devons NaNcomparer égal à NaN, c'est donc ça ou Object.is.
Neil
2

Rubis , 71 67 octets

->g{Math.methods.find{|h|g[0.5]==Math.send(h,0.5)rescue p}||:acosh}

Essayez-le en ligne!

Asone Tuhid
la source
2

JavaScript, 108 70 octets

Je n'ai pas essayé le golf en Javascript pur depuis des lustres, donc je suis sûr qu'il y a des choses à améliorer ici.

t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'')

Assez simple, vérifie chaque fonction du Mathprototype par rapport à une valeur arbitraire (0,9, de nombreuses autres valeurs fonctionnent probablement) et compare avec le résultat de la fonction de boîte noire.
Testé dans Google Chrome, se cassera si la fonction de boîte noire d'entrée n'est pas l'un des trigs.

Coupez une tonne d'octets grâce à Shaggy et Neil.

const answer = t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');
const tests = [Math.sin, Math.cos, Math.tan, Math.asin, Math.acos, Math.atan, Math.sinh, Math.cosh, Math.tanh, Math.asinh, Math.acosh, Math.atanh];

tests.forEach(test => console.log(test + ' yields ' + answer(test)));

Lente
la source
1
Très similaire à la solution sur laquelle je travaillais sur quelques bières mais que je n'arrivais pas à comprendre. 2 économies rapides que je peux repérer: 0.3 -> .3et affecter Mathà l' m intérieur getOwnPropertyNames() .
Shaggy
1
J'ai réussi à obtenir ce jusqu'à 71 octets: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. J'ai également remarqué que @Shaggy était utilisé find. Le +''fait une chaîne comparer, ce qui signifie que nous n'avons qu'à vérifier un point. Le ,0fait de nous sauter Math.atan2.
Neil
@Neil, il ne semble pas ,0 nécessaire: tio.run/##Lc6xDoMgEMbxvU/RMEFq2TvgG1jdjYknomLkzghp7dPTqEz/…
Shaggy
@Shaggy Je suppose que cela dépend de l'implémentation; dans Firefox, atan2précède acoshdans le tableau retourné par Object.getOwnPropertyNames.
Neil
Si quelqu'un getOwnPropertyNamesse posait la question, cette solution fonctionne car la première non-fonction de est Math.E, et toutes les fonctions trig sont énumérées avant cela.
MattH
2

R , 75 octets

function(b)Find(function(x)get(x)(1i)==b(1i),apropos('(sin|cos|tan)(h|$)'))

Essayez-le en ligne!

Pour l'instant (R v3.5) ça marche.
Si dans une future version R il sera ajouté une fonction correspondant à ce regex, alors qui sait: P

  • -2 octets grâce à @Giuseppe
  • -9 octets grâce à @JayCe
  • -2 octets utilisant Findau lieu defor
digEmAll
la source
sensationnel. Très agréable! Je pense que cela 1ifonctionne aussi bien que -1ipour -2 octets.
Giuseppe
@Giuseppe: J'étais sûr de l'avoir testé et ça ne fonctionnait pas ... mais probablement c'était seulement mon imagination: D
digEmAll
très agréable! Fonctionne sur TIO, dépend de votre configuration probablement dans le cas général: tio
JayCe
@JayCe: obtenir l'environnement à travers la position est risqué ... par exemple, cela ne fonctionne pas dans RStudio ... heureusement, j'ai trouvé une autre fonction recherchant les objets partout, avec le même nombre d'octets :)
digEmAll
enfin ... GET est plus court (77)
JayCe
1

HP 49G RPL, 88,0 octets à l'exclusion de l'en-tête de programme de 10 octets

Une autre solution utilisant des nombres complexes! Saisissez-le et exécutez-le en mode COMPLEXE, APPROX. Prend la fonction sur la pile.

2. SWAP EVAL { SIN COS TAN ASIN ACOS ATAN SINH COSH TANH ASINH ACOSH ATANH }
DUP 1. << 2. SWAP EVAL >> DOLIST ROT - ABS 0. POS GET

(les nouvelles lignes n'ont pas d'importance)

Pour la constante 2.0, les douze fonctions trigonométriques sont définies dans le plan complexe, nous évaluons donc toutes les douze et voyons laquelle correspond. Cette fois, la solution itérative est plus longue (111,5 octets) en raison du brassage de pile nécessaire pour l'obtenir. RPL, pour autant que je sache, ne vous permet pas de sortir d'une boucle tôt.

Jason
la source
Dans le cas où ils sont retournés en majuscules, c'est bien maintenant que j'ai édité le défi.
Laikoni
@JungHwanMin Ils sont en majuscules. Merci pour la capture! Il peut être modifié en minuscules avec ->STR DUP SIZE 3 - " " " " IFTE XOR34,5 octets. (ceux-ci sont censés être de 4 et 3 espaces, respectivement)
Jason
1

Perl 6 , 39 octets

{i.^methods.first({try $^a.(i)==.(i)})}

Essayez-le en ligne!

Par l'apparence des choses, l'un des rares à utiliser l'introspection. ivoici le nombre complexe, dont la valeur est unique pour chaque fonction trig, donc en parcourant toutes les méthodes, nous pouvons trouver la méthode correspondante et cracher implicitement son nom. Le tryest nécessaire car certaines méthodes (indésirables) ont une signature différente.

Phil H
la source