C (gcc) , 178 172 байта
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?-_:_]);}
Попробуйте онлайн!
Старый, но крутой: C (gcc) , 194 байта
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}[_]);}
Попробуйте онлайн!
-lm
Переключатель в TIO просто проверить. Если бы вы могли написать идеальную
реализацию стандартных функций триггера, вы бы получили правильный ответ.
объяснение
Идея заключалась в том, чтобы найти некоторое входное значение, чтобы при интерпретации выходных данных каждой из функций триггера как целых чисел они имели разные остатки по модулю 12. Это позволит использовать их как индексы массива.
Чтобы найти такое входное значение, я написал следующий фрагмент:
#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;
}
Если вы запустите его (который должен быть скомпилирован с помощью -lm), то получится, что со значением 0,9247 вы получите уникальные значения.
Затем я переинтерпретировал как целые числа, применил по модулю 12 и взял абсолютное значение. Это дало каждой функции индекс. Это были (от 0 до> 11): акош, синх, асин, атан, тан, кош, асин, грех, соз, атан, тан, акос.
Теперь я могу просто индексировать в массив строк, но имена очень длинные и очень похожие, поэтому вместо этого я беру их из кусочков строки.
Для этого я строю строку «asinhacoshatanh» и два массива. Первый массив указывает, какой символ в строке установить на нулевой терминатор, а второй указывает, какой символ в строке должен быть первым. Эти массивы содержат: 10,5,5,0,14,10,4,4,9,14,0,9 и 5,1,0,10,11,6,0,1,6,10,11, 5 соответственно.
Наконец, это было просто вопросом эффективной реализации алгоритма реинтерпретации в C. К сожалению, мне пришлось использовать двойной тип, и с ровно 3-мя использованием было быстрее использовать double
три раза, чем использовать #define D double\nDDD
всего 2 символа. Результат выше, описание ниже:
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
Редактировать: К сожалению, просто использование необработанного массива на самом деле короче, поэтому код становится намного проще. Тем не менее нарезка строк была веселой. Теоретически, подходящий аргумент может на самом деле придумать правильные кусочки самостоятельно с некоторой математикой.