Обчисліть обернену факторіальну


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. Отже, чи таке рішення прийнятне?
rubik

так, тому що це можна зрозуміти як 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 не підтримується на кодовому гольфі, але в основному я зашифрував ньютонівський вирішувач для f(y)=gamma(y)-n=0та x=y-1(тому що x!є gamma(x+1)) та наближень для функцій гамма та digamma.

Гамма-наближення - це наближення Стірлінга
Використання апроксимації Дігамми Формула Ейлера Маклауріна Функція Дігама
є похідною від гамма-функції, поділеної на гамма-функцію:f'(y)=gamma(y)*digamma(y)

Безумовно:

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
Jens Renders

@JensRenders, я додав кілька ітерацій розв'язувача Ньютона, змінив початкові здогадки і краще наближення до гамма-функції. Це має відповідати правилам зараз. Дозвольте мені, якщо це нормально :)
Майкл М.

Так, зараз це ідеально, я проголосував за нього :)
Jens Renders

1
Ви можете зберегти 1 чару:n=prompt(M=Math)
Флорент

Спробуйте запустити свій код на великій кількості, таких як $ 10 ^ {10 ^ 6} $, і переконайтеся, що ви отримаєте цілий результат
David G. Stork

13

Математика - 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]

Ах, всі ці вбудовані функції! Я не вважаю, що це можна зрозуміти, хіба що подібним чином.
rubik

4
Математика настільки несправедлива для математичних речей! : D
Майкл М.

1
з самої назви Mathematica
Dadan

Чи NumberQпотрібна перевірка візерунка? Або парен в E^(-t)? Є чи це обман , щоб звернутися NIntegrateдо Integrate? Напевно ... :)
orion

Це перетворюється на справжній виклик;)
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;

тощо.


вам слід вказати варіант точності, оскільки це потрібно в правилах! "Він повинен містити число, яке можна зробити довільним великим чи малим, щоб отримати довільну точність"
Jens Renders

Я не бачу його в рішеннях ised та Mathematica? Але я
загляну

1
Я бачу число 99 у версії ised, а математичну версію все одно б’ють
Jens Renders

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

Але після зміни inf на 99 це відповідає необхідній точності?
rubik

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)

2
Це code-golfпитання, тому потрібно надати найкоротшу відповідь, вказавши тривалість свого рішення.
VisioN

Хороший метод, але проблема полягає в тому, що ви не можете гарантувати, що на це коли-небудь знайдете відповідь ... Крім того, це кодовий гольф, який ви можете спробувати мінімізувати використання символів.
Jens Renders

1
Випадковий () Python використовує трістер Mersenne, який, я вважаю, проходить простір плавців Python, тому він завжди повинен припинятися, якщо є відповідь у межах допуску.
intx13

Ви маєте на увазі, що він повертає кожне значення float перед повторенням? якщо це так, цей код буде дійсним, якщо ви зможете подолати переповнення стека
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.
рубік

@rubik: приємно, не думав це використовувати. вони обидва відповідають =)
Клавдіу

2

дг - 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

EDIT: Додано два байти, тому що я не пам’ятав, що він також повинен приймати плавки!


Моє дає 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)

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

Безголовний і коментований

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