Trygonometria czarnej skrzynki

29

Napisać program lub funkcję, które można wyróżnić następujące funkcje trygonometryczne 12: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh.

Wasz program otrzymuje jedną z powyższych funkcji jako czarną skrzynkę i powinien wypisać nazwę funkcji, jak podano powyżej, lub sposób, w jaki jest ona nazwana w twoim języku.

To jest , więc wygrywa najkrótsza odpowiedź w każdym języku. Powinieneś pokazać, że kod działa poprawnie, włączając przypadki testowe ze wszystkimi 12 możliwymi danymi wejściowymi. Jeśli wybrany język nie zawiera wbudowanych funkcji dla wszystkich powyższych funkcji, musisz podać własne rozsądne implementacje brakujących.

Dalsze wyjaśnienia

  • Używanie liczb zespolonych do zapytania do czarnej skrzynki jest dozwolone, jeśli odpowiednie wbudowane poradzą sobie z nimi.
  • Ponieważ przy użyciu tylko liczb rzeczywistych, zapytania do funkcji czarnej skrzynki mogą powodować błędy domeny. W takim przypadku należy założyć, że czarna skrzynka informuje tylko o błędzie, ale nie od jakiej funkcji pochodzi.dom acoshdom atanh=
  • Jeśli zamiast błędu zostanie zwrócona jakaś inna wartość, np. NaNLub null, wówczas przesłanie powinno być w stanie je obsłużyć.

Dziękujemy za pomocną opinię w piaskownicy !

Laikoni
źródło
1
Mathematica może obsługiwać wejścia symboliczne, dzięki czemu dane wyjściowe funkcji są oceniane tylko częściowo, jeśli w ogóle. Różnica polega na tym, że zamiast obliczeń mogłem użyć dopasowania wzorca.
JungHwan Min
1
@JungHwanMin Jeśli to oznacza, że ​​możesz uzyskać dostęp do nazw funkcji z wyjścia symbolicznego, obawiam się, że nie jest to dozwolone.
Laikoni,

Odpowiedzi:

22

Python 3.6.4 w systemie Linux, 99 bajtów

Trochę głupia odpowiedź, ale:

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

Wymaga, aby funkcje trygonometryczne były jednym z wbudowanego cmathmodułu dla złożonych wejść / wyjść.

orlp
źródło
2
@JungHwanMin Uważam, że jesteś zdezorientowany. Z pewnością pełnię rzeczywistą funkcję. Zauważ, że moim jedynym odniesieniem do danych wejściowych fjest f(.029)- wywołanie funkcji z wartością.
orlp
1
Sforsowałeś to?
mbomb007,
4
@ mbomb007 Jeśli przez brutalną siłę rozumiesz pętlę wykonującą kilkaset iteracji w mgnieniu oka, tak.
orlp
3
To jest niesamowite i głupie.
Nit,
1
Istotnych?
mbomb007,
6

Perl 6 , 75 bajtów

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

Wypróbuj online!

Tak się składa, że ​​wszystkie dwanaście funkcji, które należy rozróżnić, są wbudowane i wszystkie przyjmują złożone argumenty.

[X~] ("", "a"), <sin cos tan>, ("", "h")generuje wszystkie dwanaście nazw funkcji, redukując trzy listy danych wejściowych przez łączenie różnych produktów. Biorąc pod uwagę te, .min(...)znajduje tę, w której najmniejsza różnica od funkcji wejściowej 2i.

Sean
źródło
59 bajtów X można użyć dla wielu terminów i kilka innych sztuczek do bajtów golfa
Jo King
6

C (gcc) , 178 172 bajtów

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

Wypróbuj online!

Stare, ale fajne: C (gcc) , 194 bajty

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

Wypróbuj online!

-lmPrzełącznik w TIO jest jedynie do testu. Jeśli potrafisz napisać idealną implementację standardowych funkcji trig, uzyskasz właściwą odpowiedź.

Wyjaśnienie

