Calcola l'inverso del fattoriale


30

Scrivi il codice più breve che prenderà qualsiasi numero reale maggiore di 1 come input e produrrà il suo fattoriale inverso positivo. In altre parole, risponde alla domanda "quale numero fattoriale è uguale a questo numero?". Utilizzare la funzione Gamma per estendere la definizione fattoriale a qualsiasi numero reale come descritto qui .

Per esempio:

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

perché 3! = 6e3.390077654! = 10

Regole

  • È vietato l'uso di funzioni fattoriali incorporate o funzioni gamma o funzioni che si basano su queste funzioni.
  • Il programma dovrebbe essere in grado di calcolarlo a 5 cifre decimali, con la capacità teorica di calcolarlo con qualsiasi precisione (dovrebbe contenere un numero che può essere reso arbitrariamente grande o piccolo per ottenere precisione arbitraria)
  • È consentita qualsiasi lingua, vince il codice più breve in caratteri.

Ho fatto un esempio funzionante qui . Dare un'occhiata.


2
Ciò potrebbe utilizzare alcuni altri casi di test, in particolare per coprire input zero e negativi.
Peter Taylor,

Ho modificato che l'input dovrebbe essere maggiore di 1 perché altrimenti potrebbero esserci risposte multiple.
Jens Renders

1
Ci possono essere più risposte comunque a meno che non si aggiunga anche un requisito secondo cui l'output deve essere maggiore di 1.
Peter Taylor

Il tuo esempio di lavoro fornisce 3.99999 quando immesso 24. Quindi una soluzione del genere è accettabile?
rubik,

sì perché questo può essere visto come 4, con 5 decimali corretti
Jens Renders

Risposte:


13

Javascript (116)

Magie nere qui! Fornisce un risultato in pochi millisecondi .
Solo funzioni matematiche elementari utilizzate: 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)

Peccato che LaTeX non sia supportato su codegolf ma in sostanza, ho codificato un solutore di Newton per f(y)=gamma(y)-n=0e x=y-1(perché lo x!è gamma(x+1)) e approssimazioni per le funzioni gamma e digamma.

L'approssimazione gamma è approssimazione Stirling L'approssimazione
Digamma utilizza la formula di Eulero Maclaurin
La funzione digamma è la derivata della funzione gamma divisa per la funzione gamma: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);

Casi test :

10 => 3.390062988090518
120 => 4.99999939151027
720 => 6.00000187248195
40320 => 8.000003557030217
3628800 => 10.000003941731514

Risposta molto bella anche se non soddisfa la precisione richiesta e funziona solo per numeri inferiori a 706
Jens Renders

@JensRenders, beh, ho aggiunto alcune iterazioni del solutore di Newton, ho cambiato l'ipotesi iniziale e una migliore approssimazione per la funzione gamma. Questo dovrebbe adattarsi ora alle regole. Fammi ora se va bene :)
Michael M.

Sì, ora è perfetto, l'ho votato :)
Jens Renders

1
È possibile salvare 1 carattere:n=prompt(M=Math)
Florent

Prova a eseguire il codice su un numero elevato come $ 10 ^ {10 ^ 6} $ e assicurati di ottenere un risultato intero
David G. Stork

13

Matematica - 74 54 49

Sarà il modo giusto

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

Se abbandoniamo semplicemente il test ?NumberQ, funzionerebbe comunque, ma lanci alcuni avvertimenti sgradevoli, che svanirebbero se passassimo all'integrazione simbolica Integrate, ma questo sarebbe illegale (suppongo), perché la funzione verrebbe automaticamente convertita in Gammafunzione. Inoltre possiamo liberarci della funzione esterna in quel modo.

Comunque

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

Per avere un input adeguato, basta definire la funzione (non si può vincere MatLab)

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

Se fosse consentito il fattoriale incorporato

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

