ตรีโกณมิติกล่องดำ


29

เขียนโปรแกรมหรือฟังก์ชั่นที่สามารถแยกแยะความแตกต่างฟังก์ชันตรีโกณมิติต่อไปนี้ 12: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, ,acosh atanh

โปรแกรมของคุณได้รับหนึ่งในฟังก์ชั่นด้านบนเป็นกล่องดำและควรเอาท์พุทชื่อฟังก์ชั่นตามที่กำหนดไว้ด้านบนหรือวิธีที่มันตั้งชื่อในภาษาของคุณ

นี่คือดังนั้นคำตอบที่สั้นที่สุดในแต่ละภาษาจะเป็นผู้ชนะ คุณควรแสดงให้เห็นว่ารหัสของคุณทำงานอย่างถูกต้องโดยรวมถึงกรณีทดสอบที่มีทั้ง 12 อินพุตที่เป็นไปได้ หากภาษาที่คุณเลือกไม่ได้รวม build-ins สำหรับฟังก์ชั่นด้านบนทั้งหมดคุณจะต้องจัดเตรียมการปรับใช้ที่เหมาะสมของตัวเองสำหรับคนที่ขาดหายไป

ชี้แจงเพิ่มเติม

  • การใช้ตัวเลขที่ซับซ้อนเพื่อสืบค้นช่องดำอนุญาตให้ใช้หากบิลด์อินพื้นฐานสามารถจัดการได้
  • ในฐานะที่เป็นเมื่อใช้ตัวเลขจริงเท่านั้นแบบสอบถามเพื่อฟังก์ชั่นกล่องดำสามารถให้ข้อผิดพลาดโดเมน ในกรณีนี้คุณควรสมมติว่ากล่องดำสื่อสารเฉพาะการมีอยู่ของข้อผิดพลาด แต่ไม่ใช่จากการทำงานที่เกิดขึ้นdom acoshdom atanh=
  • หากแทนที่จะมีข้อผิดพลาดค่าอื่น ๆ เช่นNaNหรือnullจะถูกส่งกลับการส่งของคุณควรจะสามารถจัดการได้

ขอบคุณสำหรับข้อเสนอแนะ sandbox ที่เป็นประโยชน์!


1
Mathematica สามารถจัดการอินพุตสัญลักษณ์เพื่อให้ฟังก์ชันเอาต์พุตถูกประเมินเพียงบางส่วนเท่านั้นถ้าหากทั้งหมด ความแตกต่างก็คือฉันสามารถใช้การจับคู่รูปแบบแทนการคำนวณ
JungHwan Min

1
@JungHwanMin ถ้านั่นหมายความว่าคุณสามารถเข้าถึงชื่อฟังก์ชั่นจากเอาต์พุตสัญลักษณ์แล้วฉันเกรงว่ามันจะไม่ได้รับอนุญาต
Laikoni

คำตอบ:


22

Python 3.6.4 on Linux, 99 bytes

Bit of a silly answer, but:

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

Requires the trigonometric functions to be one out of the built-in cmath module for complex in/output.


2
@JungHwanMin I believe that you are confused. I most certainly take an actual function. Note that my only reference to input f is f(.029) - calling the function with a value.
orlp

1
Did you bruteforce this?
mbomb007

4
@mbomb007 If by brute force you mean a loop that does a couple hundred iterations in the blink of an eye, yes.
orlp

3
This is both amazing and silly.
Nit


6

Perl 6, 75 bytes

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

Try it online!

As it happens, all twelve of the functions to be discriminated amongst are built-in and all take complex arguments.

[X~] ("", "a"), <sin cos tan>, ("", "h") generates all twelve function names by reducing the three input lists with cross-product-concatenation. Given those, .min(...) finds the one which the smallest difference from the input function at 2i.


59 bytes X can be used for multiple terms, and a few other tricks to golf bytes
Jo King

6

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?-_:_]);}

Try it online!

Old but cool: 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}[_]);}

Try it online!

The -lm switch in TIO is merely to test. If you could write a perfect implementation of the standard trig functions you would get the right answer.

Explanation

The idea was to find some input value such that when I interpret the outputs of each of the trig functions as integers they have different remainders modulo 12. This will allow them to be used as array indices.

