Trigonometría de caja negra

29

Escribir un programa o función que puede distinguir las siguientes 12 funciones trigonométricas: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh.

Su programa recibe una de las funciones anteriores como recuadro negro y debe mostrar el nombre de la función como se indica anteriormente o la forma en que se nombra en su idioma.

Este es el , por lo que gana la respuesta más corta en cada idioma. Debe mostrar que su código funciona correctamente al incluir casos de prueba con las 12 entradas posibles. Si el idioma de su elección no incluye complementos para todas las funciones anteriores, debe proporcionar sus propias implementaciones sensatas de las que faltan.

Aclaraciones adicionales

  • El uso de números complejos para consultar el cuadro negro está permitido si los complementos subyacentes pueden manejarlos.
  • Como cuando se usan solo números reales, las consultas a la función de recuadro negro pueden dar errores de dominio. En este caso, debe suponer que el cuadro negro solo comunica la existencia de un error, pero no de qué función se origina.dom acoshdom atanh=
  • Si en lugar de un error se devuelve algún otro valor, por ejemplo, NaNor null, entonces su envío debería poder manejarlos.

Gracias por los útiles comentarios de sandbox .

Laikoni
fuente
1
Mathematica puede manejar entradas simbólicas para que la salida de la función solo se evalúe parcialmente, si es que se evalúa. La diferencia que hace es que podría usar alguna coincidencia de patrones en lugar de cálculos.
JungHwan Min
1
@JungHwanMin Si eso significa que puede acceder a los nombres de las funciones desde la salida simbólica, me temo que no está permitido.
Laikoni

Respuestas:

22

Python 3.6.4 en Linux, 99 bytes

Una respuesta un poco tonta, pero:

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

Requiere que las funciones trigonométricas sean una del cmathmódulo incorporado para entrada / salida compleja.

orlp
fuente
2
@JungHwanMin Creo que estás confundido. Ciertamente tomo una función real. Tenga en cuenta que mi única referencia a la entrada fes f(.029): llamar a la función con un valor.
orlp
1
¿Bruteforce esto?
mbomb007
44
@ mbomb007 Si por fuerza bruta quieres decir un bucle que hace un par de cientos de iteraciones en un abrir y cerrar de ojos, sí.
orlp
3
Esto es sorprendente y tonto.
Nit
1
¿Pertinente?
mbomb007
6

Perl 6 , 75 bytes

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

Pruébalo en línea!

Como sucede, las doce funciones a discriminar están integradas y todas toman argumentos complejos.

[X~] ("", "a"), <sin cos tan>, ("", "h")genera los doce nombres de funciones al reducir las tres listas de entrada con concatenación de productos cruzados. Dados estos, .min(...)encuentra el que tiene la menor diferencia de la función de entrada 2i.

Sean
fuente
59 bytes X se pueden usar para varios términos, y algunos otros trucos para los bytes de golf
Jo King
6

C (gcc) , 178 172 bytes

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?-_:_]);}

Pruébalo en línea!

Viejo pero genial: C (gcc) , 194 bytes

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}[_]);}

Pruébalo en línea!

El -lmcambio en TIO es simplemente para probar. Si pudiera escribir una implementación perfecta de las funciones trigonométricas estándar, obtendría la respuesta correcta.

Explicación

La idea era encontrar algún valor de entrada tal que cuando interprete las salidas de cada una de las funciones trigonométricas como enteros, tengan diferentes restos módulo 12. Esto permitirá que se utilicen como índices de matriz.

Para encontrar dicho valor de entrada, escribí el siguiente fragmento:

#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 ejecuta eso (que debe compilarse con -lm), escupirá que con un valor de 0.9247 obtendrá valores únicos.

