Рассчитать обратное факториала

30

Напишите кратчайший код, который будет принимать любое действительное число больше 1 и будет выводить положительный обратный факториал. Другими словами, он отвечает на вопрос «какое число факториал равно этому числу?». Используйте функцию Gamma, чтобы расширить определение факториала до любого действительного числа, как описано здесь. .

Например:

input=6 output=3 
input=10 output=3.390077654

потому что 3! = 6и3.390077654! = 10

правила

  • Запрещается использовать встроенные факторные функции или гамма-функции или функции, которые зависят от этих функций.
  • Программа должна иметь возможность вычислять ее до 5 десятичных цифр, с теоретической способностью вычислять ее с любой точностью (она должна содержать число, которое можно сделать произвольно большим или маленьким, чтобы получить произвольную точность)
  • Разрешен любой язык, выигрывает самый короткий код в символах.

Я сделал рабочий пример здесь . Взглянуть.

Йенс Рендерс
источник
2
Это может использовать еще несколько тестовых случаев, в частности, чтобы охватить нулевые и отрицательные входные данные.
Питер Тейлор
Я отредактировал, что ввод должен быть больше 1, потому что в противном случае могут быть несколько ответов.
Jens Renders
1
В любом случае, может быть несколько ответов, если только вы не добавите требование, чтобы выходное значение было больше 1.
Питер Тейлор
Ваш рабочий пример дает 3.99999 при вводе 24. Так приемлемо ли такое решение?
Рубик
да, потому что это можно рассматривать как 4 до 5 десятичных знаков правильно
Йенс Рендерс

Ответы:

13

Javascript (116)

Черная магия здесь! Дает результат за несколько миллисекунд .
Только элементарные математические функции используются: ln, pow,exponential

x=9;n=prompt(M=Math);for(i=1e4;i--;)x+=(n/M.exp(-x)/M.pow(x,x-.5)/2.5066/(1+1/12/x+1/288/x/x)-1)/M.log(x);alert(x-1)

Жаль, что LaTeX не поддерживается на Codegolf, но в основном я написал кодировщик ньютона для f(y)=gamma(y)-n=0и x=y-1(потому чтоx! этоgamma(x+1) ) и аппроксимации для функций гаммы и дигаммы.

Гамма-аппроксимация - приближение Стирлинга
Дигамма-аппроксимация с использованием формулы Эйлера Маклаурина
дигамма- функция - это производная гамма-функции, деленная на гамма-функцию:f'(y)=gamma(y)*digamma(y)

Ungolfed:

n = parseInt(prompt());
x = 9; //first guess, whatever but not too high (<500 seems good)

//10000 iterations
for(i=0;i<10000;i++) {

  //approximation for digamma
  d=Math.log(x);

  //approximation for gamma
  g=Math.exp(-x)*Math.pow(x,x-0.5)*Math.sqrt(Math.PI*2)*(1+1/12/x+1/288/x/x);

  //uncomment if more precision is needed
  //d=Math.log(x)-1/2/x-1/12/x/x+120/x/x/x/x;
  //g=Math.exp(-x)*Math.pow(x,x-0.5)*Math.sqrt(Math.PI*2)*(1+1/12/x+1/288/x/x-139/51840/x/x/x);

  //classic newton, gamma derivative is gamma*digamma
  x-=(g-n)/(g*d);
}

alert(x-1);

Тестовые случаи:

10 => 3.390062988090518
120 => 4.99999939151027
720 => 6.00000187248195
40320 => 8.000003557030217
3628800 => 10.000003941731514
Майкл М.
источник
Очень хороший ответ, хотя он не соответствует требуемой точности и работает только для чисел менее 706
Йенс Рендерс
@JensRenders, ну, я добавил несколько итераций решателя Ньютона, изменил начальное предположение и лучшее приближение для гамма-функции. Это должно соответствовать правилам сейчас. Позвольте мне сейчас, если все в порядке :)
Майкл М.
Да, теперь это отлично, я проголосовал за это :)
Jens Renders
1
Вы можете сэкономить 1 символ:n=prompt(M=Math)
Флорент
Попробуйте запустить свой код для большого числа, такого как $ 10 ^ {10 ^ 6} $, и убедитесь, что вы получите целочисленный результат
Дэвид Дж. Сторк
13

