Calcular o inverso do fatorial

30

Escreva o código mais curto que aceitará qualquer número real maior que 1 como entrada e produzirá seu fatorial inverso positivo. Em outras palavras, responde à pergunta "que número fatorial é igual a esse número?". Use a função Gamma para estender a definição de fatorial para qualquer número real, conforme descrito aqui .

Por exemplo:

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

porque 3! = 6e3.390077654! = 10

Regras

  • É proibido o uso de funções fatoriais ou funções gama, ou funções que dependam dessas funções.
  • O programa deve ser capaz de calculá-lo com 5 dígitos decimais, com a capacidade teórica de calculá-lo com qualquer precisão (deve conter um número que possa ser arbitrário grande ou pequeno para obter precisão arbitrária)
  • Qualquer idioma é permitido, o código mais curto em caracteres vence.

Eu fiz um exemplo de trabalho aqui . Dar uma olhada.

Jens Renders
fonte
2
Isso poderia usar mais alguns casos de teste, em particular para cobrir entradas nulas e negativas.
Peter Taylor
Editei que a entrada deveria ser maior que 1 porque, caso contrário, poderia haver várias respostas.
Jens Renders
1
Pode haver várias respostas de qualquer maneira, a menos que você também adicionar um requisito que a saída deve ser maior que 1.
Peter Taylor
Seu exemplo de trabalho fornece 3,99999 quando inserido 24. Então, essa solução é aceitável?
rubik
sim, porque isso pode ser visto como 4, a 5 casas decimais corrigir
Jens Renders

Respostas:

13

Javascript (116)

Magias negras aqui! Dá um resultado em alguns milissegundos .
Somente funções matemáticas elementares usados: 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)

Pena que o LaTeX não é suportado no codegolf, mas basicamente codifiquei um solucionador de newton para f(y)=gamma(y)-n=0e x=y-1(porque x!é gamma(x+1)) e aproximações para as funções gama e digamma.

A aproximação gama é aproximação Stirling Aproximação
digamma use a fórmula de Euler Maclaurin
A função digamma é a derivada da função gama dividida pela função gama: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);

Casos de teste :

10 => 3.390062988090518
120 => 4.99999939151027
720 => 6.00000187248195
40320 => 8.000003557030217
3628800 => 10.000003941731514
Michael M.
fonte
Very nice resposta retomando embora não atende a precisão necessária e só funciona para números inferiores a 706
Jens Renders
@JensRenders, bem, adicionei algumas iterações do newton solver, alterei o palpite inicial e uma melhor aproximação para a função gama. Isso deve se encaixar nas regras agora. Deixa-me se é ok :)
Michael M.
Sim, agora ele é perfeito, votei-lo :)
Jens Renders
1
Você pode salvar 1 caracter:n=prompt(M=Math)
Florent
Tente executar seu código em um número tão grande quanto $ 10 ^ {10 ^ 6} $, e garantir que você obtenha um resultado inteiro
David G. Stork
13

Mathematica - 74 54 49.

A maneira correta será

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

Se simplesmente abandonássemos o teste, ?NumberQele ainda funcionaria, mas lançaria alguns avisos desagradáveis, que desapareceriam se mudarmos para a integração simbólica Integrate, mas isso seria ilegal (suponho), porque a função seria automaticamente convertida em Gammafunção. Também podemos nos livrar da função externa dessa maneira.

De qualquer forma

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

Para verificar com a entrada correta, basta definir a função (não é possível deixar o MatLab vencer)

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

Se fatorial embutido fosse permitido

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

O exposto acima não fornece um número inteiro (que é o argumento para uma verdadeira função fatorial). O seguinte faz:

Floor[InverseFunction[Gamma][n]-1]
swish
fonte
Ah, todas essas funções embutidas! Eu não acho que isso seja superável, exceto de maneira semelhante.
rubik
4
O Mathematica é tão injusto para coisas de matemática! : D
Michael M.
1
do nome próprio MATHematica
Dadan
É NumberQnecessário teste padrão? Ou parens em E^(-t)? É batota para ligar NIntegratepara Integrate? Provavelmente ... :)
orion
Ele está se transformando em um desafio real;)
mmumboss
6

ised: 72 46 caracteres

Isso é quase um ajuste perfeito ... existe uma "linguagem" por aí que parece ser feita exatamente para o golfe matemático: ised . Sua sintaxe ofuscada cria um código muito curto (sem variáveis ​​nomeadas, apenas slots de memória inteiros e muitos operadores versáteis de char único). Definindo a função gama usando uma integral, consegui 80 caracteres aparentemente aleatórios

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

Aqui, o slot de memória $ 4 é uma função fatorial, espera-se que a função de bissecção do slot de memória $ 6 e o ​​slot de memória $ 2 sejam configurados como entrada (fornecido antes de fornecer este código). Os slots $ 0 e $ 1 são os limites da bissecção. Exemplo de chamada (supondo que o código acima esteja no arquivo inversefactorial.ised)

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

Claro, você pode usar o builtin! operador, nesse caso você tem até 45 caracteres

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

Cuidado, a precedência do operador às vezes é estranha.

Editar: lembrou-se de incorporar as funções em vez de salvá-las. Bata o Mathematica com 72 caracteres!

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

E usando o! incorporado você obtém 41.


Atualização com atraso de um ano:

Acabei de perceber que isso era altamente ineficiente. Golfe até 60 caracteres:

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

Se utf-8 for usado (o Mathematica também), chegamos a 57:

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

Uma reescrita um pouco diferente pode reduzir para 46 (ou 27, se estiver usando o builtin!):

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

