Trigonometria della scatola nera


29

Scrivere un programma o una funzione in grado di distinguere i seguenti 12 funzioni trigonometriche: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh .

Al tuo programma viene assegnata una delle funzioni di cui sopra come scatola nera e dovrebbe essere visualizzato il nome della funzione come indicato sopra o il modo in cui è nominato nella tua lingua.

Questo è , quindi vince la risposta più breve in ogni lingua. Dovresti mostrare che il tuo codice funziona correttamente includendo casi di test con tutti e 12 i possibili input. Se la lingua di tua scelta non include i build-in per tutte le funzioni di cui sopra, devi fornire le tue implementazioni sensate di quelle mancanti.

Ulteriori chiarimenti

  • L'uso di numeri complessi per interrogare la scatola nera è consentito se i build-in sottostanti sono in grado di gestirli.
  • Come dom acoshdom atanh= quando si utilizza solo i numeri reali, le query alla funzione di scatola nera può dare errori di dominio. In questo caso dovresti supporre che la scatola nera comunichi solo l'esistenza di un errore, ma non da quale funzione abbia origine.
  • Se invece di un errore viene restituito un altro valore, ad es. NaNO null, viene inviato il materiale inviato.

Grazie per l'utile feedback sulla sandbox !


1
Mathematica è in grado di gestire input simbolici in modo che l'output della funzione venga valutato solo parzialmente, se non del tutto. La differenza che fa è che potrei usare alcuni pattern matching per i calcoli.
JungHwan Min

1
@JungHwanMin Se ciò significa che è possibile accedere ai nomi delle funzioni dall'output simbolico, temo che non sia consentito.
Laikoni,

Risposte:


22

Python 3.6.4 su Linux, 99 byte

Un po 'di una risposta sciocca, ma:

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

Richiede che le funzioni trigonometriche siano una parte del cmathmodulo integrato per ingressi / uscite complessi.


2
@JungHwanMin Credo che tu sia confuso. Sicuramente prendo una vera funzione. Nota che il mio unico riferimento all'input fè f(.029): chiamare la funzione con un valore.
orlp,

1
Lo hai fatto bruteforce?
mbomb007,

4
@ mbomb007 Se per forza bruta intendi un ciclo che fa un paio di centinaia di iterazioni in un battito di ciglia, sì.
orlp

3
Questo è sia sorprendente che sciocco.
Nit


6

Perl 6 , 75 byte

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

Provalo online!

In ogni caso, tutte e dodici le funzioni da discriminare sono integrate e assumono argomenti complessi.

[X~] ("", "a"), <sin cos tan>, ("", "h")genera tutti e dodici i nomi delle funzioni riducendo i tre elenchi di input con concatenazione tra prodotti. Dati questi, .min(...)trova quello con la più piccola differenza rispetto alla funzione di input 2i.


59 byte X possono essere utilizzati per più termini e alcuni altri trucchi per il golf byte
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?-_:_]);}

Provalo online!

Vecchio ma interessante: 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}[_]);}

Provalo online!

Lo -lmswitch in TIO è semplicemente da testare. Se potessi scrivere a un'implementazione perfetta delle funzioni trig standard, otterrai la risposta giusta.

Spiegazione

L'idea era quella di trovare un valore di input in modo tale che quando interpreto le uscite di ciascuna delle funzioni di trigma come numeri interi abbiano dei rimanenti moduli 12. Ciò consentirà loro di essere usati come indici di array.

Per trovare tale valore di input ho scritto il seguente frammento:

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

Se lo esegui (che deve essere compilato con -lm) si sputerà che con un valore di 0.9247 otterrai valori univoci.