Pomysł polegał na znalezieniu pewnej wartości wejściowej, takiej, że kiedy interpretuję wyniki każdej z funkcji triggera jako liczby całkowite, mają one pozostałe resztki modulo 12. Pozwoli to na ich wykorzystanie jako indeksów tablicowych.

Aby znaleźć taką wartość wejściową, napisałem następujący fragment:

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

Jeśli uruchomisz to (które należy skompilować z -lm), wypluje to, że o wartości 0.9247 otrzymasz unikalne wartości.

Następnie ponownie zinterpretowałem jako liczby całkowite, zastosowałem modulo przez 12 i przyjąłem wartość bezwzględną. To dało każdej funkcji indeks. Byli to (od 0 -> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, sin, cos, atan, tanh, acos.

Teraz mógłbym po prostu zaindeksować tablicę ciągów, ale nazwy są bardzo długie i bardzo podobne, więc zamiast tego wyciągam je z kawałków łańcucha.

Aby to zrobić, tworzę ciąg „asinhacoshatanh” i dwie tablice. Pierwsza tablica wskazuje, który znak w ciągu ma zostać ustawiony na zerowy terminator, a druga wskazuje, który znak w ciągu powinien być pierwszym. Tablice te zawierają: 10,5,5,0,14,10,4,4,9,14,0,9 i 5,1,0,10,11,6,0,1,6,10,11, 5 odpowiednio.

Ostatecznie chodziło tylko o wydajne wdrożenie algorytmu reinterpretacji w C. Niestety niestety musiałem użyć podwójnego typu, a przy dokładnie 3 zastosowaniach szybsze było użycie tylko doubletrzy razy, niż użycie #define D double\nDDD tylko 2 znaków. Wynik jest powyżej, a opis poniżej:

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

Edycja: Niestety użycie surowej tablicy jest w rzeczywistości krótsze, więc kod staje się znacznie prostszy. Mimo to krojenie sznurka było fajne. Teoretycznie odpowiedni argument może faktycznie sam wyprowadzić właściwe wycinki z pewną matematyką.

LambdaBeta
źródło
Można zapisać 20 bajtów zastępując puts(...)zprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel
Możesz zapisać 5 bajtów, kompilując -DD=doublei zamieniając wszystkie doubles w kodzie na D. Zauważ, że flaga musi być zliczona dla wszystkich bajtów.
Dodatkowe trzy bajty można przelać, zastępując char*[]je int*[]i zmieniając operator trójskładnikowy (? :) naabs(_)
6

Python 3.6.5 w systemie Linux, 90 85 bajtów

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

To opiera się na odpowiedzi orlp ; ale zamiast znaleźć 1 magiczną liczbę, znajdziemy 3! To w zasadzie oszczędza bajty, unikając wielokrotnego wstawiania literałów ciągów „sin”, „cos” i „tan”, zamiast budować odpowiedź pojedynczo.

Pierwsza magiczna liczba służy do określenia, czy jest to jedna z funkcji trygonometrycznych „łuku”, odpowiednio poprzedzająca „a”, druga do tego, czy jest to jedna z funkcji opartych na „sin”, „cos” lub „tan”, wybierając odpowiedni ciąg, a trzeci dla tego, czy jest to jedna z funkcji hiperbolicznych, dodając odpowiednio „h”.

Podobnie jak odpowiedź orlp, wykorzystuje funkcje z wbudowanego cmathmodułu Pythona jako dane wejściowe.

Zapisano 5 bajtów, stosując indeksowanie plasterków w środkowym ciągu

Znalezienie magicznych liczb

Dla kompletności, oto (mniej więcej) skrypt, którego użyłem do znalezienia tych magicznych liczb. Przeważnie po prostu pracowałem prosto w terminalu python, więc kod jest nieporządny, ale wykonuje zadanie.

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)
nthistle
źródło
1
Świetna druga odpowiedź! Czy miałbyś coś przeciwko udostępnieniu programu, którego użyłeś do znalezienia magicznych liczb?
mbomb007
Dzięki! Właśnie dodałem kod, aby znaleźć magiczne liczby w odpowiedzi, chociaż nie jest to strasznie ładne.
nthistle
4

