Black Box Trigonometry

29

Escreva um programa ou função que pode distinguir os seguintes 12 funções trigonométricas: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh.

Seu programa recebe uma das funções acima como caixa preta e deve exibir o nome da função conforme indicado acima ou da maneira como é nomeado no seu idioma.

Isso é , então a resposta mais curta em cada idioma vence. Você deve mostrar que seu código funciona corretamente incluindo casos de teste com todas as 12 entradas possíveis. Se o idioma de sua escolha não incluir build-ins para todas as funções acima, você deverá fornecer suas próprias implementações sensíveis das ausentes.

Esclarecimentos adicionais

  • O uso de números complexos para consultar a caixa preta é permitido se os build-ins subjacentes puderem lidar com eles.
  • Como quando se utiliza apenas números reais, as consultas para a função caixa negra pode dar erros de domínio. Nesse caso, você deve supor que a caixa preta comunica apenas a existência de um erro, mas não da qual função ela se origina.dom acoshdom atanh=
  • Se, em vez de um erro, algum outro valor, por exemplo, NaNou null, for retornado, seu envio deverá ser capaz de lidar com eles.

Obrigado pelo feedback útil sobre o sandbox !

Laikoni
fonte
11
O Mathematica pode manipular entradas simbólicas para que a saída da função seja avaliada apenas parcialmente, se houver. A diferença que faz é que eu poderia usar alguma correspondência de padrões em vez de cálculos.
JungHwan Min
11
@JungHwanMin Se isso significa que você pode acessar os nomes das funções na saída simbólica, receio que não seja permitido.
Laikoni 10/07/2018

Respostas:

22

Python 3.6.4 no Linux, 99 bytes

Uma resposta meio boba, mas:

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

Requer que as funções trigonométricas sejam uma fora do cmathmódulo interno para entrada / saída complexa.

orlp
fonte
2
@JungHwanMin Eu acredito que você está confuso. Eu certamente tenho uma função real. Observe que minha única referência à entrada fé f(.029)- chamar a função com um valor.
orlp 10/07/19
11
Você fez isso com força bruta?
mbomb007
4
@ mbomb007 Se por força bruta você quer dizer um loop que faz algumas centenas de iterações em um piscar de olhos, sim.
orlp
3
Isso é incrível e bobo.
Nit
11
Relevante?
Mbomb007
6

Perl 6 , 75 bytes

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

Experimente online!

Por acaso, todas as doze funções a serem discriminadas estão integradas e todas têm argumentos complexos.

[X~] ("", "a"), <sin cos tan>, ("", "h")gera todos os doze nomes de funções reduzindo as três listas de entrada com concatenação de produtos cruzados. Dado isso, .min(...)encontra aquele em que a menor diferença da função de entrada é 2i.

Sean
fonte
59 bytes X podem ser usados ​​para vários termos e alguns outros truques para bytes de golfe
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?-_:_]);}

Experimente online!

Antigo, mas interessante: 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}[_]);}

Experimente online!

A -lmopção no TIO é apenas para testar. Se você pudesse escrever uma implementação perfeita das funções trigonométricas padrão, obteria a resposta certa.

Explicação

A idéia era encontrar algum valor de entrada tal que, quando eu interpreto as saídas de cada uma das funções trigonométricas como números inteiros, elas tenham diferentes remanescentes no módulo 12. Isso permitirá que elas sejam usadas como índices de matriz.

Para encontrar esse valor de entrada, escrevi o seguinte trecho:

#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 você executar isso (que precisa ser compilado com -lm), ele citará que, com um valor de 0,9247, você obtém valores exclusivos.

Em seguida, reinterpetuei como números inteiros, apliquei o módulo em 12 e peguei o valor absoluto. Isso deu a cada função um índice. Eles eram (de 0 a 11): acosh, sinh, asinh, atanh, tan, cosh, asin, pecado, cos, atan, tanh, acos.

Agora eu poderia apenas indexar em uma matriz de seqüências de caracteres, mas os nomes são muito longos e muito semelhantes; portanto, eu os retiro das fatias de uma sequência.

Para fazer isso, construo a string "asinhacoshatanh" e duas matrizes. A primeira matriz indica qual caractere da string deve ser definido como terminador nulo, enquanto a segunda indica qual caractere da string deve ser o primeiro. Essas matrizes contêm: 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 respectivamente.

