Lượng giác hộp đen


29

Viết một chương trình hoặc chức năng có thể phân biệt được 12 hàm lượng giác sau: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh.

Chương trình của bạn được cung cấp một trong các chức năng trên dưới dạng hộp đen và sẽ xuất tên của chức năng như được nêu ở trên hoặc cách đặt tên theo ngôn ngữ của bạn.

Đây là , vì vậy câu trả lời ngắn nhất trong mỗi ngôn ngữ sẽ thắng. Bạn nên chỉ ra rằng mã của bạn hoạt động chính xác bằng cách bao gồm các trường hợp thử nghiệm với tất cả 12 đầu vào có thể. Nếu ngôn ngữ bạn chọn không bao gồm các bản dựng cho tất cả các chức năng trên, bạn phải cung cấp các triển khai hợp lý của riêng bạn cho các chức năng còn thiếu.

Nhiều sự sáng tỏ hơn

  • Sử dụng số phức để truy vấn hộp đen được cho phép nếu các bản dựng bên dưới có thể xử lý chúng.
  • Như khi sử dụng chỉ số thực, truy vấn đến các chức năng hộp đen có thể cung cấp cho các lỗi miền. Trong trường hợp này, bạn nên giả sử rằng hộp đen chỉ truyền đạt sự tồn tại của một lỗi, chứ không phải từ chức năng mà nó bắt nguồn.dom acoshdom atanh=
  • Nếu thay vì một lỗi nào đó, một số giá trị khác, ví dụ NaN, hoặc nullđược trả về, thì trình của bạn sẽ có thể xử lý chúng.

Cảm ơn các phản hồi hộp cát hữu ích !


1
Mathicala có thể xử lý các đầu vào tượng trưng để đầu ra hàm chỉ được đánh giá một phần, nếu có. Sự khác biệt mà nó tạo ra là tôi có thể sử dụng một số khớp mẫu thay vì tính toán.
JungHwan Min

1
@JungHwanMin Nếu điều đó có nghĩa là bạn có thể truy cập các tên hàm từ đầu ra tượng trưng thì tôi sợ nó không được phép.
Laikoni

Câu trả lời:


22

Python 3.6.4 trên Linux, 99 byte

Một câu trả lời ngớ ngẩn, nhưng:

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

Yêu cầu các hàm lượng giác phải là một trong số các cmathmô đun tích hợp sẵn cho đầu vào / đầu ra phức tạp.


2
@JungHwanMin Tôi tin rằng bạn đang bối rối. Tôi chắc chắn có một chức năng thực tế. Lưu ý rằng tham chiếu duy nhất của tôi đến đầu vào ff(.029)- gọi hàm bằng một giá trị.
orlp

1
Bạn đã tàn bạo điều này?
mbomb007

4
@ mbomb007 Nếu bằng vũ lực, bạn có nghĩa là một vòng lặp có vài trăm lần lặp lại trong chớp mắt, vâng.
orlp

3
Điều này là cả tuyệt vời và ngớ ngẩn.
Nit


6

Perl 6 , 75 byte

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

Hãy thử trực tuyến!

Khi điều đó xảy ra, tất cả mười hai chức năng được phân biệt đối xử đều được tích hợp sẵn và tất cả đều có các đối số phức tạp.

[X~] ("", "a"), <sin cos tan>, ("", "h")tạo ra tất cả mười hai tên hàm bằng cách giảm ba danh sách đầu vào với ghép nối sản phẩm chéo. Với những cái đó, .min(...)tìm cái khác biệt nhỏ nhất từ ​​hàm đầu vào tại 2i.


59 byte X có thể được sử dụng cho nhiều thuật ngữ và một vài thủ thuật khác đối với byte golf
Jo King

6

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 -lmchuyể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 doubleba 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.


Bạn có thể tiết kiệm 20 byte bằng cách thay thế puts(...)bằngprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel

