C (gcc) , 178 172 byte
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?-_:_]);}
Hãy thử trực tuyến!
Cũ nhưng tuyệt vời: C (gcc) , 194 byte
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}[_]);}
Hãy thử trực tuyến!
Việc -lm
chuyển đổi trong TIO chỉ đơn thuần là để kiểm tra. Nếu bạn có thể viết một
triển khai hoàn hảo các hàm trig tiêu chuẩn, bạn sẽ có câu trả lời đúng.
Giải trình
Ý tưởng là tìm một số giá trị đầu vào sao cho khi tôi diễn giải các đầu ra của từng hàm trig dưới dạng số nguyên, chúng có các phần dư khác nhau modulo 12. Điều này sẽ cho phép chúng được sử dụng như các chỉ số mảng.
Để tìm giá trị đầu vào như vậy, tôi đã viết đoạn mã sau:
#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;
}
Nếu bạn chạy cái đó (cần được biên dịch bằng -lm), nó sẽ nhổ ra rằng với giá trị 0,9247, bạn sẽ nhận được các giá trị duy nhất.
Tiếp theo tôi diễn giải lại dưới dạng số nguyên, áp dụng modulo cho 12 và lấy giá trị tuyệt đối. Điều này đã cho mỗi chức năng một chỉ mục. Họ đã (từ 0 -> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, sin, cos, atan, tanh, acos.
Bây giờ tôi chỉ có thể lập chỉ mục thành một chuỗi các chuỗi, nhưng các tên rất dài và rất giống nhau, vì vậy thay vào đó tôi đưa chúng ra khỏi các lát của một chuỗi.
Để làm điều này, tôi xây dựng chuỗi "asinhacoshatanh" và hai mảng. Mảng đầu tiên cho biết ký tự nào trong chuỗi được đặt thành dấu kết thúc null, trong khi mảng thứ hai chỉ ra ký tự nào trong chuỗi sẽ là ký tự đầu tiên. Các mảng này chứa: 10,5,5,0,14,10,4,4,9,14,0,9 và 5,1,0,10,11,6,0,1,6,10,11, 5 tương ứng.
Cuối cùng, đó chỉ là vấn đề triển khai thuật toán diễn giải lại một cách hiệu quả trong C. Đáng buồn là tôi đã phải sử dụng loại kép và với chính xác 3 lần sử dụng, nhanh hơn là chỉ sử dụng double
ba lần sau đó chỉ sử dụng #define D double\nDDD
2 ký tự. Kết quả là ở trên, một mô tả dưới đây:
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
Chỉnh sửa: Thật không may, chỉ sử dụng một mảng thô thực sự ngắn hơn, vì vậy mã trở nên đơn giản hơn nhiều. Tuy nhiên, việc cắt chuỗi là thú vị. Về lý thuyết, một lập luận thích hợp thực sự có thể tự mình đưa ra các lát cắt đúng với một số phép toán.