Quanto sopra non fornisce un numero intero (che è l'argomento per una vera funzione fattoriale). Ciò che segue:

Floor[InverseFunction[Gamma][n]-1]

Ahh tutte quelle funzioni integrate! Non penso che questo sia battibile se non in modo simile.
rubik

4
Mathematica è così ingiusta per le cose matematiche! : D
Michael M.

1
dal nome stesso MATHematica
Dadan

È NumberQrichiesto il test del modello? O parens in E^(-t)? E 'barare a girare NIntegratea Integrate? Probabilmente ... :)
orion

Si sta trasformando in una vera sfida;)
mmumboss

6

zato: 72 46 caratteri

Questa è quasi una misura perfetta ... c'è un "linguaggio" là fuori che sembra essere pensato proprio per il golf in matematica: ised . La sua sintassi offuscata crea un codice molto breve (senza variabili nominate, solo slot di memoria interi e molti operatori versatili a carattere singolo). Definendo la funzione gamma usando un integrale, ho ottenuto 80 caratteri apparentemente casuali

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

Qui, lo slot di memoria $ 4 è una funzione fattoriale, lo slot di memoria $ 6 ha una funzione di bisection e lo slot di memoria $ 2 dovrebbe essere impostato su input (dato prima di approvare questo codice). Le fessure $ 0 e $ 1 sono i limiti di bisection. Esempio di chiamata (presupponendo che il codice sopra sia nel file inversefactorial.ised)

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

Certo, potresti usare il builtin! operatore, nel qual caso si arriva a 45 caratteri

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

Attenzione, la precendenza dell'operatore a volte è strana.

Modifica: ricordato di incorporare le funzioni invece di salvarle. Batti Mathematica con 72 personaggi!

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

E usando il! incorporato ottieni 41.


Un aggiornamento scaduto un anno:

Ho appena realizzato che era altamente inefficiente. Ridotto a 60 caratteri:

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

Se viene usato utf-8 (anche Mathematica), arriviamo a 57:

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

Una riscrittura leggermente diversa può ridurla a 46 (o 27 se si utilizza built-in!):

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

Gli ultimi due caratteri possono essere rimossi se si è d'accordo con la stampa della risposta due volte.


Sarei sorpreso se vedessi qualcuno battere questo: o
Jens Renders

@JensRenders: l'ho appena fatto;)
mmumboss

Per chiarire la discussione sull'accuratezza: è impostato da .1 (fase di integrazione) e 99 (limite di integrazione). Bisection va alla precisione della macchina. Il limite di bisection @ 1,99 può essere mantenuto a 99 a meno che non si desideri inserire numeri sopra (99!).
Orione,

@mmumboss ti ha ripreso :)
orion,

5

MATLAB 54 47

Se scelgo le giuste sfide MATLAB è davvero bello per il golf :). Nel mio codice trovo la soluzione all'equazione (ux!) = 0 in cui u è l'input dell'utente e x la variabile da risolvere. Ciò significa che u = 6 porterà a x = 3, ecc ...

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

L'accuratezza può essere cambiata modificando il limite superiore dell'integrale, che è impostato su 99. Abbassandolo si cambierà l'accuratezza dell'uscita come segue. Ad esempio per un input di 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;

eccetera.


dovresti specificare l'opzione per l'accuratezza poiché è richiesta nelle regole! "Dovrebbe contenere un numero che può essere reso arbitrariamente grande o piccolo per ottenere una precisione arbitraria"
Jens Renders,

Non lo vedo neanche nelle soluzioni ised e Mathematica? Ma lo esaminerò ..
mmumboss

1
Vedo il numero 99 nella versione ised e la versione matematica è battuta comunque
Jens Renders

Data la posizione nel codice, questo è probabilmente il limite superiore per l'integrale. Nel mio codice questo è inf. Quindi sì, se cambio questa inf in 99, la mia risposta diventa meno accurata, il che significa che questo numero influenza la precisione e quindi rispondo alle regole. Se lo cambio a 99, salvo anche un carattere;)
mmumboss