Bạn có thể lưu 5 byte bằng cách biên dịch -DD=doublevà thay thế tất cả doubles trong mã của bạn bằng D. Lưu ý rằng cờ cần được tính cho tổng số byte.

Có thể loại bỏ thêm ba byte bằng cách thay thế char*[]bằng int*[]và thay đổi toán tử ternary (? :) thành mộtabs(_)

6

Python 3.6.5 trên Linux, 90 85 byte

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

Điều này được xây dựng dựa trên câu trả lời của orlp ; nhưng thay vì tìm 1 số ma thuật, chúng tôi tìm thấy 3! Điều này về cơ bản chỉ tiết kiệm byte bằng cách tránh đặt chuỗi ký tự cho "sin", "cos" và "tan" nhiều lần, thay vào đó xây dựng câu trả lời từng phần một.

Số ma thuật đầu tiên được sử dụng để xác định xem đó có phải là một trong các hàm lượng giác "cung" hay không, đặt trước một "a" tương ứng, thứ hai cho dù đó là một trong các hàm dựa trên "sin", "cos" hay "tan" chuỗi thích hợp và chuỗi thứ ba cho dù đó là một trong các hàm hyperbol, nối thêm "h" tương ứng.

Giống như câu trả lời của orlp, nó sử dụng các hàm từ cmathmô đun tích hợp sẵn của Python làm đầu vào.

Đã lưu 5 byte bằng cách sử dụng lập chỉ mục lát vào chuỗi giữa

Tìm số ma thuật

Để hoàn thiện, đây là (ít nhiều) kịch bản mà tôi đã sử dụng để tìm những con số ma thuật này. Tôi hầu như chỉ làm việc thẳng trong một thiết bị đầu cuối python, vì vậy mã rất lộn xộn, nhưng nó hoàn thành công việc.

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)

1
Câu trả lời thứ hai tuyệt vời! Bạn có phiền khi chia sẻ chương trình bạn đã sử dụng để tìm những con số kỳ diệu không?
mbomb007

Cảm ơn! Tôi chỉ cần thêm mã để tìm số ma thuật cho câu trả lời, mặc dù nó không đẹp lắm.
nthistle

4

Con trăn , 108 94 90 byte

So sánh kết quả của hàm đầu vào với kết quả của tất cả các hàm cho giá trị .2.

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

Dùng thử trực tuyến

-14 byte của Jonathan Allen
-4 byte bởi Rod


Không cần re, chỉ nhận được những cái cần thiết với cắt: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](viết lại để làm việc trên TIO như việc nhập khẩu phải xảy ra trước khi d=dir(cmath)chưa F=phải nằm trong tiêu đề để không được tính).
Jonathan Allan


Rất đẹp! Cảm ơn
mbomb007

4

Thuốc nhuộm APL , 25 21 19 byte

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

Hãy thử trực tuyến!

-3 cảm ơn H.PWiz
-2 nhờ ngn

Đi qua tất cả các hàm trig cần thiết (trong APL là 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) cộng với một số thứ khác (cái này đi qua máng -7..7), tìm cái nào khớp input○2và xuất ra "với" , cái nào xuất ra dưới dạngnum∘○


3

C (gcc) với -lm, 374 346 324 byte

Cảm ơn Giacomo Garabello cho những gợi ý.

Tôi đã có thể tiết kiệm thêm một chút dung lượng bằng cách có macro trợ giúp thực hiện dán mã thông báo ngoài macro ban đầu của tôi, chuỗi này.

Trong các thử nghiệm, tôi đã sử dụng một vài hàm trig không phải thư viện để xác nhận tính hợp lệ của kết quả. Vì kết quả giữa các hàm thư viện và không phải thư viện không chính xác cùng một giá trị dấu phẩy động, tôi đã so sánh sự khác biệt của các kết quả so với một giá trị nhỏ ε thay vì sử dụng đẳng thức.

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

Hãy thử trực tuyến!


Tôi quản lý để loại bỏ 14 byte. Trong TIO bạn có thể tìm thấy các chi tiết. Hãy thử trực tuyến!
Giacomo Garabello