Finalmente, era apenas uma questão de implementar o algoritmo de reinterpretação de forma eficiente em C. Infelizmente, eu tive que usar o tipo duplo e, com exatamente 3 usos, foi mais rápido usar apenas doubletrês vezes e usar #define D double\nDDD apenas 2 caracteres. O resultado está acima, uma descrição está abaixo:

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: Infelizmente, apenas o uso de uma matriz bruta é realmente mais curto, portanto o código se torna muito mais simples. No entanto, o corte das cordas foi divertido. Em teoria, um argumento apropriado pode, na verdade, apresentar as fatias certas por conta própria com um pouco de matemática.

LambdaBeta
fonte
Você pode salvar 20 bytes substituindo puts(...)porprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Curtis Bechtel
Você pode salvar 5 bytes compilando -DD=doublee substituindo todos os doubles no seu código por D. Observe que o sinalizador precisa ser contado para o total de bytes.
Um adicional de três bytes pode ser eliminado por substituição char*[]com int*[], e alterando o operador ternário (:) a uma?abs(_)
6

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

Isso se baseia na resposta do orlp ; mas em vez de encontrar 1 número mágico, encontramos 3! Isso basicamente salva os bytes, evitando colocar as strings literais para "sin", "cos" e "tan" várias vezes, em vez de criar a resposta uma parte de cada vez.

O primeiro número mágico é usado para determinar se é uma das funções trigonométricas do "arco", acrescentando um "a" de acordo, e o segundo para se é uma das funções baseadas em "pecado", "cos" ou "bronzeado", selecionando a sequência apropriada e a terceira, para saber se é uma das funções hiperbólicas, acrescentando um "h" de acordo.

Como a resposta do orlp, ele usa as funções do cmathmódulo interno do Python como entrada.

Salvou 5 bytes usando a indexação de fatia na cadeia do meio

Encontrando os números mágicos

Para completar, aqui está (mais ou menos) o script que eu usei para encontrar esses números mágicos. Na maioria das vezes, trabalhei diretamente em um terminal python, então o código é confuso, mas faz o trabalho.

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
fonte
11
Ótima segunda resposta! Você se importaria de compartilhar o programa usado para encontrar os números mágicos?
mbomb007
Obrigado! Acabei de adicionar código para encontrar os números mágicos na resposta, embora não seja muito bonito.
Nthistle 13/07/19
4

Python , 108 94 90 bytes

Compara o resultado da função de entrada com os resultados de todas as funções do valor .2.

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

Experimente online

-14 bytes por Jonathan Allen
-4 bytes por Rod

mbomb007
fonte
Não é necessário re, basta obter os necessários com o fatiamento: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](reescrito para funcionar no TIO, pois a importação deve ocorrer antes e d=dir(cmath)ainda F=deve estar no cabeçalho para não ser contada).
Jonathan Allan
Muito agradável! Graças
mbomb007
4

Dyalog APL , 25 21 19 bytes

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

Experimente online!

-3 graças a H.PWiz
-2 graças a ngn

Percorre todas as funções trigonométricas necessárias (que são no APL 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2) e mais algumas coisas (isso ocorre -7..7), localiza qual corresponde input○2e gera "com" , que gera comonum∘○

dzaima
fonte
3

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

Obrigado a Giacomo Garabello pelas sugestões.

Consegui economizar um pouco mais de espaço fazendo com que uma macro auxiliar fizesse a colagem de tokens, além da macro original, que faz strings.

Nos testes, usei algumas funções trigonométricas não relacionadas à biblioteca para confirmar a validade dos resultados. Como os resultados entre as funções de biblioteca e não de biblioteca não eram exatamente o mesmo valor de ponto flutuante, comparei a diferença dos resultados com um valor pequeno ε em vez de usar igualdade.

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

Experimente online!

ErikF
fonte
Eu consegui remover 14 bytes. No TIO, você encontra os detalhes. Experimente online!
Giacomo Garabello
+1 de mim, mas eu fiz encontrar uma solução sub 200 usando uma estratégia diferente :)
LambdaBeta
3

JavaScript, 76 67 66 bytes

Não é bonito, mas fui longe demais na toca do coelho com algumas cervejas para não publicá-la. Derivado independentemente da solução de Nit.

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

Experimente online

  • Guardado 6 bytes graças a Neil
  • Guardado 1 bye graças a l4m2