Python , 108 94 90 bajtów

Porównuje wynik funkcji wejściowej z wynikami wszystkich funkcji dla wartości .2.

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

Wypróbuj online

-14 bajtów Jonathana Allena
-4 bajty Rod

mbomb007
źródło
Nie ma potrzeby re, po prostu zdobądź te, które są potrzebne przy krojeniu: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](przepisany do pracy na TIO, ponieważ import musi nastąpić wcześniej, d=dir(cmath)ale F=musi znajdować się w nagłówku, aby nie zostać policzony).
Jonathan Allan
Bardzo dobrze! Dzięki
mbomb007,
4

Dyalog APL , 25 21 19 bajtów

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

Wypróbuj online!

-3 dzięki H.PWiz
-2 dzięki ngn

Przechodzi przez wszystkie wymagane funkcje wyzwalania (które są w APL 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) plus kilka innych rzeczy (to idzie koryta -7..7), znajduje, która z nich pasuje input○2, i wyprowadza to „z” , które wyprowadza jakonum∘○

dzaima
źródło
3

C (gcc) z -lm, 374 346 324 bajtów

Dzięki Giacomo Garabello za sugestie.

Byłem w stanie zaoszczędzić trochę więcej miejsca, mając makro pomocnika do wklejania tokenów oprócz mojego oryginalnego makra, które wykonuje przeciąganie.

W testach użyłem kilku niebibliotecznych funkcji wyzwalających, aby potwierdzić poprawność wyników. Ponieważ wyniki między funkcjami bibliotecznymi a pozabibliotecznymi nie były dokładnie takie same, jak zmiennoprzecinkowe, porównałem różnicę wyników z małą wartością ε zamiast używania równości.

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

Wypróbuj online!

ErikF
źródło
Udało mi się usunąć 14 bajtów. W TIO można znaleźć szczegóły. Wypróbuj online!
Giacomo Garabello,
+1 ode mnie, ale znalazłem rozwiązanie poniżej 200 przy użyciu innej strategii :)
LambdaBeta
3

JavaScript, 76 67 66 bajtów

Nie ładnie, ale poszedłem z tym zbyt daleko w dół króliczej nory, aby nie opublikować tego. Pochodzi niezależnie od rozwiązania Nit.

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

Wypróbuj online

Kudłaty
źródło
b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (choć nie do końca wiem, dlaczego przekonwertować na String, aby porównać)
l4m2
Nie wiem, dlaczego o tym nie pomyślałem. Dzięki, @ l4m2.
Kudłaty
@ l4m2 Musimy NaNporównać równe NaN, więc albo to, albo Object.is.
Neil
2

Ruby , 71 67 bajtów

->g{Math.methods.find{|h|g[0.5]==Math.send(h,0.5)rescue p}||:acosh}

Wypróbuj online!

Asone Tuhid
źródło
2

JavaScript, 108 70 bajtów

Od wieków nie grałem w golfa w czystym JavaScript, więc jestem pewien, że jest coś, co można poprawić.

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

Całkiem prosto, Mathporównuje każdą funkcję prototypu z dowolną wartością (0,9, prawdopodobnie wiele innych wartości działa) i porównuje z wynikiem funkcji czarnej skrzynki.
Testowany w Google Chrome, zepsuje się, jeśli wejściowa funkcja czarnej skrzynki nie jest jednym z wyzwalaczy.

Odciąć mnóstwo bajtów dzięki Kudłatemu i Neilowi.

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)));

