Trigonometri Kotak Hitam


29

Menulis sebuah program atau fungsi yang dapat membedakan berikut 12 fungsi trigonometri: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh .

Program Anda diberi salah satu fungsi di atas sebagai kotak hitam dan harus menampilkan nama fungsi baik seperti yang diberikan di atas atau cara namanya dalam bahasa Anda.

Ini adalah , jadi jawaban tersingkat di setiap bahasa menang. Anda harus menunjukkan bahwa kode Anda berfungsi dengan benar dengan memasukkan kasus uji dengan semua 12 input yang mungkin. Jika bahasa pilihan Anda tidak termasuk built-in untuk semua fungsi di atas, Anda harus memberikan implementasi masuk akal Anda sendiri dari yang hilang.

Klarifikasi lebih lanjut

  • Menggunakan bilangan kompleks untuk menanyakan kotak hitam diizinkan jika build-in yang mendasarinya dapat menanganinya.
  • Sebagai dom acoshdom atanh= saat hanya menggunakan bilangan real, kueri ke fungsi kotak hitam dapat memberikan kesalahan domain. Dalam hal ini Anda harus mengasumsikan bahwa kotak hitam hanya mengkomunikasikan adanya kesalahan, tetapi bukan dari mana fungsi itu berasal.
  • Jika alih-alih kesalahan beberapa nilai lain, misalnya NaNatau null, dikembalikan, maka kiriman Anda harus dapat menanganinya.

Terima kasih atas umpan balik kotak pasir yang bermanfaat !


1
Mathematica dapat menangani input simbolik sehingga output fungsi hanya dievaluasi sebagian, jika sama sekali. Perbedaannya adalah bahwa saya bisa menggunakan beberapa pencocokan pola daripada perhitungan.
JungHwan Min

1
@JungHwanMin Jika itu berarti Anda dapat mengakses nama fungsi dari output simbolis maka saya khawatir itu tidak diperbolehkan.
Laikoni

Jawaban:


22

Python 3.6.4 di Linux, 99 byte

Sedikit jawaban konyol, tapi:

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

Membutuhkan fungsi trigonometri menjadi salah satu dari cmathmodul bawaan untuk / keluaran yang kompleks.


2
@JungHwanMin Saya yakin Anda bingung. Saya tentu saja mengambil fungsi yang sebenarnya. Perhatikan bahwa satu-satunya referensi saya untuk input fadalah f(.029)- memanggil fungsi dengan nilai.
orlp

1
Apakah Anda bruteforce ini?
mbomb007

4
@ mbomb007 Jika dengan kekerasan Anda maksudkan loop yang melakukan beberapa ratus iterasi dalam sekejap mata, ya.
orlp

3
Ini luar biasa dan konyol.
Nit


6

Perl 6 , 75 byte

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

Cobalah online!

Ketika itu terjadi, ke dua belas fungsi yang akan didiskriminasi adalah bawaan dan semuanya mengambil argumen yang kompleks.

[X~] ("", "a"), <sin cos tan>, ("", "h")menghasilkan semua dua belas nama fungsi dengan mengurangi tiga daftar input dengan gabungan produk-silang. Diberikan itu, .min(...)menemukan satu yang perbedaan terkecil dari fungsi input 2i.


59 byte X dapat digunakan untuk beberapa istilah, dan beberapa trik lain untuk 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?-_:_]);}

Cobalah online!

Tua tapi keren: 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}[_]);}

Cobalah online!

The -lmswitch TIO adalah hanya untuk tes. Jika Anda bisa menulis implementasi sempurna fungsi trigonometri standar, Anda akan mendapatkan jawaban yang tepat.

Penjelasan

Idenya adalah untuk menemukan beberapa nilai input sehingga ketika saya menginterpretasikan output dari masing-masing fungsi trig sebagai bilangan bulat mereka memiliki modulo sisa yang berbeda 12. Ini akan memungkinkan mereka untuk digunakan sebagai indeks array.

Untuk menemukan nilai input seperti itu, saya menulis cuplikan berikut:

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

Jika Anda menjalankan itu (yang perlu dikompilasi dengan -lm) itu akan memuntahkan bahwa dengan nilai 0,9247 Anda mendapatkan nilai-nilai unik.