Successivamente ho reinterpretato come numeri interi, ho applicato il modulo per 12 e ho preso il valore assoluto. Ciò ha dato a ciascuna funzione un indice. Erano (da 0 a> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, sin, cos, atan, tanh, acos.

Ora potrei semplicemente indicizzare in una matrice di stringhe, ma i nomi sono molto lunghi e molto simili, quindi invece li tolgo da sezioni di una stringa.

Per fare questo costruisco la stringa "asinhacoshatanh" e due array. Il primo array indica quale carattere nella stringa deve essere impostato sul terminatore null, mentre il secondo indica quale carattere nella stringa dovrebbe essere il primo. Questi array contengono: 10,5,5,0,14,10,4,4,9,14,0,9 e 5,1,0,10,11,6,0,1,6,10,11, 5 rispettivamente.

Alla fine, si trattava solo di implementare l'algoritmo di reinterpretazione in modo efficiente in C. Purtroppo, ho dovuto usare il doppio tipo e, con esattamente 3 usi, è stato più veloce usarlo doubletre volte e poi usarlo #define D double\nDDD con solo 2 caratteri. Il risultato è sopra, una descrizione è sotto:

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

Modifica: Sfortunatamente il solo utilizzo di un array raw è in realtà più breve, quindi il codice diventa molto più semplice. Ciò nonostante, il taglio delle corde è stato divertente. In teoria un argomento appropriato potrebbe in realtà trovare le giuste sezioni da solo con un po 'di matematica.


Puoi salvare 20 byte sostituendo puts(...)conprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel il

Puoi salvare 5 byte compilando -DD=doublee sostituendo tutti doublei messaggi nel tuo codice con D. Si noti che il flag deve essere conteggiato per i byte totali.

Altri tre byte possono essere eliminati sostituendo char*[]con int*[], e cambiando l'operatore ternario (:) a un?abs(_)

6

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

Questo si basa sulla risposta di orlp ; ma invece di trovare 1 numero magico, ne troviamo 3! Questo in pratica salva solo byte evitando di mettere le letterali stringa per "sin", "cos" e "tan" più volte, invece costruendo la risposta una parte alla volta.

Il primo numero magico viene utilizzato per determinare se si tratta di una delle funzioni trigonometriche "ad arco", anteponendo una "a" di conseguenza, il secondo per indicare se si tratta di una delle funzioni basate su "sin", "cos" o "abbronzatura", selezionando la stringa appropriata e la terza per indicare se si tratta di una delle funzioni iperboliche, aggiungendo una "h" di conseguenza.

Come la risposta di orlp, utilizza cmathcome input le funzioni del modulo integrato di Python .

5 byte salvati utilizzando l'indicizzazione delle sezioni nella stringa centrale

Alla ricerca dei numeri magici

Per completezza, ecco (più o meno) la sceneggiatura che ho usato per trovare questi numeri magici. Per lo più ho appena lavorato direttamente in un terminale Python, quindi il codice è disordinato, ma fa il lavoro.

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
Ottima seconda risposta! Ti dispiacerebbe condividere il programma che hai usato per trovare i numeri magici?
mbomb007,

Grazie! Ho appena aggiunto il codice per trovare i numeri magici alla risposta, anche se non è terribilmente carino.
nthistle,

4

Python , 108 94 90 byte

Confronta il risultato della funzione di input con i risultati di tutte le funzioni per il valore .2.

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

Provalo online

-14 byte di Jonathan Allen
-4 byte di Rod


Non è necessario re, basta ottenere quelli necessari con lo slicing: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](riscritto per funzionare su TIO poiché l'importazione deve avvenire prima, d=dir(cmath)ma F=deve essere nell'intestazione per non essere conteggiata).
Jonathan Allan,


Molto bella! Grazie
mbomb007 l'

4

Dyalog APL , 25 21 19 byte

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

Provalo online!

-3 grazie a H.PWiz
-2 grazie a ngn

Passa attraverso tutte le funzioni di trigger richieste (che in APL sono 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) più alcune altre cose (passa attraverso -7..7), trova quale corrisponde input○2, e genera quello "con" , che genera comenum∘○


3

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

Grazie a Giacomo Garabello per i suggerimenti.

Sono stato in grado di risparmiare un po 'più di spazio facendo in modo che una macro helper esegua il token-incolla oltre alla mia macro originale che esegue la stringatura.

Nei test ho usato un paio di funzioni di trigge non di libreria per confermare la validità dei risultati. Poiché i risultati tra le funzioni libreria e non libreria non erano esattamente lo stesso valore in virgola mobile, ho confrontato la differenza dei risultati con un valore piccolo ε invece di usare l'uguaglianza.

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

Provalo online!


Sono riuscito a rimuovere 14 byte. Nel TIO puoi trovare i dettagli. Provalo online!
Giacomo Garabello,

+1 da me, ma ho trovato una soluzione sub 200 usando una strategia diversa :)
LambdaBeta,

3

JavaScript, 76 67 66 byte

Non carino ma sono andato troppo lontano nella tana del coniglio con questo su alcune birre per non pubblicarlo. Derivato indipendentemente dalla soluzione di Nit.

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