Luego reinterpreté como enteros, apliqué el módulo por 12 y tomé el valor absoluto. Esto le dio a cada función un índice. Eran (de 0 -> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, sin, cos, atan, tanh, acos.

Ahora podría simplemente indexar en una serie de cadenas, pero los nombres son muy largos y muy similares, por lo que en su lugar los saco de los segmentos de una cadena.

Para hacer esto, construyo la cadena "asinhacoshatanh" y dos matrices. La primera matriz indica qué carácter de la cadena establecer en el terminador nulo, mientras que la segunda indica qué carácter de la cadena debe ser el primero. Estas matrices contienen: 10,5,5,0,14,10,4,4,9,14,0,9 y 5,1,0,10,11,6,0,1,6,10,11, 5 respectivamente.

Finalmente, fue solo una cuestión de implementar el algoritmo de reinterpretación de manera eficiente en C. Lamentablemente tuve que usar el tipo doble, y con exactamente 3 usos, fue más rápido usar doubletres veces que #define D double\nDDD solo 2 caracteres. El resultado está arriba, una descripción está abajo:

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

Editar: Desafortunadamente, solo usar una matriz cruda es en realidad más corto, por lo que el código se vuelve mucho más simple. No obstante, el corte de cuerdas fue divertido. En teoría, un argumento apropiado podría llegar a las rebanadas correctas por sí solo con algunas matemáticas.

LambdaBeta
fuente
Puede guardar 20 bytes reemplazando puts(...)conprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel
Puede guardar 5 bytes compilando -DD=doubley reemplazando todos los doubles en su código con D. Tenga en cuenta que la bandera debe contarse para el total de bytes.
Otros tres bytes adicionales se pueden arrojar mediante la sustitución char*[]con int*[], y cambiando el operador ternario (:) para una?abs(_)
6

Python 3.6.5 en Linux, 90 85 bytes

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

Esto se basa en la respuesta de orlp ; pero en lugar de encontrar 1 número mágico, ¡encontramos 3! Básicamente, esto solo ahorra bytes al evitar poner los literales de cadena para "sin", "cos" y "tan" varias veces, en lugar de construir la respuesta una parte a la vez.

El primer número mágico se usa para determinar si es una de las funciones trigonométricas de "arco", anteponiendo una "a" en consecuencia, la segunda para determinar si es una de las funciones basadas en "pecado", "cos" o "bronceado", seleccionando la cadena apropiada y la tercera para determinar si es una de las funciones hiperbólicas, agregando una "h" en consecuencia.

Al igual que la respuesta de orlp, utiliza las funciones del cmathmódulo incorporado de Python como entrada.

Guardado 5 bytes mediante el uso de indexación de corte en la cadena del medio

Encontrar los números mágicos

Para completar, aquí está (más o menos) el guión que usé para encontrar estos números mágicos. La mayoría de las veces trabajé directamente en una terminal de Python, por lo que el código es desordenado, pero hace el trabajo.

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
fuente
1
Gran segunda respuesta! ¿Te importaría compartir el programa que usaste para encontrar los números mágicos?
mbomb007
¡Gracias! Acabo de agregar código para encontrar los números mágicos a la respuesta, aunque no es terriblemente bonita.
nthistle
4

Python , 108 94 90 bytes

Compara el resultado de la función de entrada con los resultados de todas las funciones para el valor .2.

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

Pruébalo en línea

-14 bytes por Jonathan Allen
-4 bytes por Rod

mbomb007
fuente
No es necesario re, solo obtenga los necesarios con el corte: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](reescrito para trabajar en TIO ya que la importación debe ocurrir antes d=dir(cmath)pero F=debe estar en el encabezado para no contarse).
Jonathan Allan
¡Muy agradable! Gracias
mbomb007
4

Dyalog APL , 25 21 19 bytes

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

Pruébalo en línea!

-3 gracias a H.PWiz
-2 gracias a ngn

Atraviesa todas las funciones trigonométricas requeridas (que en APL son 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) más algunas cosas más (esto atraviesa -7..7), encuentra cuál coincide input○2y genera ese "con" , que genera comonum∘○

dzaima
fuente
3

C (gcc) con -lm, 374 346 324 bytes

Gracias a Giacomo Garabello por las sugerencias.

Pude ahorrar un poco más de espacio al hacer que una macro auxiliar pegue tokens además de mi macro original que hace cadenas.

En las pruebas, utilicé un par de funciones trigonométricas que no son de biblioteca para confirmar la validez de los resultados. Como los resultados entre las funciones de biblioteca y no biblioteca no eran exactamente el mismo valor de coma flotante, comparé la diferencia de los resultados con un valor pequeño ε en lugar de usar la igualdad.

#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;}

Pruébalo en línea!

ErikF
fuente
Logré eliminar 14 bytes. En el TIO puedes encontrar los detalles. Pruébalo en línea!
Giacomo Garabello
+1 de mi parte, pero encontré una solución sub 200 usando una estrategia diferente :)
LambdaBeta
3

JavaScript, 76 67 66 bytes

No es bonito, pero fui demasiado lejos por la madriguera del conejo con esto en unas cervezas para no publicarlo. Derivado independientemente de la solución de Nit.

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