Selanjutnya saya reinterpeted sebagai integer, menerapkan modulo oleh 12, dan mengambil nilai absolut. Ini memberi masing-masing fungsi indeks. Mereka (dari 0 -> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, dosa, cos, atan, tanh, acos.

Sekarang saya hanya bisa mengindeks ke array string, tetapi nama-nama yang sangat panjang dan sangat mirip, jadi alih-alih saya mengambilnya dari potongan string.

Untuk melakukan ini saya membangun string "asinhacoshatanh" dan dua array. Array pertama menunjukkan karakter mana dalam string yang akan ditetapkan ke terminator nol, sedangkan yang kedua menunjukkan karakter mana dalam string harus yang pertama. Array ini berisi: 10,5,5,0,14,10,4,4,9,9,14,0,9 dan 5,1,0,10,11,6,0,1,1,6,10,11, 5 masing-masing.

Akhirnya itu hanya masalah menerapkan algoritma reinterpretasi secara efisien dalam C. Sayangnya saya harus menggunakan tipe ganda, dan dengan tepat 3 penggunaan, lebih cepat hanya menggunakan doubletiga kali kemudian menggunakan #define D double\nDDD hanya 2 karakter. Hasilnya di atas, uraiannya di bawah:

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

Sunting: Sayangnya hanya menggunakan array mentah sebenarnya lebih pendek, sehingga kode menjadi lebih sederhana. Meskipun demikian, mengiris senar itu menyenangkan. Secara teori, argumen yang tepat mungkin benar-benar muncul dengan irisan yang tepat dengan beberapa matematika.


Anda dapat menghemat 20 byte dengan menggantinya puts(...)denganprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel

Anda dapat menyimpan 5 byte dengan mengkompilasi dengan -DD=doubledan mengganti semua doublekode Anda dengan D. Perhatikan bahwa bendera perlu dihitung untuk total byte.

Tiga byte tambahan dapat ditumpahkan dengan mengganti char*[]dengan int*[], dan dengan mengubah operator ternary (? :) menjadiabs(_)

6

Python 3.6.5 di 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"

Ini dibangun berdasarkan jawaban orlp ; tetapi alih-alih menemukan 1 angka ajaib, kami menemukan 3! Ini pada dasarnya hanya menghemat byte dengan menghindari menempatkan string literal untuk "sin", "cos", dan "tan" beberapa kali, alih-alih membangun jawaban satu bagian pada suatu waktu.

Angka ajaib pertama digunakan untuk menentukan apakah itu salah satu dari fungsi trigonometrik "busur", dengan menambahkan sebuah "a" yang sesuai, yang kedua untuk apakah itu salah satu dari fungsi berbasis "dosa", "cos", atau "tan", memilih string yang sesuai, dan yang ketiga untuk apakah itu salah satu fungsi hiperbolik, menambahkan "h" yang sesuai.

Seperti jawaban orlp, ia menggunakan fungsi-fungsi dari cmathmodul bawaan Python sebagai input.

Disimpan 5 byte dengan menggunakan slice indexing ke string tengah

Menemukan Angka Ajaib

Untuk kelengkapan, inilah (kurang lebih) skrip yang saya gunakan untuk menemukan angka ajaib ini. Saya kebanyakan hanya bekerja langsung di terminal python, jadi kodenya berantakan, tapi itu menyelesaikan pekerjaan.

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
Jawaban kedua yang bagus! Maukah Anda berbagi program yang Anda gunakan untuk menemukan angka ajaib?
mbomb007

Terima kasih! Saya baru saja menambahkan kode untuk menemukan angka ajaib untuk jawabannya, meskipun tidak terlalu cantik.
nthistle

4

Python , 108 94 90 byte

Membandingkan hasil fungsi input dengan hasil semua fungsi untuk nilai .2.

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

Cobalah online

-14 byte oleh Jonathan Allen
-4 byte oleh Rod


Tidak perlu untuk re, hanya mendapatkan yang diperlukan dengan mengiris: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](ditulis ulang untuk bekerja pada TIO sebagai impor harus terjadi sebelum d=dir(cmath)belum F=harus di header untuk tidak dihitung).
Jonathan Allan


Sangat bagus! Terima kasih
mbomb007

4

Dyalog APL , 25 21 19 byte

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

Cobalah online!

-3 terima kasih kepada H.PWiz
-2 terima kasih kepada ngn

Pergi melalui semua fungsi trigonometri yang diperlukan (yang ada di APL 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) ditambah beberapa hal lagi (ini berjalan melalui -7..7), menemukan mana yang cocok input○2, dan menghasilkan yang "dengan" , yang menghasilkan sebagainum∘○


3

C (gcc) dengan -lm, 374 346 324 byte

Terima kasih kepada Giacomo Garabello untuk sarannya.

Saya dapat menghemat sedikit lebih banyak ruang dengan memiliki makro pembantu melakukan token-paste di samping makro asli saya yang melakukan pengurutan.

Dalam tes, saya menggunakan beberapa fungsi trigonometri non-perpustakaan untuk mengkonfirmasi validitas hasil. Karena hasil antara fungsi perpustakaan dan non-perpustakaan tidak persis nilai floating-point yang sama, saya membandingkan perbedaan hasil terhadap nilai kecil ε daripada menggunakan kesetaraan.

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

Cobalah online!


Saya berhasil menghapus 14 byte. Di TIO Anda dapat menemukan detailnya. Cobalah online!
Giacomo Garabello

+1 dari saya, tetapi saya menemukan solusi sub 200 menggunakan strategi yang berbeda :)
LambdaBeta

3

JavaScript, 76 67 66 byte