Provalo online

  • 6 byte salvati grazie a Neil
  • Salvato 1 ciao grazie a l4m2

b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (anche se non so bene perché convertirlo in String per il confronto)
l4m2

Non so perché non ci abbia pensato. Grazie, @ l4m2.
Shaggy,

@ l4m2 Dobbiamo NaNconfrontare uguale a NaN, quindi è quello o Object.is.
Neil,



2

JavaScript, 108 70 byte

Non ho provato a giocare a golf in Javascript puro da anni, quindi sono sicuro che ci sono cose da migliorare qui.

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

Abbastanza semplice, controlla ogni funzione sul Math prototipo rispetto a un valore arbitrario (0.9, molti altri valori probabilmente funzionano) e si confronta con il risultato della funzione scatola nera.
Testato su Google Chrome, si interromperà se la funzione di casella nera di input non è uno dei trigs.

Taglia una tonnellata di byte grazie a Shaggy e 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
Molto simile alla soluzione a cui stavo lavorando su alcune birre ma non riuscivo a capire. 2 risparmi rapidi posso posto: 0.3 -> .3e assegnare Mathai m all'interno getOwnPropertyNames() .
Shaggy,

1
Sono riuscito ad ottenere questo fino a 71 byte: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. Ho notato che ha usato anche @Shaggy find. La +''fa una stringa confrontare, il che significa che abbiamo solo per controllare un punto. Il ,0ci fa saltare Math.atan2.
Neil,


@Shaggy Immagino che dipenda dall'implementazione; in Firefox, atan2precede acoshl'array restituito da Object.getOwnPropertyNames.
Neil,

Se qualcuno si chiedeva, questa soluzione funziona perché la prima non funzione da getOwnPropertyNamesè Math.E, e tutte le funzioni di trigma enumerate prima.
Matt

2

R , 75 byte

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

Provalo online!

Per il momento (R v3.5) funziona.
Se in una futura versione R verrà aggiunta una funzione corrispondente a questa regex, allora chi lo sa: P

  • -2 byte grazie a @Giuseppe
  • -9 byte grazie a @JayCe
  • -2 byte usando Findinvece difor

Wow. Molto bella! Penso che 1ifunzioni anche -1iper -2 byte.
Giuseppe,

@Giuseppe: ero sicuro di averlo provato e non funzionava ... ma probabilmente era solo la mia immaginazione: D
digEmAll

molto bella! Funziona su TIO, dipende dalla tua configurazione probabilmente nel caso generale: tio
JayCe

@JayCe: ottenere l'ambiente attraverso la posizione è rischioso ... per esempio non funziona in RStudio ... fortunatamente ho trovato un'altra funzione che cerca gli oggetti ovunque, con lo stesso bytecount :)
digEmAll

finalmente ... GET è più corto (77)
JayCe,

1

HP 49G RPL, 88,0 byte escluso intestazione del programma a 10 byte

Un'altra soluzione che utilizza numeri complessi! Immettere ed eseguirlo in modalità COMPLESSO, CIRCA. Prende la funzione in pila.

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

(le newline non contano)

Per la costante 2.0, tutte e dodici le funzioni di trig sono definite nel piano complesso, quindi valutiamo solo tutte e dodici e vediamo quale corrisponde. Questa volta, la soluzione iterativa è più lunga (111,5 byte) a causa del mescolamento dello stack necessario per ottenerlo. RPL, per quanto ne so, non ti consente di uscire presto da un ciclo.


Nel caso in cui vengano restituiti in maiuscolo, va bene ora come ho modificato la sfida.
Laikoni,

@JungHwanMin Sono in maiuscolo. Grazie per la cattura! Può essere modificato in minuscolo con ->STR DUP SIZE 3 - " " " " IFTE XOR34,5 byte. (quelli dovrebbero essere 4 e 3 spazi, rispettivamente)
Jason

1

Perl 6 , 39 byte

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

Provalo online!

A quanto pare, uno dei pochi a usare l'introspezione. iecco il numero complesso, il cui valore è univoco per ciascuna funzione di trigge, quindi scorrendo tutti i metodi possiamo trovare il metodo di corrispondenza e sputare implicitamente il suo nome. Il tryè necessario in quanto alcuni metodi (indesiderati) hanno la una firma diversa.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.