Calculer l'inverse de factorielle


30

Écrivez le code le plus court qui prendra en entrée tout nombre réel supérieur à 1 et produira sa factorielle inverse positive. En d'autres termes, il répond à la question "quel nombre factoriel est égal à ce nombre?". Utilisez la fonction Gamma pour étendre la définition de factorielle à n'importe quel nombre réel comme décrit ici .

Par exemple:

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

parce que 3! = 6et3.390077654! = 10

Règles

  • Il est interdit d'utiliser des fonctions factorielles intégrées ou des fonctions gamma, ou des fonctions qui reposent sur ces fonctions.
  • Le programme devrait pouvoir le calculer à 5 chiffres décimaux, avec la capacité théorique de le calculer avec n'importe quelle précision (il devrait contenir un nombre qui peut être rendu arbitraire grand ou petit pour obtenir une précision arbitraire)
  • N'importe quelle langue est autorisée, le code le plus court en caractères l'emporte.

J'ai fait un exemple de travail ici . Regarde.


2
Cela pourrait utiliser d'autres cas de test, en particulier pour couvrir les entrées nulles et négatives.
Peter Taylor

J'ai édité que l'entrée devrait être supérieure à 1 car sinon il pourrait y avoir plusieurs réponses.
Jens Renders

1
De toute façon, il peut y avoir plusieurs réponses, sauf si vous ajoutez également une exigence selon laquelle la sortie doit être supérieure à 1.
Peter Taylor

Votre exemple de travail donne 3.99999 lorsqu'il est entré 24. Une telle solution est-elle donc acceptable?
rubik

oui car cela peut être vu comme 4, à 5 décimales correctes
Jens Renders

Réponses:


13

Javascript (116)

La magie noire ici! Donne un résultat en quelques millisecondes .
Seules les fonctions mathématiques élémentaires utilisées: 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)

Dommage que LaTeX ne soit pas supporté sur codegolf mais en gros, j'ai codé un solveur newton pour f(y)=gamma(y)-n=0et x=y-1(car x!c'est gamma(x+1)) et des approximations pour les fonctions gamma et digamma.

L'approximation gamma est l'approximation de Stirling L'approximation
Digamma utilise la formule d'Euler Maclaurin
La fonction digamma est la dérivée de la fonction gamma divisée par la fonction gamma:f'(y)=gamma(y)*digamma(y)

Non golfé:

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);

Cas de test:

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

Très belle réponse même si elle ne répond pas à la précision requise et ne fonctionne que pour les nombres inférieurs à 706
Jens Renders

@JensRenders, eh bien, j'ai ajouté quelques itérations du solveur newton, changé la supposition initiale et une meilleure approximation pour la fonction gamma. Cela devrait maintenant correspondre aux règles. Laissez-moi maintenant si ça va :)
Michael M.

Oui, maintenant c'est parfait, je l'ai voté :)
Jens Renders

1
Vous pouvez enregistrer 1 personnage:n=prompt(M=Math)
Florent

Essayez d'exécuter votre code sur un grand nombre tel que $ 10 ^ {10 ^ 6} $ et assurez-vous d'obtenir un résultat entier
David G. Stork

13

Mathematica - 74 54 49

La bonne façon sera

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

Si nous abandonnions simplement le test, ?NumberQcela fonctionnerait toujours, mais lancerait des avertissements désagréables, qui disparaîtraient si nous passions à l'intégration symbolique Integrate, mais ce serait illégal (je suppose), car la fonction serait automatiquement convertie en Gammafonction. Nous pouvons également nous débarrasser de la fonction externe de cette façon.

En tous cas

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

Pour vérifier avec une entrée appropriée, juste la définition de la fonction (ne peut pas laisser MatLab gagner)

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

Si le factoriel intégré était autorisé

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

