Black-Box-Trigonometrie


29

Schreiben Sie ein Programm oder eine Funktion, die die folgenden 12 trigonometrischen Funktionen unterscheiden: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh.

Ihr Programm erhält eine der oben genannten Funktionen als Blackbox und sollte den Namen der Funktion entweder wie oben angegeben oder wie in Ihrer Sprache angegeben ausgeben.

Das ist , also gewinnt die kürzeste Antwort in jeder Sprache. Sie sollten zeigen, dass Ihr Code korrekt funktioniert, indem Sie Testfälle mit allen 12 möglichen Eingaben einschließen. Wenn die Sprache Ihrer Wahl nicht für alle oben genannten Funktionen Build-Ins enthält, müssen Sie Ihre eigenen sinnvollen Implementierungen der fehlenden Funktionen bereitstellen.

Weitere Erläuterungen

  • Die Verwendung komplexer Zahlen zur Abfrage der Black Box ist zulässig, wenn die zugrunde liegenden Build-Ins damit umgehen können.
  • Wie , wenn nur reelle Zahlen verwenden, Anfragen an die Black - Box - Funktion Domain - Fehler geben können. In diesem Fall sollten Sie davon ausgehen, dass die Blackbox nur das Vorhandensein eines Fehlers meldet, nicht jedoch, von welcher Funktion sie stammt.dOm eincOshdOm einteinnh=
  • Wenn anstelle eines Fehlers ein anderer Wert zurückgegeben wird, z. B. NaNoder null, sollte Ihre Übermittlung in der Lage sein, diesen zu verarbeiten.

Vielen Dank für das hilfreiche Sandbox-Feedback !


1
Mathematica kann symbolische Eingaben verarbeiten, sodass die Funktionsausgabe, wenn überhaupt, nur teilweise ausgewertet wird. Der Unterschied besteht darin, dass ich anstelle von Berechnungen einen Mustervergleich verwenden könnte.
JungHwan Min

1
@JungHwanMin Wenn dies bedeutet, dass Sie über die symbolische Ausgabe auf die Funktionsnamen zugreifen können, ist dies leider nicht zulässig.
Laikoni

Antworten:


22

Python 3.6.4 unter Linux, 99 Byte

Eine dumme Antwort, aber:

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

Erfordert, dass die trigonometrischen Funktionen cmathfür komplexe Ein- / Ausgaben eine aus dem integrierten Modul sind.


2
@JungHwanMin Ich glaube, dass Sie verwirrt sind. Ich nehme mit Sicherheit eine tatsächliche Funktion ein. Beachten Sie, dass mein einziger Verweis auf die Eingabe darin fbesteht f(.029), die Funktion mit einem Wert aufzurufen.
Orlp

1
Hast du das bruteforce?
mbomb007

4
@ mbomb007 Wenn du mit brachialer Gewalt eine Schleife meinst, die ein paar hundert Iterationen im Handumdrehen durchführt, ja.
Orlp

3
Das ist sowohl erstaunlich als auch albern.
Nit


6

Perl 6 , 75 Bytes

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

Probieren Sie es online!

Es ist so, dass alle zwölf zu unterscheidenden Funktionen eingebaut sind und komplexe Argumente enthalten.

[X~] ("", "a"), <sin cos tan>, ("", "h")Generiert alle zwölf Funktionsnamen durch Reduzieren der drei Eingabelisten mit produktübergreifender Verkettung. Angesichts dessen .min(...)findet man diejenige, bei der der kleinste Unterschied zur Eingabefunktion besteht 2i.


59 Bytes X können für mehrere Begriffe und ein paar andere Tricks zum Golf-Bytes verwendet werden
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?-_:_]);}

Probieren Sie es online!

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

Probieren Sie es online!

Der -lmSchalter in TIO dient lediglich zum Testen. Wenn Sie eine perfekte Implementierung der Standard-Triggerfunktionen schreiben könnten, würden Sie die richtige Antwort erhalten.

Erläuterung

