C (gcc) , 178 172 bajtów
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?-_:_]);}
Wypróbuj online!
Stare, ale fajne: C (gcc) , 194 bajty
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}[_]);}
Wypróbuj online!
-lm
Przełącznik w TIO jest jedynie do testu. Jeśli potrafisz napisać idealną
implementację standardowych funkcji trig, uzyskasz właściwą odpowiedź.
Wyjaśnienie
Pomysł polegał na znalezieniu pewnej wartości wejściowej, takiej, że kiedy interpretuję wyniki każdej z funkcji triggera jako liczby całkowite, mają one pozostałe resztki modulo 12. Pozwoli to na ich wykorzystanie jako indeksów tablicowych.
Aby znaleźć taką wartość wejściową, napisałem następujący fragment:
#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;
}
Jeśli uruchomisz to (które należy skompilować z -lm), wypluje to, że o wartości 0.9247 otrzymasz unikalne wartości.
Następnie ponownie zinterpretowałem jako liczby całkowite, zastosowałem modulo przez 12 i przyjąłem wartość bezwzględną. To dało każdej funkcji indeks. Byli to (od 0 -> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, sin, cos, atan, tanh, acos.
Teraz mógłbym po prostu zaindeksować tablicę ciągów, ale nazwy są bardzo długie i bardzo podobne, więc zamiast tego wyciągam je z kawałków łańcucha.
Aby to zrobić, tworzę ciąg „asinhacoshatanh” i dwie tablice. Pierwsza tablica wskazuje, który znak w ciągu ma zostać ustawiony na zerowy terminator, a druga wskazuje, który znak w ciągu powinien być pierwszym. Tablice te zawierają: 10,5,5,0,14,10,4,4,9,14,0,9 i 5,1,0,10,11,6,0,1,6,10,11, 5 odpowiednio.
Ostatecznie chodziło tylko o wydajne wdrożenie algorytmu reinterpretacji w C. Niestety niestety musiałem użyć podwójnego typu, a przy dokładnie 3 zastosowaniach szybsze było użycie tylko double
trzy razy, niż użycie #define D double\nDDD
tylko 2 znaków. Wynik jest powyżej, a opis poniżej:
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
Edycja: Niestety użycie surowej tablicy jest w rzeczywistości krótsze, więc kod staje się znacznie prostszy. Mimo to krojenie sznurka było fajne. Teoretycznie odpowiedni argument może faktycznie sam wyprowadzić właściwe wycinki z pewną matematyką.