+1 từ tôi, nhưng tôi đã tìm thấy một giải pháp phụ 200 bằng cách sử dụng một chiến lược khác :)
LambdaBeta

3

JavaScript, 76 67 66 byte

Không đẹp nhưng tôi đã đi quá xa xuống hố thỏ với cái này qua một vài loại bia để không đăng nó. Có nguồn gốc độc lập từ giải pháp của Nitơ.

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

Dùng thử trực tuyến

  • Đã lưu 6 byte nhờ Neil
  • Lưu 1 tạm biệt nhờ l4m2

b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (though I don't quite know why convert to String to compare)
l4m2

Don't know why I didn't think of that. Thanks, @l4m2.
Shaggy

@l4m2 We need NaN to compare equal to NaN, so it's either that or Object.is.
Neil



2

JavaScript, 108 70 bytes

I haven't tried golfing in pure Javascript in ages, so I'm sure there's stuff to improve here.

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

Pretty straightforward, checks every function on the Math prototype against an arbitrary value (0.9, many other values probably work) and compares with the result of the black box function.
Tested in Google Chrome, will break if the input black box function is not one of the trigs.

Cut off a ton of bytes thanks to Shaggy and 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)));


1
Very similar to the solution I was working on over a few beers but couldn't quite figure out. 2 quick savings I can spot: 0.3 -> .3 and assign Math to m within getOwnPropertyNames().
Shaggy

1
I managed to get this down to 71 bytes: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. I noticed @Shaggy used find as well. The +'' does a string compare, meaning we only have to check one point. The ,0 makes us skip Math.atan2.
Neil

@Neil, it doesn't looks like the ,0 is needed: tio.run/##Lc6xDoMgEMbxvU/RMEFq2TvgG1jdjYknomLkzghp7dPTqEz/…
Shaggy

@Shaggy I guess it's implementation-dependent; in Firefox, atan2 precedes acosh in the array returned by Object.getOwnPropertyNames.
Neil

If anyone was wondering, this solution works because the first non-function from getOwnPropertyNames is Math.E, and all trig functions enumerate before that.
MattH

2

R, 75 bytes

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

Try it online!

For the moment (R v3.5) it works.
If in a future R version it will be added a function matching this regex, then who knows :P

  • -2 bytes thanks to @Giuseppe
  • -9 bytes thanks to @JayCe
  • -2 bytes using Find instead of for

wow. Very nice! I think 1i works as well as -1i for -2 bytes.
Giuseppe

@Giuseppe: I was sure I had tested it and it wasn't working... but probably it was only my imagination :D
digEmAll

very Nice! Works on TIO, depends on your config probably in the general case: tio
JayCe

@JayCe: getting the environment through position is risky...for example it doesn't work in RStudio...fortunately I found another function searching for the objects everywhere, with the same bytecount :)
digEmAll


1

HP 49G RPL, 88.0 bytes excluding 10 byte program header

Another solution using complex numbers! Enter and execute it in COMPLEX, APPROX mode. Takes the function on the stack.

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

(the newlines don't matter)

For the constant 2.0, all twelve trig functions are defined in the complex plane, so we just evaluate all twelve and see which one matches. This time, the iterative solution is longer (111.5 bytes) because of the stack shuffling needed to get it. RPL, as far as I know, doesn't let you break out of a loop early.


In case they are returned as upper-case, that's fine now as I edited the challenge.
Laikoni

@JungHwanMin They are upper-case. Thanks for the catch! It can be modified to lower-case with ->STR DUP SIZE 3 - " " " " IFTE XOR, 34.5 bytes. (those are supposed to be 4 and 3 spaces, respectively)
Jason

1

Perl 6, 39 bytes

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

Try it online!

By the looks of things, one of the few to use introspection. i here is the complex number, whose value is unique for each trig function, so by iterating through all the methods we can find the matching method and implicitly spit out its name. The try is needed as some (unwanted) methods have the a different signature.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.