In order to find such an input value I wrote the following snippet:

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

If you run that (which needs to be compiled with -lm) it will spit out that with a value of 0.9247 you get unique values.

Next I reinterpeted as integers, applied modulo by 12, and took the absolute value. This gave each function an index. They were (from 0 -> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, sin, cos, atan, tanh, acos.

Now I could just index into an array of strings, but the names are very long and very similar, so instead I take them out of slices of a string.

To do this I construct the string "asinhacoshatanh" and two arrays. The first array indicates which character in the string to set to the null terminator, while the second indicates which character in the string should be the first one. These arrays contain: 10,5,5,0,14,10,4,4,9,14,0,9 and 5,1,0,10,11,6,0,1,6,10,11,5 respectively.

Finally it was just a matter of implementing the reinterpretation algorithm efficiently in C. Sadly I had to use the double type, and with exactly 3 uses, it was quicker to just use double three times then to use #define D double\nDDD by just 2 characters. The result is above, a description is below:

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

Edit: Unfortunately just using a raw array is actually shorter, so the code becomes much simpler. Nonetheless the string slicing was fun. In theory an appropriate argument might actually come up with the right slices on its own with some math.


You can save 20 bytes by replacing puts(...) with printf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel

You can save 5 bytes by compiling with -DD=double and replacing all doubles in your code with D. Note that the flag needs to be counted for total bytes.

An additional three bytes can be shed by replacing char*[] with int*[], and by changing the ternary operator (?:) to an abs(_)

6

Python 3.6.5 on Linux, 90 85 bytes

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

This builds upon orlp's answer; but instead of finding 1 magic number, we find 3! This basically just saves bytes by avoiding putting the string literals for "sin", "cos", and "tan" multiple times, instead building the answer one part at a time.

The first magic number is used to determine whether it's one of the "arc" trigonometric functions, prepending an "a" accordingly, the second for whether it's one of the "sin", "cos", or "tan" based functions, selecting the appropriate string, and the third for whether it's one of the hyperbolic functions, appending a "h" accordingly.

Like orlp's answer, it uses the functions from Python's built-in cmath module as input.

Saved 5 bytes by using slice indexing into the middle string

Finding the Magic Numbers

For completeness, here's (more or less) the script I used to find these magic numbers. I mostly just worked straight in a python terminal, so the code is messy, but it gets the job done.

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
Great second answer! Would you mind sharing the program you used to find the magic numbers?
mbomb007

Thanks! I just added code to find the magic numbers to the answer, although it isn't terribly pretty.
nthistle

4

Python, 108 94 90 bytes

Compares the result of the input function to the results of all of the functions for the value .2.

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

Try it online

-14 bytes by Jonathan Allen
-4 bytes by Rod


No need for re, just get the ones needed with slicing: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0] (rewritten to work on TIO as the import must happen before d=dir(cmath) yet F= must be in the header to not be counted).
Jonathan Allan


Very nice! Thanks
mbomb007

4

Dyalog APL, 25 21 19 bytes

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

Try it online!

-3 thanks to H.PWiz
-2 thanks to ngn

Goes trough all the required trig functions (which in APL are 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) plus some more things (this goes trough -7..7), finds which one matches input○2, and outputs that "with" , which outputs as num∘○


3

C (gcc) with -lm, 374 346 324 bytes

Thanks to Giacomo Garabello for the suggestions.

I was able to save a bit more space by having a helper macro do token-pasting in addition to my original macro which does stringizing.

In the tests, I used a couple of non-library trig functions to confirm the validity of the results. As the results between the library and non-library functions weren't exactly the same floating-point value, I compared the difference of the results against a small value ε instead of using equality.

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

Try it online!


I managed to remove 14 bytes. In the TIO you can find the details. Try it online!
Giacomo Garabello

+1 from me, but I did find a sub 200 solution using a different strategy :)
LambdaBeta

3

JavaScript, 76 67 66 bytes

Not pretty but I went far too far down the rabbit hole with this over a few beers to not post it. Derived independently from Nit's solution.

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

Try it online

  • Saved 6 bytes thanks to Neil
  • Saved 1 bye thanks to 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.


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.