Mathematica - 74 54 49

Правильный путь будет

f[x_?NumberQ]:=NIntegrate[t^x E^-t,{t,0,∞}]
x/.FindRoot[f@x-Input[],{x,1}]

Если мы просто отбросим тест, ?NumberQон все равно сработает, но выдаст несколько неприятных предупреждений, которые исчезнут, если мы перейдем к символьной интеграции Integrate, но это будет недопустимо (я полагаю), потому что функция будет автоматически преобразована в Gammaфункцию. Также мы можем избавиться от внешней функции таким образом.

Так или иначе

x/.FindRoot[Integrate[t^x E^-t,{t,0,∞}]-Input[],{x,1}]

Черт с правильным вводом, просто определение функции (не может позволить MatLab победить)

x/.FindRoot[Integrate[t^x E^-t,{t,0,∞}]-#,{x,1}]&

Если бы встроенные факториалы были разрешены

N@InverseFunction[#!&]@Input[]

Выше не дает целого числа (которое является аргументом для истинной факториальной функции). Следующее делает:

Floor[InverseFunction[Gamma][n]-1]
рассекать
источник
Ааа все эти встроенные функции! Я не думаю, что это можно победить, кроме как аналогичным образом.
Рубик
4
Mathematica настолько несправедлива к математике! : D
Майкл М.
1
от самого имени MATHematica
Дадан
NumberQТребуется ли проверка шаблона? Или Паренс E^(-t)? Является ли это обман , чтобы обратиться NIntegrateк Integrate? Вероятно ... :)
Орион
Это превращается в настоящий вызов;)
mmumboss
6

ароматизированные: 72 46 персонажей

Это почти идеально подходит ... есть "язык", который, кажется, предназначен именно для математического гольфа: ised . Его запутанный синтаксис обеспечивает очень короткий код (без именованных переменных, только целочисленные слоты памяти и множество универсальных операторов с одним символом). Определив гамма-функцию с помощью интеграла, я получил до 80, казалось бы, случайных символов

@4{:.1*@+{@3[.,.1,99]^x:*exp-$3}:}@6{:@{$4::@5avg${0,1}>$2}$5:}@0,0@1,99;$6:::.

Здесь слот памяти $ 4 - это факториальная функция, ожидается, что функция деления на слоты памяти $ 6 и слот памяти $ 2 будут установлены на вход (заданный перед поиском этого кода). Слоты $ 0 и $ 1 являются границами пополам. Пример вызова (при условии, что приведенный выше код находится в файле inversefactorial.ised)

bash> ised '@2{556}' --f inversefactorial.ised
556
5.86118

Конечно, вы можете использовать встроенный! оператор, в этом случае вы получите до 45 символов

@6{:@{{@5avg${0,1}}!>$2}$5:}@0,0@1,99;$6:::.

Осторожность, предпочтение оператора иногда странно.

Редактировать: запомнил встроенные функции вместо их сохранения. Удар Mathematica с 72 персонажами!

@0,0@1,99;{:@{{:.1*@+{@3[.,.1,99]^x:*exp-$3}:}::@5avg${0,1}>$2}$5:}:::.

И используя! Встроенный вы получаете 41.


Годовое обновление:

Я только что понял, что это было крайне неэффективно. Гольф до 60 символов:

@0#@1,99;{:@{.1*@3[.,.1,99]^@5avg${0,1}@:exp-$3>$2}$5:}:::.

Если используется utf-8 (Mathematica делает это тоже), мы получаем 57:

@0#@1,99;{:@{.1*@3[.,.1,99]^@5avg${0,1}·exp-$3>$2}$5:}∙.

Немного другая перезапись может сократить его до 46 (или 27 при использовании встроенного!):

{:x_S{.5@3[.,.1,99]^avgx·exp-$3*.1<$2}:}∙∓99_0

Последние два символа можно удалить, если вы согласны с тем, что ответ будет напечатан дважды.

Орион
источник
Я был бы удивлен, если бы увидел, как кто-то победил: o
Jens Renders
@JensRenders: Я только что сделал;)
mmumboss
Чтобы прояснить дискуссию о точности: она установлена ​​на .1 (шаг интеграции) и 99 (предел интеграции). Бисекция идет на точность машины. Предел бисекции @ 1,99 можно сохранить на 99, если вы не хотите вводить числа выше (99!).
Орион
@mmumboss снова тебя получил :)
orion
5

