Черный ящик Тригонометрия


29

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

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

Это , поэтому выигрывает самый короткий ответ на каждом языке. Вы должны показать, что ваш код работает правильно, включив контрольные примеры со всеми 12 возможными входными данными. Если выбранный вами язык не включает встроенные модули для всех вышеперечисленных функций, вы должны предоставить собственные разумные реализации отсутствующих.

Дальнейшие разъяснения

  • Использование комплексных чисел для запроса черного ящика разрешено, если базовые встроенные модули могут их обрабатывать.
  • Как dom acoshdom atanh= при использовании только действительные числа, запросы к функции черного ящика может дать ошибки домена. В этом случае вы должны предположить, что черный ящик сообщает только о существовании ошибки, но не из какой функции он исходит.
  • Если вместо ошибки возвращается какое-то другое значение, например, NaNили null, то ваше представление должно быть в состоянии обработать их.

Спасибо за полезную обратную связь песочницы !


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

1
@JungHwanMin Если это означает, что вы можете получить доступ к именам функций из символьного вывода, то, боюсь, это не разрешено.
Лайкони

Ответы:


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. К сожалению, мне пришлось использовать двойной тип, и с ровно 3-мя использованием было быстрее использовать doubleтри раза, чем использовать #define D double\nDDD всего 2 символа. Результат выше, описание ниже:

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! Это в основном просто экономит байты, избегая многократного размещения строковых литералов для «sin», «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

Python , 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

Дьялог АПЛ , 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 байта

Спасибо Джакомо Гарабелло за предложения.

Я смог сэкономить немного больше места, используя вспомогательный макрос, выполняющий вставку токена в дополнение к моему исходному макросу, который выполняет строковое форматирование.

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

#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 от меня, но я нашел решение sub 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 Я думаю, это зависит от реализации; в Firefox atan2предшествует acoshв массиве, возвращаемом Object.getOwnPropertyNames.
Нил

Если кому-то интересно, это решение работает, потому что первая не-функция из getOwnPropertyNames- это Math.E, а все триггерные функции перечисляются до этого.
MattH

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


1

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

Другое решение с использованием комплексных чисел! Введите и выполните его в режиме COMPLEX, APPROX. Берет функцию в стеке.

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.