Tidak cantik tapi aku pergi terlalu jauh ke lubang kelinci dengan beberapa bir untuk tidak mempostingnya. Berasal secara independen dari solusi Nit.

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

Cobalah online

  • Disimpan 6 byte, terima kasih Neil
  • Disimpan 1 bye berkat l4m2

b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (meskipun saya tidak tahu mengapa mengkonversi ke String untuk membandingkan)
l4m2

Tidak tahu mengapa saya tidak memikirkan itu. Terima kasih, @ l4m2.
Shaggy

@ l4m2 Kita harus NaNmembandingkan sama dengan NaN, jadi apakah itu atau Object.is.
Neil



2

JavaScript, 108 70 byte

Saya belum pernah mencoba bermain golf dalam Javascript murni, jadi saya yakin ada beberapa hal yang perlu ditingkatkan di sini.

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

Cukup mudah, periksa setiap fungsi pada Math prototipe terhadap nilai arbitrer (0,9, banyak nilai lain mungkin bekerja) dan membandingkannya dengan hasil dari fungsi kotak hitam.
Diuji di Google Chrome, akan pecah jika fungsi kotak hitam input bukan salah satu pemicu.

Potong satu ton byte berkat Shaggy dan 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
Sangat mirip dengan solusi yang saya kerjakan selama beberapa gelas bir tetapi tidak bisa mengetahuinya. 2 penghematan cepat yang dapat saya temukan: 0.3 -> .3dan tetapkan Mathke m dalam getOwnPropertyNames() .
Shaggy

1
Aku berhasil untuk turun ini untuk 71 byte: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. Saya perhatikan @Shaggy juga digunakan find. The +''tidak string membandingkan, berarti kita hanya harus memeriksa satu titik. Itu ,0membuat kita lewati Math.atan2.
Neil


@ Shaggy Saya rasa ini tergantung pada implementasi; di Firefox, atan2mendahului acoshdalam array yang dikembalikan oleh Object.getOwnPropertyNames.
Neil

Jika ada yang bertanya-tanya, solusi ini berfungsi karena non-fungsi pertama dari getOwnPropertyNamesadalah Math.E, dan semua fungsi trigon sebelum itu.
MattH

2

R , 75 byte

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

Cobalah online!

Untuk saat ini (R v3.5) berfungsi.
Jika dalam versi R yang akan datang akan ditambahkan fungsi yang cocok dengan regex ini, lalu siapa yang tahu: P

  • -2 byte terima kasih kepada @Giuseppe
  • -9 byte berkat @JayCe
  • -2 byte menggunakan Findsebagai gantinyafor

Wow. Sangat bagus! Saya pikir 1iberfungsi juga -1iuntuk -2 byte.
Giuseppe

@ Giuseppe: Saya yakin saya telah mengujinya dan tidak bekerja ... tapi mungkin itu hanya imajinasi saya: D
digEmAll

sangat bagus! Bekerja pada TIO, tergantung pada konfigurasi Anda mungkin dalam kasus umum: tio
JayCe

@ Jayaye: mendapatkan lingkungan melalui posisi berisiko ... misalnya tidak bekerja di RStudio ... untungnya saya menemukan fungsi lain mencari objek di mana-mana, dengan bytecount yang sama :)
digEmAll


1

HP 49G RPL, 88,0 byte tidak termasuk header program 10 byte

Solusi lain menggunakan bilangan kompleks! Masuk dan laksanakan dalam mode COMPLEX, APPROX. Mengambil fungsi pada 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

(baris baru tidak masalah)

Untuk konstanta 2.0, semua dua belas fungsi trigonometri didefinisikan dalam bidang kompleks, jadi kami hanya mengevaluasi kedua belas trigonometri dan melihat mana yang cocok. Kali ini, solusi berulang lebih lama (111,5 byte) karena tumpukan shuffling diperlukan untuk mendapatkannya. RPL, sejauh yang saya tahu, tidak membiarkan Anda keluar dari lingkaran lebih awal.


Jika mereka dikembalikan sebagai huruf besar, tidak apa-apa sekarang ketika saya mengedit tantangan.
Laikoni

@JungHwanMin Mereka huruf besar. Terima kasih atas tangkapannya! Ini dapat dimodifikasi untuk huruf kecil dengan ->STR DUP SIZE 3 - " " " " IFTE XOR, 34,5 byte. (masing-masing seharusnya terdiri dari 4 dan 3 ruang)
Jason

1

Perl 6 , 39 byte

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

Cobalah online!

Dari penampilan, salah satu dari sedikit yang menggunakan introspeksi. idi sini adalah bilangan kompleks, yang nilainya unik untuk setiap fungsi trigonometri, jadi dengan mengulangi semua metode kita dapat menemukan metode yang cocok dan secara implisit meludahkan namanya. Ini trydiperlukan karena beberapa metode (yang tidak diinginkan) memiliki tanda tangan yang berbeda.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.