Die Idee war, einen Eingabewert so zu finden, dass, wenn ich die Ausgaben jeder der Triggerfunktionen als Ganzzahlen interpretiere, sie unterschiedliche Reste modulo 12 haben. Dadurch können sie als Array-Indizes verwendet werden.

Um einen solchen Eingabewert zu finden, habe ich folgendes Snippet geschrieben:

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

Wenn Sie das ausführen (was mit -lm kompiliert werden muss), wird darauf hingewiesen, dass Sie mit einem Wert von 0,9247 eindeutige Werte erhalten.

Als nächstes habe ich als ganze Zahlen neu interpretiert, modulo mit 12 angewendet und den absoluten Wert genommen. Dies gab jeder Funktion einen Index. Sie waren (von 0 -> 11): acosh, sinh, asinh, atanh, tan, cosh, asin, sin, cos, atan, tanh, acos.

Jetzt konnte ich nur eine Reihe von Zeichenfolgen indizieren, aber die Namen sind sehr lang und sehr ähnlich, und stattdessen nehme ich sie aus Schnitten einer Zeichenfolge heraus.

Dazu konstruiere ich den String "asinhacoshatanh" und zwei Arrays. Das erste Array gibt an, welches Zeichen in der Zeichenfolge als Nullterminator festgelegt werden soll, während das zweite angibt, welches Zeichen in der Zeichenfolge das erste sein soll. Diese Arrays enthalten: 10,5,5,0,14,10,4,4,9,14,0,9 und 5,1,0,10,11,6,0,1,6,10,11, 5 jeweils.

Schließlich ging es nur darum, den Neuinterpretationsalgorithmus in C effizient zu implementieren. Leider musste ich den Doppeltyp verwenden, und mit genau drei Verwendungen war es schneller, nur doubledrei Mal zu verwenden, als #define D double\nDDD mit nur zwei Zeichen. Das Ergebnis ist oben, eine Beschreibung ist unten:

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

Bearbeiten: Leider ist nur die Verwendung eines unformatierten Arrays tatsächlich kürzer, sodass der Code viel einfacher wird. Trotzdem hat das Aufschneiden der Saiten Spaß gemacht. Theoretisch könnte ein geeignetes Argument mit etwas Mathematik tatsächlich die richtigen Schichten für sich finden.


Sie können 20 Bytes einsparen, indem Sie puts(...)durchprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel

Sie können 5 Bytes einsparen, indem Sie mit kompilieren -DD=doubleund alle doubles in Ihrem Code durch ersetzen D. Beachten Sie, dass das Flag für die Gesamtzahl der Bytes gezählt werden muss.

Weitere drei Bytes können durch Ersetzen char*[]von int*[]und Ändern des ternären Operators (? :) in anabs(_)

6

Python 3.6.5 unter Linux, 90 bis 85 Byte

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

Dies baut auf der Antwort von Orlp auf ; aber anstatt 1 magische Zahl zu finden, finden wir 3! Dies spart im Grunde genommen nur Bytes, indem vermieden wird, dass die String-Literale mehrmals für "sin", "cos" und "tan" gesetzt werden, anstatt die Antwort einzeln zu erstellen.

Die erste magische Zahl wird verwendet, um zu bestimmen, ob es sich um eine der "bogen" -trigonometrischen Funktionen handelt, wobei dementsprechend ein "a" vorangestellt wird, die zweite, ob es sich um eine der "sin" -, "cos" - oder "tan" -basierten Funktionen handelt die entsprechende Zeichenfolge und die dritte, um festzustellen, ob es sich um eine der hyperbolischen Funktionen handelt, wobei dementsprechend ein "h" angehängt wird.

Wie bei der Antwort von orlp werden die Funktionen des integrierten cmathModuls von Python als Eingabe verwendet.

5 Bytes durch Slice-Indizierung in der mittleren Zeichenfolge gespeichert

Die magischen Zahlen finden