Os dois últimos caracteres podem ser removidos se você estiver de acordo com a resposta impressa duas vezes.

orion
fonte
Eu ficaria surpreso se eu iria ver alguém bater este: o
Jens Renders
@JensRenders: Acabei de fazer;) #
mmumboss
Para esclarecer a discussão sobre precisão: é definido por .1 (etapa de integração) e 99 (limite de integração). A bissecção vai para a precisão da máquina. O limite de bissecção @ 1,99 pode ser mantido em 99, a menos que você queira inserir números acima (99!).
orion
@mmumboss te peguei novamente :)
orion
5

MATLAB 54 47

Se eu escolher os desafios certos, o MATLAB é muito bom para jogar golfe :). No meu código, encontro a solução para a equação (ux!) = 0 na qual u é a entrada do usuário ex x a variável a ser resolvida. Isso significa que u = 6 levará a x = 3, etc ...

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

A precisão pode ser alterada alterando o limite superior da integral, que é definido como 99. Reduzir isso alterará a precisão da saída da seguinte maneira. Por exemplo, para uma entrada 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.

mmumboss
fonte
você deve especificar a opção de precisão, pois isso é necessário nas regras! "Ele deve conter um número que pode ser grande ou pequeno arbitrário para obter precisão arbitrária"
Jens renderiza
Também não vejo isso nas soluções ised e Mathematica? Mas eu vou olhar para ele ..
mmumboss
1
Eu vejo o número 99 na versão ised, e a versão mathematica é batida de qualquer maneira
Jens processa 6/14
Dada a posição no código, esse é provavelmente o limite superior da integral. No meu código, isso é inf. Então, sim, se eu alterar esse inf para 99, minha resposta se tornará menos precisa, o que significa que esse número influencia a precisão e, portanto, cumpro as regras. Se eu alterá-lo para 99, eu até salvo um caractere;)
mmumboss
Mas, depois de alterar inf para 99, ele atende à precisão exigida?
Rubik
3

Python - 199 caracteres

Ok, então você precisará de muito espaço na pilha e muito tempo, mas, ei, chegará lá!

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)

Aqui está outra abordagem com ainda mais recursão.

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)

Ambos podem ser testados >>>f(10,1)desde que você defina o limite de recursão em torno de 10000. Mais de uma casa decimal de precisão provavelmente não será concluída com nenhum limite de recursão realista.

Incorporando os comentários e algumas modificações, até 199 caracteres.

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
fonte
2
Como é uma code-golfpergunta, é necessário fornecer a resposta mais curta, informando a duração da sua solução.
VisioN
Um método interessante, mas o problema é que você não pode garantir que isso ache a resposta ... Além disso, este é o codegolf zo, você pode tentar minimizar o uso de caracteres.
Jens Renders
1
O random () do Python usa um Mersenne Twister que, acredito, percorre o espaço dos carros alegóricos do Python, portanto deve sempre terminar se houver uma resposta dentro da tolerância.
intx13
Você quer dizer que ele retorna todos os valores flutuantes antes de repetir um? se esse é o caso do que este código seria válido se você onde capaz de superar o estouro de pilha
Jens Renders
2
O código é capaz, é só que você e eu não pode ter o tempo nem os recursos do computador para executá-lo até a conclusão;)
intx13
3

Python 2.7 - 215 189 caracteres

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

Para alterar a precisão: altere 1e-5para um número menor para maior precisão, número maior para pior precisão. Para uma melhor precisão, você provavelmente deseja dar um valor melhor e.

Isso apenas implementa a função fatorial como fe, em seguida, faz uma pesquisa binária para aprimorar o valor mais preciso do inverso da entrada. Supõe que a resposta seja menor ou igual a 99 (com certeza não funcionaria para uma resposta do 365, recebo um erro de estouro de matemática). O uso muito razoável de espaço e tempo, sempre termina.

Como alternativa, substitua if abs(n-f(x))<=10**-5: print x;breakpor print xpara cortar 50 caracteres . Ele funcionará para sempre, oferecendo uma estimativa cada vez mais precisa. Não tenho certeza se isso se encaixaria com as regras.

Claudiu
fonte
Eu não conhecia esse site para contar caracteres. Eu sempre uso cat file | wc -c.
rubik
@ Rubik: Oh legal, não pensei em usar isso. ambos correspondem =)
Claudiu
2

dg - 131 133 bytes

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

Como o dg produz o bytecode CPython, isso também deve contar para o Python, mas ... Alguns exemplos:

$ 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

Edição: Adicionado dois bytes, porque eu não lembrava que ele deveria aceitar carros alegóricos também!

rubik
fonte
O meu dá 42.8006566063, então eles correspondem a 5 dígitos de precisão!
Claudiu
Isso é ótimo! Não sei onde fica o limite superior, mas deve quebrar em algum lugar. Para 1e100ele dá: 69.95780520000001para 1e150ele produz 96.10586423000002, enquanto que para 1e200ele explode. Mas realmente eu não sei se esses resultados são confiáveis ...
Rubik
1

R , 92 bytes

Uma função, gque recebe entrada ze gera o fatorial inverso desse número

É quase certo que ainda há muito a ser jogado fora disso, então se você vir algo que eu possa melhorar, entre em contato.

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)

Experimente online!

Sem Golfe e Comentado

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

Experimente online!

Taylor Scott
fonte
0

Javascript (sem usar loops!)

Para fazer isso, usei uma aproximação numérica bem conhecida do inverso da aproximação do fator de Stirling (e também me inspirei nesse código de ..cough .. tosse .. de outra pessoa ...)

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)))))))))
    }
}
Antonio Ragagnin
fonte