Gnida
źródło
1
Bardzo podobne do rozwiązania, nad którym pracowałem nad kilkoma piwami, ale nie mogłem się zorientować. 2 szybkie oszczędności, które mogę dostrzec: 0.3 -> .3i przypisać Mathdo m wewnątrz getOwnPropertyNames() .
Kudłaty
1
Udało mi się dostać to w dół do 71 bajtów: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. Zauważyłem również @Shaggy find. +''Robi ciąg porównać, co oznacza, że tylko trzeba sprawdzić jeden punkt. To ,0sprawia, że ​​pomijamy Math.atan2.
Neil
@Neil, nie wygląda na ,0 to, że jest potrzebne: tio.run/##Lc6xDoMgEMbxvU/RMEFq2TvgG1jdjYknomLkzghp7dPTqEz/…
Shaggy
@Shaggy Myślę, że to zależy od implementacji; w przeglądarce Firefox atan2poprzedza acoshtablicę zwróconą przez Object.getOwnPropertyNames.
Neil,
Jeśli ktoś się zastanawiał, to rozwiązanie działa, ponieważ pierwszą niefunkcją getOwnPropertyNamesjest Math.E, a wszystkie funkcje trygoniczne wyliczają wcześniej.
MattH
2

R , 75 bajtów

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

Wypróbuj online!

Na razie (R v3.5) to działa.
Jeśli w przyszłej wersji R zostanie dodana funkcja pasująca do tego wyrażenia regularnego, to kto wie: P

  • -2 bajty dzięki @Giuseppe
  • -9 bajtów dzięki @JayCe
  • -2 bajty Findzamiastfor
digEmAll
źródło
łał. Bardzo dobrze! Myślę, że 1idziała równie dobrze jak -1ina -2 bajty.
Giuseppe,
@Giuseppe: Byłem pewien, że go przetestowałem i nie działa ... ale prawdopodobnie była to tylko moja wyobraźnia: D
digEmAll
bardzo dobrze! Działa na TIO, zależy od twojej konfiguracji prawdopodobnie w ogólnym przypadku: tio
JayCe
@JayCe: przenoszenie środowiska przez pozycję jest ryzykowne ... na przykład nie działa w RStudio ... na szczęście znalazłem inną funkcję szukającą obiektów wszędzie, z tą samą liczbą bajtów :)
digEmAll
w końcu ... GET jest krótszy (77)
JayCe
1

HP 49G RPL, 88,0 bajtów z wyłączeniem 10-bajtowego nagłówka programu

Kolejne rozwiązanie wykorzystujące liczby zespolone! Wejdź i uruchom go w trybie COMPLEX, APPROX. Pobiera funkcję na stosie.

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

(nowe linie nie mają znaczenia)

W przypadku stałej 2.0 wszystkie dwanaście funkcji wyzwalających jest zdefiniowanych w płaszczyźnie złożonej, więc po prostu oceniamy wszystkie dwanaście i sprawdzamy, która z nich pasuje. Tym razem iteracyjne rozwiązanie jest dłuższe (111,5 bajtów) z powodu przetasowania stosu potrzebnego do jego uzyskania. O ile mi wiadomo, RPL nie pozwala wcześnie wyjść z pętli.

Jason
źródło
W przypadku, gdy zostaną zwrócone dużymi literami, teraz jest to w porządku, gdy edytowałem wyzwanie.
Laikoni,
@JungHwanMin Są wielkie litery. Dzięki za haczyk! Można go zmienić na małe litery ->STR DUP SIZE 3 - " " " " IFTE XOR, 34,5 bajtów. (powinny to być odpowiednio 4 i 3 spacje)
Jason
1

Perl 6 , 39 bajtów

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

Wypróbuj online!

Wygląda na to, że jeden z niewielu korzysta z introspekcji. ioto liczba zespolona, ​​której wartość jest unikalna dla każdej funkcji triggera, więc iterując wszystkie metody, możemy znaleźć pasującą metodę i niejawnie wypluć jej nazwę. Jest tryto konieczne, ponieważ niektóre (niepożądane) metody mają inną sygnaturę.

Phil H.
źródło