Shaggy
fonte
b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (embora eu não sei bem porque convertido em String para comparar)
l4m2
Não sei por que não pensei nisso. Obrigado, @ l4m2.
Salsicha
@ l4m2 Precisamos NaNcomparar igual a NaN, então é isso ou Object.is.
Neil
2

Ruby , 71 67 bytes

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

Experimente online!

Asone Tuhid
fonte
2

JavaScript, 108 70 bytes

Eu não tentei jogar golfe em Javascript puro há anos, então tenho certeza de que há coisas a melhorar aqui.

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

Bem simples, verifica todas as funções no Math protótipo em relação a um valor arbitrário (0,9, muitos outros valores provavelmente funcionam) e se compara ao resultado da função de caixa preta.
Testado no Google Chrome, será interrompido se a função de caixa preta de entrada não for um dos truques.

Corte uma tonelada de bytes graças 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)));

Nit
fonte
11
Muito parecido com a solução em que estava trabalhando em algumas cervejas, mas não consegui descobrir. 2 economias rápidas que posso identificar: 0.3 -> .3e atribuir Matha m dentro getOwnPropertyNames() .
Shaggy
11
Eu consegui isso até 71 bytes: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. Notei que o @Shaggy também foi usado find. O +''compara uma string, o que significa que precisamos verificar apenas um ponto. O ,0que nos faz saltar Math.atan2.
1111 Neil
@ Neil, não parece que ,0 é necessário: tio.run/##Lc6xDoMgEMbxvU/RMEFq2TvgG1jdjYknomLkzghp7dPTqEz/…
Shaggy
@ Shagy Acho que depende da implementação; no Firefox, atan2precede acoshna matriz retornada por Object.getOwnPropertyNames.
1111 Neil
Se alguém estava se perguntando, esta solução funciona porque a primeira não função de getOwnPropertyNamesé Math.E, e todas as funções trig enumeradas antes disso.
Matth
2

R , 75 bytes

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

Experimente online!

No momento (R v3.5), ele funciona.
Se em uma futura versão R for adicionada uma função correspondente a este regex, quem sabe: P

  • -2 bytes graças a @ Giuseppe
  • -9 bytes graças a @JayCe
  • -2 bytes usando em Findvez defor
digEmAll
fonte
Uau. Muito agradável! Eu acho que 1ifunciona tão bem quanto -1ipara -2 bytes.
Giuseppe
@ Giuseppe: Eu tinha certeza de que tinha testado e não estava funcionando ... mas provavelmente era apenas minha imaginação: D
digEmAll 11/18
muito agradável! Funciona no TIO, depende da sua configuração provavelmente no caso geral: tio
Jayce
@JayCe: recebendo o meio ambiente através da posição é arriscado ... por exemplo, ele não funciona em rstudio ... felizmente eu encontrei uma outra pesquisa função para os objetos em todos os lugares, com o mesmo bytecount :)
digEmAll
finalmente... GET é mais curto (77)
Jayce
1

HP 49G RPL, 88,0 bytes, excluindo o cabeçalho do programa de 10 bytes

Outra solução usando números complexos! Digite e execute-o no modo COMPLEX, APPROX. Assume a função na pilha.

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

(as novas linhas não importam)

Para a constante 2.0, todas as doze funções trigonométricas são definidas no plano complexo; portanto, apenas avaliamos todas as doze e vemos qual delas corresponde. Desta vez, a solução iterativa é mais longa (111,5 bytes) devido ao embaralhamento da pilha necessário para obtê-lo. A RPL, até onde eu sei, não permite que você saia do circuito mais cedo.

Jason
fonte
Caso eles sejam retornados em maiúsculas, tudo bem agora, quando editei o desafio.
Laikoni
@JungHwanMin São maiúsculas. Obrigado pela captura! Pode ser modificado para minúsculas com ->STR DUP SIZE 3 - " " " " IFTE XOR34,5 bytes. (aqueles deveriam ser de 4 e 3 lugares, respectivamente)
Jason
1

Perl 6 , 39 bytes

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

Experimente online!

Pela aparência das coisas, um dos poucos a usar a introspecção. iaqui está o número complexo, cujo valor é único para cada função trigonométrica, portanto, percorrendo todos os métodos, podemos encontrar o método correspondente e citar implicitamente seu nome. otry é necessário, pois alguns métodos (indesejados) têm a uma assinatura diferente.

Phil H
fonte