Тригонометрія чорної коробки


29

Написати програму або функцію, можна виділити наступні 12 тригонометричні функції: sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh.

Вашій програмі надається одна з перерахованих вище функцій як чорний ящик, і вона повинна виводити ім’я функції або як зазначено вище, або як вона названа вашою мовою.

Це , тому найкоротша відповідь на кожній мові виграє. Ви повинні показати, що ваш код працює правильно, включаючи тестові випадки з усіма 12 можливими входами. Якщо обрана мова не включає вбудовані файли для всіх перерахованих вище функцій, вам слід надати власні розумні реалізації відсутніх.

Подальші роз’яснення

  • Використання складних чисел для запиту чорного поля дозволяється, якщо базові вбудовані файли можуть обробляти їх.
  • Оскільки при використанні лише реальних цифр, запити до функції чорного поля можуть створювати помилки домену. У цьому випадку слід припустити, що чорна скринька повідомляє лише про помилку, але не від того, з якої функції вона походить.гом аcосгодгом атангод=
  • Якщо замість помилки повернеться якесь інше значення, наприклад, NaNабо null, ваше подання має бути в змозі обробити їх.

Дякуємо за корисні відгуки пісочниці !


1
Mathematica може обробляти символічні входи таким чином, що результати роботи оцінюються лише частково, якщо вони є. Різниця, яку він робить, полягає в тому, що я міг би використовувати деякі відповідність шаблонів замість обчислень.
JungHwan Min

1
@JungHwanMin Якщо це означає, що ви можете отримати доступ до імен функцій із символічного виводу, то, боюся, це заборонено.
Laikoni

Відповіді:


22

Python 3.6.4 на Linux, 99 байт

Трохи дурної відповіді, але:

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

Потрібно, щоб тригонометричні функції були однією з вбудованого cmathмодуля для складного вводу / виводу.


2
@JungHwanMin Я вважаю, що ви розгублені. Я, звичайно, беру фактичну функцію. Зверніть увагу , що моя єдина посилання на вхід fбуде f(.029)- виклик функції зі значенням.
orlp

1
Ви жорстоко це зробили?
mbomb007

4
@ mbomb007 Якщо під грубою силою ви маєте на увазі цикл, який робить пару сотень ітерацій за мить ока, так.
orlp

3
Це і дивно, і нерозумно.
Ніт


6

Perl 6 , 75 байт

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

Спробуйте в Інтернеті!

Як це буває, всі дванадцять функцій, до яких слід дискримінувати, є вбудованими і всі приймають складні аргументи.

[X~] ("", "a"), <sin cos tan>, ("", "h")генерує всі дванадцять імен функцій за рахунок скорочення трьох списків вхідних даних з перехресним зв'язком продукту. З огляду на це, .min(...)знаходить той, у якого найменша відмінність від функції введення 2i.


59 байт X можна використовувати для декількох термінів та ще декілька хитрощів до байтів для гольфу
Джо Кінг,

6

C (gcc) , 178 172 байт

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

Спробуйте в Інтернеті!

Старий, але крутий: C (gcc) , 194 байт

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

Спробуйте в Інтернеті!

-lmПеремикач в TIO просто перевірити. Якби ви могли написати ідеальну реалізацію стандартних триггерних функцій, ви отримаєте правильну відповідь.

Пояснення

Ідея полягала в тому, щоб знайти якесь вхідне значення таким, що коли я інтерпретую виходи кожної з функцій триггеру як цілі числа, вони мають різні залишки модуля 12. Це дозволить використовувати їх як індекси масиву.

Для того щоб знайти таке вхідне значення, я написав такий фрагмент:

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

Якщо запустити це (що потрібно скомпілювати з -lm), виплюне, що зі значенням 0,9247 ви отримаєте унікальні значення.

Далі я повторно ввів цілі числа, застосував модуль на 12 і прийняв абсолютне значення. Це давало кожній функції індекс. Вони були (від 0 -> 11): акош, син, асінь, атан, загар, кош, асин, гріх, сос, атан, тань, аксос.

Тепер я міг просто індексувати масив рядків, але назви дуже довгі та дуже схожі, тому замість цього я виймаю їх із фрагментів рядка.

Для цього я будую рядок "asinhacoshatanh" і два масиви. Перший масив вказує, який символ у рядку потрібно встановити на нульовий термінатор, а другий вказує, який символ у рядку повинен бути першим. Ці масиви містять: 10,5,5,0,14,10,4,4,9,14,0,9 та 5,1,0,10,11,6,0,1,6,10,11, 5 відповідно.