Ce qui précède ne donne pas un entier (qui est l'argument pour une vraie fonction factorielle). Ce qui suit:

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

Ahh toutes ces fonctions intégrées! Je ne pense pas que ce soit battable, sauf d'une manière similaire.
rubik

4
Mathematica est tellement injuste pour les trucs mathématiques! : D
Michael M.

1
du nom lui-même MATHematica
Dadan

Un NumberQtest de configuration est-il requis? Ou parens E^(-t)? Est - il triche à tourner NIntegrateà Integrate? Probablement ... :)
orion

Ça devient un vrai défi;)
mmumboss

6

isé: 72 46 caractères

C'est presque un ajustement parfait ... il existe un "langage" qui semble être destiné précisément au golf mathématique: ised . Sa syntaxe obscurcie en fait un code très court (pas de variables nommées, juste des emplacements de mémoire entiers et beaucoup d'opérateurs polyvalents à caractère unique). Définissant la fonction gamma à l'aide d'une intégrale, je l'ai obtenue avec 80 caractères apparemment aléatoires

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

Ici, le slot mémoire $ 4 est une fonction factorielle, le slot mémoire $ 6 fonction de bissection et le slot mémoire $ 2 devraient être définis pour entrer (donné avant de sourcer ce code). Les emplacements $ 0 et $ 1 sont les limites de la bissection. Exemple d'appel (en supposant que le code ci-dessus se trouve dans le fichier inversefactorial.ised)

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

Bien sûr, vous pouvez utiliser la fonction intégrée! opérateur, auquel cas vous obtenez jusqu'à 45 caractères

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

La précaution de l'opérateur est parfois bizarre.

Modifier: n'oubliez pas d'inclure les fonctions au lieu de les enregistrer. Battez Mathematica avec 72 caractères!

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

Et en utilisant le! intégré vous obtenez 41.


Une mise à jour attendue depuis un an:

Je viens de réaliser que c'était très inefficace. Golfé jusqu'à 60 caractères:

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

Si utf-8 est utilisé (Mathematica le fait aussi), nous arrivons à 57:

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

Une réécriture un peu différente peut la réduire à 46 (ou 27 si vous utilisez la fonction intégrée!):

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

Les deux derniers caractères peuvent être supprimés si vous êtes d'accord pour que la réponse soit imprimée deux fois.


Je serais surpris si je voyais quelqu'un battre ceci: o
Jens Renders

@JensRenders: Je viens de le faire;)
mmumboss

Pour clarifier la discussion sur la précision: elle est définie par 0,1 (étape d'intégration) et 99 (limite d'intégration). La bisection va à la précision de la machine. La limite de bissection @ 1,99 peut être maintenue à 99, sauf si vous voulez saisir des nombres au-dessus (99!).
orion

@mmumboss vous a encore une fois :)
orion

5

MATLAB 54 47

Si je choisis les bons défis, MATLAB est vraiment bien pour le golf :). Dans mon code, je trouve la solution de l'équation (ux!) = 0 dans laquelle u est l'entrée utilisateur et x la variable à résoudre. Cela signifie que u = 6 conduira à x = 3, etc ...

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

La précision peut être modifiée en modifiant la limite supérieure de l'intégrale, qui est fixée à 99. L'abaisser modifiera la précision de la sortie comme suit. Par exemple pour une entrée de 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;

etc.


vous devez spécifier l'option de précision car cela est requis dans les règles! "Il doit contenir un nombre qui peut être rendu arbitraire grand ou petit pour obtenir une précision arbitraire"
Jens Renders

Je ne le vois pas non plus dans les solutions ised et Mathematica? Mais je vais y
réfléchir

1
Je vois le numéro 99 dans la version ised, et la version mathématique est battue de toute façon
Jens Renders

Étant donné la position dans le code, il s'agit probablement de la limite supérieure de l'intégrale. Dans mon code, c'est inf. Alors oui, si je change cet inf en 99, ma réponse devient moins précise, ce qui signifie que ce nombre influence la précision, et donc je respecte les règles. Si je le change en 99
j'enregistre

Mais après avoir changé l'inf à 99, est-ce qu'il répond à la précision requise?
rubik

3

