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 -lm
cambio 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 double
tres 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.