Pruébalo en línea

  • Guardado 6 bytes gracias a Neil
  • Guardado 1 bye gracias a l4m2
Lanudo
fuente
b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (aunque no sé por qué convertir a String para comparar)
l4m2
No sé por qué no pensé en eso. Gracias, @ l4m2.
Shaggy
@ l4m2 Necesitamos NaNcomparar igual a NaN, así que es eso o Object.is.
Neil
2

JavaScript, 108 70 bytes

No he probado el golf en Javascript puro en años, así que estoy seguro de que hay cosas que mejorar aquí.

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

Bastante sencillo, Mathcompara todas las funciones del prototipo con un valor arbitrario (0.9, muchos otros valores probablemente funcionan) y lo compara con el resultado de la función de recuadro negro.
Probado en Google Chrome, se romperá si la función de cuadro negro de entrada no es uno de los desencadenantes.

Cortar una tonelada de bytes gracias a Shaggy y 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)));

Liendre
fuente
1
Muy similar a la solución en la que estaba trabajando con unas pocas cervezas, pero no pude entenderlo. 2 ahorros rápidos que puedo detectar: 0.3 -> .3y asignar Matha m dentro getOwnPropertyNames() .
Shaggy
1
He conseguido esto a 71 bytes: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. Noté que @Shaggy también lo usaba find. Se +''compara una cadena, lo que significa que solo tenemos que verificar un punto. El ,0nos hace saltar Math.atan2.
Neil
@Neil, no parece que ,0 sea ​​necesario: tio.run/##Lc6xDoMgEMbxvU/RMEFq2TvgG1jdjYknomLkzghp7dPTqEz/…
Shaggy
@Shaggy, supongo que depende de la implementación; en Firefox, atan2precede acoshen la matriz devuelta por Object.getOwnPropertyNames.
Neil
Si alguien se preguntaba, esta solución funciona porque la primera no función de getOwnPropertyNameses Math.E, y todas las funciones trigonométricas se enumeran antes.
MattH
2

R , 75 bytes

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

Pruébalo en línea!

Por el momento (R v3.5) funciona.
Si en una futura versión R se agregará una función que coincida con esta expresión regular, entonces quién sabe: P

  • -2 bytes gracias a @Giuseppe
  • -9 bytes gracias a @JayCe
  • -2 bytes usando en Findlugar defor
digEmAll
fuente
Guau. ¡Muy agradable! Creo que 1ifunciona tan bien como -1ipara -2 bytes.
Giuseppe
@Giuseppe: Estaba seguro de que lo había probado y no estaba funcionando ... pero probablemente fue solo mi imaginación: D
digEmAll
¡muy agradable! Funciona en TIO, depende de su configuración, probablemente en el caso general: tio
JayCe
@ JayCe: hacer que el entorno se posicione es arriesgado ... por ejemplo, no funciona en RStudio ... afortunadamente encontré otra función buscando los objetos en todas partes, con el mismo bytecount :)
digEmAll
finalmente ... GET es más corto (77)
JayCe
1

HP 49G RPL, 88.0 bytes excluyendo el encabezado del programa de 10 bytes

¡Otra solución usando números complejos! Ingrese y ejecútelo en modo COMPLEJO, APROX. Toma la función en la pila.

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

(las nuevas líneas no importan)

Para la constante 2.0, las doce funciones trigonométricas se definen en el plano complejo, por lo que solo evaluamos las doce y vemos cuál coincide. Esta vez, la solución iterativa es más larga (111.5 bytes) debido a la combinación de pila necesaria para obtenerla. RPL, hasta donde yo sé, no te permite salir de un ciclo temprano.

Jason
fuente
En caso de que se devuelvan como mayúsculas, está bien ahora que edité el desafío.
Laikoni
@JungHwanMin Son mayúsculas. Gracias por la captura! Se puede modificar a minúsculas con ->STR DUP SIZE 3 - " " " " IFTE XOR34.5 bytes. (se supone que son 4 y 3 espacios, respectivamente)
Jason
1

Perl 6 , 39 bytes

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

Pruébalo en línea!

Por lo que parece, uno de los pocos que usa la introspección. iAquí está el número complejo, cuyo valor es único para cada función trigonométrica, por lo que al iterar a través de todos los métodos podemos encontrar el método coincidente y escupir implícitamente su nombre. El tryes necesaria, ya que algunos (no deseados) métodos tienen la una firma diferente.

Phil H
fuente