Der Vollständigkeit halber hier (mehr oder weniger) das Skript, mit dem ich diese magischen Zahlen gefunden habe. Ich habe meistens nur direkt in einem Python-Terminal gearbeitet, der Code ist also chaotisch, aber es erledigt die Arbeit.

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
Tolle zweite Antwort! Würde es Ihnen etwas ausmachen, das Programm zu teilen, mit dem Sie die magischen Zahlen gefunden haben?
mbomb007

Vielen Dank! Ich habe gerade Code hinzugefügt, um die magischen Zahlen zu finden, obwohl es nicht besonders hübsch ist.
Distel

4

Python , 108 94 90 Bytes

Vergleicht das Ergebnis der Eingabefunktion mit den Ergebnissen aller Funktionen für den Wert .2.

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

Probieren Sie es online aus

-14 Bytes von Jonathan Allen
-4 Bytes von Rod


Keine Notwendigkeit für re, bekommt nur das , die mit Slicing benötigt: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](neu geschriebene Arbeit auf TIO als der Import , bevor passieren muss d=dir(cmath)noch F=im Header sein muss , um nicht gezählt).
Jonathan Allan


Sehr schön! Danke
mbomb007

4

Dyalog APL , 25 21 19 Bytes

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

Probieren Sie es online!

-3 danke an H.PWiz
-2 danke an ngn

Durchläuft alle erforderlichen Triggerfunktionen (die in APL vorhanden sind 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) sowie einige weitere Dinge (dies geht durch -7..7), findet, welche übereinstimmen input○2, und gibt das "mit" aus , welches als ausgibtnum∘○


3

C (GCC) mit -lm, 374 346 324 Bytes

Vielen Dank an Giacomo Garabello für die Vorschläge.

Ich konnte etwas mehr Platz sparen, indem ich zusätzlich zu meinem ursprünglichen Makro, das das Stringing übernimmt, ein Hilfsmakro zum Einfügen von Token verwendete.

In den Tests habe ich einige nicht bibliothekarische Triggerfunktionen verwendet, um die Gültigkeit der Ergebnisse zu bestätigen. Da die Ergebnisse zwischen Bibliotheks- und Nicht-Bibliotheksfunktionen nicht exakt der gleiche Gleitkommawert waren, habe ich die Differenz der Ergebnisse mit einem kleinen Wert ε verglichen, anstatt Gleichheit zu verwenden.

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

Probieren Sie es online!


Ich habe es geschafft, 14 Bytes zu entfernen. In der TIO finden Sie die Details. Probieren Sie es online!
Giacomo Garabello

+1 von mir, aber ich habe eine Sub-200-Lösung mit einer anderen Strategie gefunden :)
LambdaBeta

3

JavaScript, 76 67 66 Bytes

Nicht hübsch, aber ich bin mit ein paar Bier viel zu weit in den Kaninchenbau gegangen, um es nicht zu posten. Abgeleitet unabhängig von der Lösung von Nit.

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

Probieren Sie es online aus


b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (obwohl ich nicht genau weiß, warum ich zu String konvertiere, um zu vergleichen)
l4m2

Ich weiß nicht, warum ich nicht daran gedacht habe. Danke, @ l4m2.
Shaggy

@ l4m2 Wir müssen NaNgleich vergleichen NaN, also ist es entweder das oder Object.is.
Neil



2

JavaScript, 108 70 Bytes

Ich habe lange nicht mehr versucht, in reinem Javascript zu golfen, daher bin ich mir sicher, dass es hier noch Verbesserungspotential gibt.

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

Ziemlich einfach, prüft jede Funktion auf der Math Prototyps gegen einen beliebigen Wert (0,9, viele andere Werte funktionieren wahrscheinlich) und vergleicht sie mit dem Ergebnis der Black-Box-Funktion.
Getestet in Google Chrome. Bricht ab, wenn die Eingabe-Blackbox-Funktion nicht zu den Trigs gehört.