Python - 199 caractères

D'accord, vous aurez donc besoin de beaucoup d'espace de pile et de beaucoup de temps, mais bon, ça y arrivera!

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)

Voici une autre approche avec encore plus de récursivité.

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)

Ces deux >>>f(10,1)paramètres peuvent être testés à condition de définir la limite de récursivité autour de 10000. Plus d'une décimale d'exactitude ne correspondra probablement pas à une limite de récursivité réaliste.

Intégrant les commentaires et quelques modifications, jusqu'à 199 caractères.

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
Il s'agit d'une code-golfquestion, vous devez donc fournir la réponse la plus courte, en indiquant la longueur de votre solution.
VisioN

Une bonne méthode mais le problème est que vous ne pouvez pas garantir que cela trouvera jamais la réponse ... De plus, c'est du codegolf zo que vous pourriez essayer de minimiser l'utilisation des caractères.
Jens Renders

1
Random () de Python utilise un Twister de Mersenne qui, je crois, parcourt l'espace des flotteurs de Python, donc il devrait toujours se terminer s'il y a une réponse dans la tolérance.
intx13

Voulez-vous dire qu'il renvoie chaque valeur flottante avant de répéter une valeur? si tel est le cas, ce code serait valide si vous pouviez surmonter le débordement de la pile
Jens Renders

2
Le code est capable, c'est juste que vous et moi n'avons peut-être pas le temps ni les ressources informatiques pour l'exécuter jusqu'à son terme;)
intx13

3

Python 2.7 - 215 189 caractères

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)

Usage:

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

Pour modifier la précision: passez 1e-5à un nombre plus petit pour une plus grande précision, un nombre plus grand pour une précision moindre. Pour une meilleure précision, vous voudrez probablement donner une meilleure valeur pour e.

Cela implémente simplement la fonction factorielle en tant que f, puis effectue une recherche binaire pour affiner la valeur la plus précise de l'inverse de l'entrée. Suppose que la réponse est inférieure ou égale à 99 (cela ne fonctionnerait pas pour une réponse de 365 à coup sûr, j'obtiens une erreur de débordement mathématique). L'utilisation d'espace et de temps très raisonnable se termine toujours.

Vous pouvez également remplacer if abs(n-f(x))<=10**-5: print x;breakpar print xpour raser 50 caractères . Il bouclera pour toujours, vous donnant une estimation de plus en plus précise. Je ne sais pas si cela correspondrait aux règles.


Je ne connaissais pas ce site pour compter les caractères. J'utilise toujours cat file | wc -c.
rubik

@rubik: oh gentil, je ne pensais pas utiliser ça. ils correspondent tous les deux =)
Claudiu

2

dg - 131 133 octets

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

Puisque dg produit du bytecode CPython, cela devrait aussi compter pour Python, mais oh ... Quelques exemples:

$ 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: Ajout de deux octets parce que je ne me souvenais pas qu'il devrait également accepter les flottants!


Le mien donne 42.8006566063, donc ils correspondent à moins de 5 chiffres de précision!
Claudiu

C'est génial! Je ne sais pas où est la limite supérieure, mais elle devrait se casser quelque part. Car 1e100il donne:, 69.95780520000001car 1e150il sort 96.10586423000002, tandis que pour 1e200il explose. Mais vraiment je ne sais pas si ces résultats sont fiables ...
rubik

1

R , 92 octets

Une fonction, gqui prend en entrée zet sort l'inverse factorielle de ce nombre

Il y a presque certainement plus à jouer à cela, donc si vous voyez quelque chose que je peux améliorer, faites-le moi savoir.

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)

Essayez-le en ligne!

Non golfé et commenté

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

Essayez-le en ligne!


0

Javascript (sans utiliser de boucles!)

Pour ce faire, j'ai utilisé une approximation numérique bien connue de l'inverse de l'approximation factorielle de Stirling , (et j'ai également été inspiré par ce ..cough .. toux .. code de quelqu'un d'autre ...)

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)))))))))
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.