MATLAB 54 47

Если я выберу правильные задачи, MATLAB действительно хорош для игры в гольф :). В моем коде я нахожу решение уравнения (ux!) = 0, в котором u - пользовательский ввод, а x - переменная для решения. Это означает, что u = 6 приведет к x = 3 и т. Д.

@(x)fsolve(@(y)u-quad(@(x)x.^y./exp(x),0,99),1)

Точность может быть изменена путем изменения верхнего предела интеграла, который установлен на 99. Снижение этого изменит точность вывода следующим образом. Например, для ввода 10:

upper limit = 99; answer = 3.390077650833145;
upper limit = 20; answer = 3.390082293675363;
upper limit = 10; answer = 3.402035336604546;
upper limit = 05; answer = 3.747303578099607;

и т.п.

mmumboss
источник
Вы должны указать опцию для точности, как это требуется в правилах! «Он должен содержать число, которое можно сделать произвольным большим или маленьким, чтобы получить произвольную точность»
Jens Renders
Я не вижу этого в решениях ised и Mathematica? Но я посмотрю на это ..
mmumboss
1
Я вижу номер 99 в версии ised, а версия mathematica в любом случае побита
Jens Renders
Учитывая положение в коде, это, вероятно, верхний предел для интеграла. В моем коде это инф. Так что да, если я поменяю эту инфу на 99, мой ответ станет менее точным, что означает, что это число влияет на точность, и поэтому я отвечаю правилам. Если я изменю его на 99, я даже
сохраню
Но после изменения inf на 99 он соответствует требуемой точности?
Рубик
3

Питон - 199 символов

Итак, вам понадобится много места в стеке и много времени, но, эй, оно будет!

from random import *
from math import e
def f(x,n):
    q=randint(0,x)+random()
    z=0
    d=0.1**n
    y=d
    while y<100:
            z+=y**q*e**(-y)*d
            y+=d
    return q if round(z,n)==x else f(x,n)

Вот еще один подход с еще большей рекурсией.

from random import *
from math import e
def f(x,n):
    q=randint(0,x)+random()
    return q if round(h(q,0,0.1**n,0),n)==x else f(x,n)
def h(q,z,d,y):
    if y>100:return z
    else:return h(q,z+y**q*e**(-y)*d,d,y+d)

Оба из них могут быть проверены при >>>f(10,1)условии, что вы установите предельное значение рекурсии около 10000. Более одного десятичного знака точности, вероятно, не будет дополнено каким-либо реалистичным предельным значением рекурсии.

Включая комментарии и несколько модификаций, до 199 символов.

from random import*
from math import*
def f(x,n):
    q=random()*x+random()
    z=y=0
    while y<100:
            z+=y**q*e**-y*0.1**n
            y+=0.1**n
    return q if round(z,n)==x else f(x,n)
intx13
источник
2
Это code-golfвопрос, поэтому вам нужно дать кратчайший ответ, указав длину вашего решения.
VisioN
Хороший метод, но проблема в том, что вы не можете гарантировать, что он когда-нибудь найдет ответ ... Кроме того, это Codegolf ZO, вы можете попытаться свести к минимуму использование символов.
Jens Renders
1
Функция Python random () использует Mersenne Twister, который, как я считаю, обходит пространство поплавков Python, поэтому он всегда должен завершаться, если в пределах допуска есть ответ.
intx13
Вы имеете в виду, что он возвращает каждое значение с плавающей точкой перед повторением одного? если это так, то этот код будет действителен, если вы сможете преодолеть переполнение стека
Jens Renders
2
Код способен, просто у вас и у меня может не быть ни времени, ни ресурсов компьютера, чтобы выполнить его до конца;)
intx13
3

Python 2,7 - 215 189 символов

f=lambda t:sum((x*.1)**t*2.71828**-(x*.1)*.1for x in range(999))
n=float(raw_input());x=1.;F=0;C=99
while 1:
 if abs(n-f(x))<1e-5:print x;break
 F,C,x=f(x)<n and(x,C,(x+C)/2)or(F,x,(x+F)/2)

Использование:

