Com o recente ataque do Python , aqui está uma tentativa de mostrar os pontos fortes do Python. Seu desafio é escrever um programa que calcule o fatorial do número mais alto possível em 10 segundos.n
Sua pontuação será (highest n for your program on your machine)/(highest n for my program on your machine)
Regras
- Você deve calcular uma solução inteira exata. Como o fatorial seria muito maior do que o que pode caber em um número inteiro não assinado de 64 bits, você pode usar cadeias de caracteres se o seu idioma não suportar números inteiros grandes
- As brechas padrão são proibidas. Particularmente, você não pode usar nenhum recurso externo.
- Somente a parte do cálculo (isso inclui tempo para quaisquer soluções alternativas usando seqüências de caracteres) aumenta o tempo total, que deve ser inferior a 10 segundos, em média.
- Apenas programas de thread único.
- Você deve armazenar a saída em um formato facilmente imprimível (já que a impressão leva tempo) (veja meu programa abaixo), sequência, variável, matriz de caracteres etc.
EDITAR:
- Seu programa deve fornecer a saída correta para todos
n
:1 <= n <= (your highest n)
EDIT2:
- Detesto dizer isso explicitamente, mas o uso das funções fatoriais internas do seu idioma se enquadra nas brechas padrão http://meta.codegolf.stackexchange.com/a/1078/8766 Desculpe Mathematica e Sage
Meu programa
from __future__ import print_function
import time
def factorial( n ):
return reduce( ( lambda x , y : x * y ) , xrange( 1 , n + 1 ) , 1 )
start = time.clock()
answer = factorial( 90000 )
end = time.clock()
print ( answer )
print ( "Time:" , end - start , "sec" )
Maior pontuação ganha. Para o registro, meu código pode gerenciar n = 90000
em cerca de 9.89
segundos em um Pentium 4 3.0 GHz
EDIT: todos podem adicionar a pontuação, em vez de apenas o n mais alto . Apenas o mais alto n
não tem significado por si só, pois depende do seu hardware. É impossível ter um critério de vitória objetivo de outra forma. A resposta de ali0sha faz isso corretamente.
Nós temos um vencedor. Não aceitei a resposta java https://codegolf.stackexchange.com/a/26974/8766 , pois meio que saia perto de http://meta.codegolf.stackexchange.com/a/1080/8766
fonte
operator.mul
em vez da função lambdafactorial(Inf)
:, retornaInf
em uma fração de segundo.Respostas:
C ++ com GMP, pontuação = 55,55 (10.000.000 / 180.000)
fonte
Python 2.7
42.575 = (6.812.000 / 160.000) aprox.
Código:
Teste:
Resultado:
Como funciona:
Multiplicações maiores levam mais tempo, portanto, queremos fazer o menor número possível de multiplicações. Isto é especialmente verdade no Python, onde para números menores que
2^64
usamos aritmética de hardware e, acima disso, usamos software. Então,m(L)
começamos com uma listaL
; se o comprimento for ímpar, removemos um número da consideração para torná-lo igual novamente. Então multiplicamos elemento1
com elemento-2
, elemento3
com-4
, etc, para queEssa abordagem garante que estamos usando a aritmética de hardware pelo maior tempo possível, após o qual passamos para a eficiente biblioteca aritmética gmc.
Além disso
fac2
, adotamos uma abordagem mais clássica de dividir e conquistar, onde dividimos cada múltiplo de 2 e os trocamos de bits no final para um aumento de desempenho trivial. Eu o incluí aqui porque geralmente é cerca de 0,5% mais rápido quefac1
.Versão Golfed de
fac1
(porque eu posso), 220Bfonte
gmpy2
? $ python Python 2.7.3 (padrão, 27 de fevereiro de 2014, 19:58:35) [GCC 4.6.3] no linux2 Digite "help", "copyright", "credits" ou "license" para obter mais informações. >>> do gmpy2 import mpz Traceback (última chamada mais recente): Arquivo "<stdin>", linha 1, em <module> ImportError: Nenhum módulo chamado gmpy2 >>>while len(L): ...
vez dewhile len(L)>1: ...
?Java - 125.15 (21.400.000 / 171.000)
Também copiado descaradamente do repositório Github de Peter Luschny (obrigado @ semi-extrínseco) e licenciado sob a licença MIT, ele usa o algoritmo "fatoração primordial aninhada ao quadrado", conforme proposto por Albert Schönhage et al. (de acordo com a página de descrição dos algoritmos fatoriais de Luschny ).
Adaptei levemente o algoritmo para usar o BigInteger do Java e não usar uma tabela de pesquisa para n <20.
Compilado com o gcj, que usa o GMP para sua implementação do BigInteger, e rodou no Linux 3.12.4 (Gentoo), em um Core i7 4700MQ a 2.40GHz
fonte
gcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
Python 3, n = 100000
Uma simples alteração no algoritmo era tudo o que era necessário para aumentar o código de amostra em 10000.
Obviamente, não é a resposta mais criativa, mas há realmente apenas uma maneira de fazer um fatorial ...
fonte
Perl + C, n = cerca de 3 milhões
Aqui estou usando a biblioteca Math :: BigInt :: GMP disponível no CPAN, que fornece um enorme aumento de velocidade para os principais objetos Math :: BigInt do Perl.
Lembre-se de que meu computador provavelmente é um pouco mais lento que o seu. Usando seu script Python original, só posso calcular
factorial(40000)
em 10 segundos;factorial(90000)
leva muito mais tempo. (Apertei Ctrl + C depois de um minuto.) No seu hardware, usando Math :: BigInt :: GMP, você poderá calcular o fatorial de 5 milhões ou mais em menos de 10 segundos.Uma coisa que você pode notar é que, embora o fatorial seja calculado incrivelmente rápido, a impressão do resultado é muito lenta, demorando cerca de três vezes mais que o cálculo original. Isso ocorre porque o GMP usa internamente uma representação binária em vez de decimal e a impressão exige conversão binária em decimal.
fonte
Math::BigInt
Python 2.7
5.94 = 1'200'000 / 202'000
Faz uso da relativa facilidade de multiplicação de muitos grupos de pequenos números e, em seguida, multiplicando-os em comparação com o grande número de multiplicações envolvendo um número enorme.
fonte
C #: 0,48 (77.000 / 160.000)
Eu não estou feliz com isso.
C # é tão lento?
Mas aqui está a minha entrada de qualquer maneira.
Quando n = 77000, é preciso
00:00:09:8708952
calcular.Estou executando no modo Release, fora do Visual Studio, usando um Core i3-2330M @ 2.2GHz.
Edit: Como não estou fazendo nada inteligente, aceito esse resultado. Talvez o .NET Framework 4.5 esteja sobrecarregado (ou o BigInteger não é tão rápido).
fonte
n
zero approached by
operador para torná-la mais bonita (como começar comn = ... + 1
, em seguida, fazerwhile (0 <-- n) result *= n;
)bc, pontuação = 0,19
Que diabos, aqui está o meu candidato para "Quanto você pode multiplicar lentamente?"
bc é "Uma linguagem de calculadora de precisão arbitrária", mas infelizmente bastante lenta:
Em cerca de 10 segundos no meu MacBook Pro de meados de 2012 (Intel Core i7 de 2,3 GHz), a resposta python de referência pode calcular 122000 !, mas esse script bc pode calcular apenas 23600 !.
Por outro lado, 10000! leva 1,5s com o script de referência python, mas o script bc leva 50s.
Oh céus.
fonte
read()
, então eu corritime sed 's/read()/28000/' factorial.bc | bc
.Bash: score = 0.001206 (181/150000)
Eu roubei as funções matemáticas do Rosettacode - Longa multiplicação que não analisei nem tentei otimizar.
Você pode alterar o algoritmo ou tentar um método de divisão de strings diferente.
fonte
Python 3, algo avançado de Peter Luschny: 8,25x (1 280 000/155 000)
Copiado descaradamente de Peter Luschny,
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm ,
que fornece esse código sob a licença "Creative Commons Attribution-ShareAlike 3.0".
Este é realmente um algoritmo bastante avançado, usando algo chamado "fatorial oscilante" e uma lista de números primos. Eu suspeito que poderia ser ainda mais rápido se gostasse de muitas das outras respostas e realizasse a maioria das multiplicações com números inteiros de 32 bits.
fonte
Java - 10.9
n = 885000
Mergesort-y.
BigInteger
s são lentos.Recomendações para bibliotecas inteiras Java de alta velocidade e precisão arbitrária? : P
fonte
C ++ (específico para x86_64) - 3.0 (390000/130000)
(facilmente transportável para x86-32, transportar para outras arquiteturas implica uma perda significativa de velocidade)
Aqui está minha própria micro-implementação de aritmética longa.
O cálculo em si leva 10 segundos e, embora a saída esteja na forma facilmente imprimível (consulte a
operator<<
sobrecarga), leva mais tempo para imprimi-lo.fonte
g++ -O2 factorial.cc -o factorial
e ela marca 3.90 = 382000 / 98000.r
aumenta. Nesse caso, e você pode fazer um aumentor
em 10 segundos, sua pontuação diminui.Python 3: 280000/168000
Tempo executando seu programa: entre
9.87585953253
e10.3046453994
. Tempo executando meu programa: about10.35296977897559
.Li esta resposta no cs.SE e decidi tentar implementá-la em Python. No entanto, descobri acidentalmente isso
n! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!
(nota:!!
é o fatorial duplo ). Então eu converti isso para uma forma não recursiva.Os comentários mostram minha tentativa de evitar recalcular o fatorial duplo, mas descobri que armazenar todos os valores custava muito memória e fazia com que meu computador funcionasse ainda mais devagar. Eu posso melhorar isso armazenando apenas o necessário.
Estranhamente, implementei a multiplicação direta ingênua no Python 3, e é melhor que o seu programa:
n = 169000
em 10 segundos .:fonte
Ruby 2.1
pontuação = 1,80 = 176_000 / 98_000
EDIT: aprimorado de
1,35 = 132_000 / 98_000Peguei idéias do algoritmo fatorial GMP . Este programa usa a biblioteca padrão para gerar números primos. Ruby é uma má escolha porque a multiplicação parece mais lenta no Ruby do que no Python.
Sim, meu binário de
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-openbsd]
links contra o GMP. O Ruby 2.1 adicionou um recurso para usar o GMP para multiplicação grande, mas ainda parece mais lento que o Python 2.7.fonte
Julia - Pontuação = 15.194
Utilizando exatamente a mesma abordagem que a do programa de referência ... ou seja,
Portanto, ele usa reduzir, a operação básica de multiplicação binária e um intervalo (nesse caso, usar big (n) para forçar o cálculo a ser feito no BigInt em vez de Int64). A partir disso, eu recebo
No meu computador, com o programa de referência em execução com a entrada 154000, recebo a
Time: 10.041181 sec
saída (execute usandopython ./test.py
, em que test.py é o nome do arquivo que contém o código de referência)fonte
tcl, 13757
Minha resposta é verificar os limites de tcl.
A primeira linha é apenas para definir um parâmetro de entrada:
Os outros são o próprio algoritmo:
Testei meu código em http://rextester.com/live/WEL36956 ; Se eu aumentar, recebo um SIGKILL; may n pode ficar maior em um intérprete tcl local, o que eu não tenho.
fonte