Нарешті, це було лише питанням ефективного впровадження алгоритму реінтерпретації в C. На жаль, мені довелося використовувати подвійний тип, і з точно трьома способами використання було швидше просто doubleтри рази просто використовувати #define D double\nDDD лише два символи. Результат вище, опис нижче:

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

Редагувати: На жаль, просто використання необробленого масиву насправді коротше, тому код стає набагато простішим. Тим не менш нарізка струн була веселою. Теоретично відповідний аргумент насправді може скласти правильні фрагменти самостійно з деякою математикою.


Ви можете зекономити 20 байт, замінивши puts(...)наprintf("%.5s","acoshsinh asinhatanhtan cosh asin sin cos atan tanh acos "+5*(_<0?-_:_))
Кертіс Бехтел

Ви можете зберегти 5 байт, компілюючи -DD=doubleта замінюючи всі doubles у коді D. Зауважте, що прапор потрібно рахувати за загальну кількість байтів.

Ще три байта може бути пролита шляхом заміни char*[]з int*[], а також шляхом зміни потрійний оператор (:) до?abs(_)

6

Python 3.6.5 в Linux, 90 85 байт

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

Це спирається на відповідь orlp ; але замість того, щоб знайти 1 магічне число, ми знаходимо 3! Це в основному просто економить байти, уникаючи декількох разів ставити рядкові рядки для "greh", "cos" і "tan", замість того, щоб будувати відповідь по одній частині.

Перше магічне число використовується для визначення того, чи є це одна з тригонометричних функцій "дуги", передбачуючи відповідно "a", друге для того, чи є це одна з функцій на основі "sin", "cos" чи "tan" відповідний рядок, і третя для того, чи є це однією з гіперболічних функцій, додаючи відповідно "h".

Як і відповідь orlp, він використовує функції вбудованого cmathмодуля Python як вхід.

Збережено 5 байт за допомогою індексації фрагментів у середній рядку

Пошук магічних чисел

Для повноти ось ось (більш-менш) сценарій, який я використовував для пошуку цих чарівних чисел. Я здебільшого просто працював прямо в терміналі python, тому код безладний, але це робить роботу.

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
Чудова друга відповідь! Чи не проти поділитися програмою, яку ви використовували для пошуку чарівних чисел?
mbomb007

Спасибі! Я щойно додав код, щоб знайти чарівні числа до відповіді, хоча це не дуже страшно.
nthistle

4

Пітон , 108 94 90 байт

Порівняє результат функції введення з результатами всіх функцій за значенням .2.

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

Спробуйте в Інтернеті

-14 байт Джонатана Аллена
-4 байти за Род


Немає необхідності re, просто дістаньте ті, які потрібні для нарізки: lambda f,d=dir(cmath):[s for s in d[4:12]+d[22:]if eval("cmath."+s)(.2)==f(.2)][0](переписаний для роботи над TIO, оскільки імпорт повинен відбуватися раніше, d=dir(cmath)але він F=повинен бути в заголовку, щоб його не рахували).
Джонатан Аллан


Дуже хороша! Дякую
mbomb007

4

Діялог APL , 25 21 19 байт

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

Спробуйте в Інтернеті!

-3 завдяки H.PWiz
-2 завдяки ngn

Перебирає всі необхідні функції триггеру (які є в APL 1 2 3 5 6 7 ¯1 ¯2 ¯3 ¯5 ¯6 ¯7○2), а також деякі інші речі (це проходить через користь -7..7), знаходить, яка з них відповідає input○2, і виводить, що "з" , яка виводить якnum∘○


3

C (gcc) з -lm, 374 346 324 байт

Дякую Джакомо Гарабелло за пропозиції.

Я зміг заощадити трохи більше місця, маючи допоміжний макрос do-token-pasteing на додаток до мого оригінального макросу, який робить стримування.

У тестах я використовував пару небібліотечних триггерних функцій для підтвердження достовірності результатів. Оскільки результати між функціями бібліотеки та небібліотеки не були абсолютно однаковими значеннями з плаваючою комою, я порівняв різницю результатів із малим значенням ε замість того, щоб використовувати рівність.

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

Спробуйте в Інтернеті!


Мені вдалося видалити 14 байт. У TIO ви можете знайти деталі. Спробуйте в Інтернеті!
Джакомо Гарабелло

+1 від мене, але я знайшов рішення для суб-200, використовуючи іншу стратегію :)
LambdaBeta

3

JavaScript, 76 67 66 байт

Не дуже, але я зайшов надто далеко вниз у кролячій норі з цим над кількома пивами, щоб не розмістити його. Похідне незалежно від розчину Ніта.

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