Ma dopo aver cambiato inf in 99 soddisfa la precisione richiesta?
rubik,

3

Python - 199 caratteri

Ok, quindi avrai bisogno di molto spazio di stack e molto tempo, ma ehi, ci arriverà!

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)

Ecco un altro approccio con ancora più ricorsione.

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)

Entrambi questi possono essere testati a >>>f(10,1)condizione che si imposti il ​​limite di ricorsione intorno a 10000. Probabilmente più di un decimale di precisione non sarà completo con alcun limite di ricorsione realistico.

Incorporando i commenti e alcune modifiche, fino a 199 caratteri.

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
Questa è una code-golfdomanda, quindi è necessario fornire la risposta più breve, indicando la lunghezza della soluzione.
VisioN,

Un metodo carino, ma il problema è che non puoi garantire che questo troverà mai la risposta ... Inoltre, questo è codegolf zo che potresti provare a minimizzare l'uso del personaggio.
Jens Renders

1
Python's random () usa un Mersenne Twister che credo percorra lo spazio dei float di Python, quindi dovrebbe sempre terminare se c'è una risposta all'interno della tolleranza.
intx13

Vuoi dire che restituisce ogni valore float prima di ripeterne uno? in tal caso, questo codice sarebbe valido se si fosse in grado di superare lo overflow dello stack
Jens Renders

2
Il codice è in grado, è solo che io e te potremmo non avere il tempo né le risorse del computer per eseguirlo fino al completamento;)
intx13

3

Python 2.7 - 215 189 caratteri

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)

Uso:

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

Per modificare la precisione: passare 1e-5a un numero più piccolo per una maggiore precisione, un numero più grande per una precisione peggiore. Per una migliore precisione, probabilmente vorrai dare un valore migliore e.

Questo implementa semplicemente la funzione fattoriale come fe quindi esegue una ricerca binaria per affinare il valore più accurato dell'inverso dell'input. Presuppone che la risposta sia inferiore o uguale a 99 (non funzionerebbe sicuramente con una risposta di 365, ricevo un errore di overflow della matematica). L'utilizzo di spazio e tempo molto ragionevole termina sempre.

In alternativa, sostituisci if abs(n-f(x))<=10**-5: print x;breakcon print xper radere 50 caratteri . Andrà in loop per sempre, dandoti una stima sempre più accurata. Non sono sicuro se questo si adatterebbe con le regole però.


Non conoscevo quel sito per contare i caratteri. Lo uso sempre cat file | wc -c.
rubik

@rubik: oh bello, non pensavo di usarlo. entrambi corrispondono =)
Claudiu il

2

dg - 131 133 byte

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

Poiché dg produce il bytecode CPython, questo dovrebbe valere anche per Python, ma oh ... Alcuni esempi:

$ 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: Aggiunti due byte perché non ricordavo che dovrebbe accettare anche i float!


Il mio dà 42.8006566063, in modo che corrispondano entro 5 cifre di precisione!
Claudiu,

È fantastico! Non so dove sia il limite superiore, ma dovrebbe rompersi da qualche parte. Per 1e100dà: 69.95780520000001, per 1e150l'emette 96.10586423000002, mentre per 1e200esso esplode. Ma davvero non so se questi risultati siano affidabili ...
rubik

1

R , 92 byte

Una funzione, gche accetta input ze genera il fattoriale inverso di quel numero

C'è sicuramente molto altro da scoprire, quindi se vedi qualcosa che posso migliorare, per favore fatemelo sapere.

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)

Provalo online!

Ungolfed e commentato

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

Provalo online!


0

Javascript (senza usare i loop!)

Per fare questo, ho usato una nota approssimazione numerica dell'inverso dell'approssimazione fattoriale Stirling , (e ho anche preso ispirazione da questo .. tosse .. tosse .. codice di qualcun altro ...)

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)))))))))
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.