# echo 6 | python invfact_golf.py
2.99999904633
# echo 10 | python invfact_golf.py
3.39007514715
# echo 3628800 | python invfact_golf.py
9.99999685376

Чтобы изменить точность: измените 1e-5на меньшее число для большей точности, большее число для худшей точности. Для большей точности вы, вероятно, хотите дать лучшее значение для e.

Это просто реализует функцию факториала как f , а затем выполняет двоичный поиск, чтобы отточить наиболее точное значение инверсии входных данных. Предполагается, что ответ меньше или равен 99 (наверняка он не сработает для ответа 365, я получаю математическую ошибку переполнения). Очень разумное использование пространства и времени, всегда заканчивается.

С другой стороны , заменить if abs(n-f(x))<=10**-5: print x;breakс print xсбрить 50 символов . Это будет цикл навсегда, давая вам более точную оценку. Не уверен, что это будет соответствовать правилам, хотя.

Клаудиу
источник
Я не знал этот сайт для подсчета символов. Я всегда использую cat file | wc -c.
Рубик
@рубик: о, хорошо, не думал использовать это. они оба совпадают =)
Claudiu
2

dg - 131 133 байта

o,d,n=0,0.1,float$input!
for w in(-2..9)=>while(sum$map(i->d*(i*d)**(o+ 10**(-w))/(2.718281**(i*d)))(0..999))<n=>o+=10**(-w)
print o

Так как dg создает байт-код CPython, это должно учитываться и для Python, но ... Вот несколько примеров:

$ dg gam.dg 
10
3.3900766499999984
$ dg gam.dg 
24
3.9999989799999995
$ dg gam.dg 
100
4.892517629999997
$ dg gam.dg 
12637326743
13.27087070999999
$ dg gam.dg  # i'm not really sure about this one :P it's instantaneous though
28492739842739428347929842398472934929234239432948923
42.800660880000066
$ dg gam.dg  # a float example
284253.232359
8.891269689999989

РЕДАКТИРОВАТЬ: Добавил два байта, потому что я не помню, чтобы он также принимал float!

рубик
источник
Мой дает 42.8006566063, поэтому они соответствуют в 5 цифр точности!
Клаудиу
Замечательно! Я не знаю, где находится верхняя граница, но она должна где-то сломаться. Ибо 1e100он дает:, 69.95780520000001для 1e150него выводит 96.10586423000002, а для 1e200него взрывается. Но на самом деле я не знаю, достоверны ли эти результаты ...
rubik
1

R , 92 байта

Функция, gкоторая принимает на вход zи выводит обратный факториал этого числа

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

library(pryr)
g=f(z,uniroot(f(a,integrate(f(x,x^a*exp(-x)),0,Inf)$v-z),c(0,z+1),tol=1e-9)$r)

Попробуйте онлайн!

Ungolfed и комментируется

library(pryr)                     # Add pryr to workspace
inv.factorial = f(z,              # Declare function which  
  uniroot(                        # Finds the root of
    f(a, integrate(               # The integral of 
      f(x, x^a*exp(-x))           # The gamma function
        ,0 ,Inf                   # From 0 to Infinity
      )$value-z                   # Minus the input value, `z`
    ), c(0, z+1),                 # On the bound of 0 to z+1
    tol = 1e-323                  # With a specified tolerance
  )$root                          # And outputs the root
)                                 # End function

Попробуйте онлайн!

Тейлор Скотт
источник
0

Javascript (без использования циклов!)

Чтобы сделать это, я использовал известную числовую аппроксимацию обратного приближения факторинга Стирлинга (и также получил вдохновение от этого ... кашля ... кашля ... кода кого-то еще ...)

function f(n){
    if(n==1) return 1;
    else if(n==2) return 2;
    else if(n==6) return 3;
    else if(n==24) return 4;
    else{
        return Math.round((((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592))+(((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592))+(((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592))+(Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592)))/Math.log((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592)))))/Math.log((((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592))+(Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592)))/Math.log((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592)))))))/Math.log((((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592))+(((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592))+(Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592)))/Math.log((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592)))))/Math.log((((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592))+(Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592)))/Math.log((Math.log(n)/Math.LN10 *  Math.log(10.) - .5 * Math.log(2.*3.141592)))))))))
    }
}
Антонио Раганьин
источник