Schneiden Sie eine Menge Bytes dank Shaggy und Neil ab.

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
Sehr ähnlich zu der Lösung, an der ich bei ein paar Bieren gearbeitet habe, konnte es aber nicht ganz herausfinden. 2 schnelle Einsparungen kann ich erkennen: 0.3 -> .3und weisen Mathauf m innerhalb getOwnPropertyNames() .
Shaggy

1
Ich schaffte es diese bis zu 71 Bytes zu erhalten: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. Mir ist aufgefallen, dass @Shaggy auch benutzt wird find. Das +''bedeutet, dass wir nur einen Punkt überprüfen müssen. Das ,0lässt uns überspringen Math.atan2.
Neil

@Neil, es sieht nicht so aus, als ob das ,0 benötigt wird: tio.run/##Lc6xDoMgEMbxvU/RMEFq2TvgG1jdjYknomLkzghp7dPTqEz/…
Shaggy

@ Shaggy Ich denke, es ist implementierungsabhängig; in Firefox steht atan2vor acoshdem Array, das von zurückgegeben wird Object.getOwnPropertyNames.
Neil

Wenn sich jemand wundert, funktioniert diese Lösung, da die erste getOwnPropertyNamesNichtfunktion von Math.E ist und alle Triggerfunktionen davor aufgelistet sind.
MattH

2

R , 75 Bytes

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

Probieren Sie es online!

Im Moment (R v3.5) funktioniert es.
Wenn in einer zukünftigen R-Version eine Funktion hinzugefügt wird, die dieser Regex entspricht, wer weiß dann: P

  • -2 Bytes dank @Giuseppe
  • -9 Bytes dank @JayCe
  • -2 Bytes mit Findanstelle vonfor

Wow. Sehr schön! Ich denke 1ifunktioniert genauso gut wie -1ifür -2 Bytes.
Giuseppe

@ Giuseppe: Ich war mir sicher, dass ich es getestet hatte und es funktionierte nicht ... aber wahrscheinlich war es nur meine Einbildung: D
digEmAll

Sehr schön! Funktioniert mit TIO, hängt von deiner Konfiguration ab, wahrscheinlich im allgemeinen Fall: tio
JayCe

@ JayCe: Es ist riskant, die Umgebung in Position zu bringen ... zum Beispiel funktioniert es in RStudio nicht ... Zum Glück habe ich eine andere Funktion gefunden, die überall nach Objekten sucht, mit demselben Bytecount :)
digEmAll


1

HP 49G RPL, 88,0 Byte ohne 10-Byte-Programm-Header

Eine andere Lösung mit komplexen Zahlen! Geben Sie es im Modus KOMPLEX, CA. ein und führen Sie es aus. Übernimmt die Funktion auf dem Stapel.

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

(die Zeilenumbrüche spielen keine Rolle)

Für die Konstante 2.0 sind alle zwölf Triggerfunktionen in der komplexen Ebene definiert. Wir werten also nur alle zwölf aus und sehen, welche übereinstimmen. Diesmal ist die iterative Lösung länger (111,5 Byte), da der Stack gemischt werden muss, um sie abzurufen. RPL lässt Sie, soweit ich weiß, nicht früh aus einer Schleife ausbrechen.


Falls sie als Großbuchstaben zurückgegeben werden, ist das jetzt in Ordnung, da ich die Herausforderung bearbeitet habe.
Laikoni

@ JungHwanMin Sie sind in Großbuchstaben. Danke für den Fang! Sie kann mit ->STR DUP SIZE 3 - " " " " IFTE XOR34,5 Byte in Kleinbuchstaben geändert werden . (diese sollen 4 bzw. 3 Leerzeichen sein)
Jason

1

Perl 6 , 39 Bytes

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

Probieren Sie es online!

So wie es aussieht, einer der wenigen, der Selbstbeobachtung anwendet. iHier ist die komplexe Zahl, deren Wert für jede Triggerfunktion eindeutig ist. Wenn wir also alle Methoden durchlaufen, können wir die passende Methode finden und implizit ihren Namen ausspucken. Das trywird benötigt, da einige (unerwünschte) Methoden eine andere Signatur haben.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.