Спробуйте в Інтернеті

  • Збережено 6 байт завдяки Нілу
  • Збережено 1 бай завдяки l4m2

b=>Object.getOwnPropertyNames(M=Math).find(x=>M[x](.8)+M==b(.8)+M)? (хоча я не зовсім знаю, навіщо конвертувати у String для порівняння)
l4m2

Не знаю, чому я про це не думав. Спасибі, @ l4m2.
Кудлатий

@ l4m2 Нам потрібно NaNпорівняти рівне NaN, так це або те, або Object.is.
Ніл



2

JavaScript, 108 70 байт

Я не пробував гольф у чистому Javascript у віки, тому впевнений, що тут є речі, які можна покращити.

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

Досить просто, перевіряє кожну функцію Mathпрототипу на довільне значення (0,9, багато інших значень, ймовірно, працює) і порівнює з результатом функції чорного поля.
Перевірена в Google Chrome, буде порушена, якщо функція чорного поля введення не є одним із спустошек.

Відріжте тонну байтів завдяки Шаггі та Нілу.

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
Дуже схоже на рішення, над яким я працював над кількома пивами, але не міг зрозуміти. 2 швидких заощадження, які я можу помітити: 0.3 -> .3і призначити Mathв m межах getOwnPropertyNames() .
Кудлатий

1
Мені вдалося отримати це вниз до 71 байт: t=>Object.getOwnPropertyNames(m=Math).find(f=>m[f](.9,0)+''==t(.9)+'');. Я помітив, що @Shaggy також використовується find. +''Робить рядок порівняння, означає , що ми повинні перевірити тільки одну точку. ,0Змушує нас пропустити Math.atan2.
Ніл

@Neil, це не схоже на ,0 потрібне: tio.run/##Lc6xDoMgEMbxvU/RMEFq2TvgG1jdjYknomLkzghp7dPTqEz/…
Shaggy

@Shaggy я думаю, це залежить від реалізації; у Firefox, що atan2передує acoshмасиву, повернутому Object.getOwnPropertyNames.
Ніл

Якщо хтось цікавився, це рішення працює, тому що перша нефункція з getOwnPropertyNames- це Math.E, а всі триггерні функції перераховуються до цього.
Метт

2

R , 75 байт

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

Спробуйте в Інтернеті!

На даний момент (R v3.5) він працює.
Якщо в майбутній R-версії буде додана функція, що відповідає цьому регулярному вираженню, то хто знає: P

  • -2 байти завдяки @Giuseppe
  • -9 байт завдяки @JayCe
  • -2 байти, використовуючи Findзамістьfor

Ого. Дуже хороша! Я думаю, 1iпрацює так само, як і -1iдля -2 байт.
Джузеппе

@Giuseppe: Я був впевнений, що протестував його, і він не працював ... але, мабуть, це була лише моя фантазія: D
digEmAll

дуже хороша! Працює над TIO, залежить від вашої конфігурації, ймовірно, у загальному випадку: tio
JayCe

@JayCe: отримання середовища через позицію ризиковано ... наприклад, це не працює в RStudio ... на щастя, я знайшов іншу функцію пошуку об’єктів скрізь, з тим самим числом рахунків :)
digEmAll

нарешті ... GET коротше (77)
JayCe

1

HP 49G RPL, 88,0 байт, виключаючи 10-байтний заголовок програми

Ще одне рішення з використанням складних чисел! Введіть і виконайте його в режимі КОМПЛЕКС, АППРОКС. Бере функцію на стеку.

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

(новинки не мають значення)

Для постійного 2.0 всі дванадцять триггерних функцій визначені в складній площині, тому ми просто оцінюємо всі дванадцять і бачимо, яка з них відповідає. Цього разу ітераційне рішення довше (111,5 байт) через переміщення стека, необхідного для його отримання. Наскільки я знаю, RPL не дозволяє вам вийти з циклу рано.


У випадку, якщо вони будуть повернені у верхньому регістрі, це добре зараз, коли я редагував виклик.
Лайконі

@JungHwanMin Це великі регістри. Дякую за улов! Він може бути змінений на малі ->STR DUP SIZE 3 - " " " " IFTE XORрегістри, 34,5 байт. (вони повинні бути відповідно 4 і 3 місця)
Джейсон

1

Perl 6 , 39 байт

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

Спробуйте в Інтернеті!

За зовнішнім виглядом речей одна з небагатьох, яка використовує самоаналіз. iось комплексне число, значення якого унікальне для кожної триггерної функції, тому, повторюючи всі методи, ми можемо знайти метод узгодження і неявно виплюнути його ім'я. Це tryпотрібно, оскільки деякі (небажані) методи мають різний підпис.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.