Quanto você pode multiplicar rapidamente?

12

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:


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 = 90000em cerca de 9.89segundos 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 nnã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

user80551
fonte
1
Você pode usar operator.mulem vez da função lambda
gnibbler
1
Bit surpreendeu isso, mas supondo que eu li as regras corretamente, essa solução MATLAB seria legal factorial(Inf):, retorna Infem uma fração de segundo.
Dennis Jaheruddin
1
@ Doorknob Isso se encaixa nas brechas padrão.
6307 Justin
1
@DennisJaheruddin, é um pouco exagerado se referir a "Inf" como uma "solução inteira exata".
tobyink
1
@Quincunx Não, qualquer idioma é permitido.
User80551

Respostas:

7

C ++ com GMP, pontuação = 55,55 (10.000.000 / 180.000)

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <queue>
#include <gmpxx.h>

int main(int argc, char *argv[]) {
  uint64_t n = atoi(argv[1]);

  // Iterate through 1..n.  Strip off powers of 2.  Multiply
  // remainders together into <= 64 bit chunks.
  uint64_t twos = 0;
  std::vector<uint64_t> terms;
  uint64_t m = 1;
  for(uint64_t i = 1; i <= n; i++) {
    uint64_t j = __builtin_ctzll(i);
    twos += j;
    uint64_t k = i >> j;
    if(__builtin_clzll(m) + __builtin_clzll(k) >= 64) {
      m *= k;
    } else {
      terms.push_back(m);
      m = k;
    }
  }
  if(m != 1) terms.push_back(m);

  // convert to gmp
  // why isn't there a 64-bit constructor?
  std::queue<mpz_class> gmpterms;
  for(int i = 0; i < terms.size(); i++) {
    mpz_class x = (uint32_t)(terms[i] >> 32);
    x <<= 32;
    x += (uint32_t)terms[i];
    gmpterms.push(x);
  }

  // pop two from the bottom, multiply them, push on the end.
  while(gmpterms.size() > 1) {
    mpz_class a = gmpterms.front();
    gmpterms.pop();
    mpz_class b = gmpterms.front();
    gmpterms.pop();
    gmpterms.push(a * b);
  }

  mpz_class r = gmpterms.front();
  r <<= twos;
  //std::cout << r << std::endl;
}
Keith Randall
fonte
8

Python 2.7

42.575 = (6.812.000 / 160.000) aprox.


Código:

import gmpy2

def fac1(n):
    m=lambda(L):([]if len(L)%2==0 else[L.pop()])+map(lambda(l):l[0]*l[1],zip(L[1::2],L[-2::-2]))
    L=map(gmpy2.mpz,xrange(1,n+1))
    Number = (len(L)-1).bit_length()
    while Number:Number-=1;L=m(L)
    return L[0]

def fac2(n):
    global E; E=0
    def f(i):
        global E; E+=i//2
        return[]if i==1 else f(i//2)+range(3,i,2)+[[1,i][i%2]]
    m=lambda(L):([]if len(L)%2==0 else[L.pop()])+map(lambda(l):l[0]*l[1],zip(L[1::2],L[-2::-2]))
    L=map(gmpy2.mpz,f(n))
    N=(len(L)-1).bit_length()
    while N: N-=1;L=m(L)
    return L[0]<<E

Teste:

import time

start = time.time()
baseline(160000)
print time.time()-start

start = time.time()
fac1(6811000)
print time.time()-start

start = time.time()
fac2(6812000)
print time.time()-start

start = time.time()
gmpy2.fac(26000000)
print time.time()-start

Resultado:

10.0069999695
10.0729999542
10.0360000134
9.98699998856

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^64usamos aritmética de hardware e, acima disso, usamos software. Então, m(L)começamos com uma lista L; se o comprimento for ímpar, removemos um número da consideração para torná-lo igual novamente. Então multiplicamos elemento 1com elemento -2, elemento 3com -4, etc, para que

m([1,2,3,4,5,6,7,8]) = [2*7, 4*5, 6*3, 8*1] = [14, 20, 18, 8]
m([10,12,6]) = [360,112]
m([120,6]) = [40320]

Essa 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 que fac1.

Versão Golfed de fac1(porque eu posso), 220B

import gmpy2
def f(n):
    m=lambda(L):([]if len(L)%2==0 else[L.pop()])+map(lambda(l):l[0]*l[1],zip(L[1::2],L[-2::-2]))
    L=map(gmpy2.mpz,xrange(1,n+1));N=(len(L)-1).bit_length()
    while N:N-=1;L=m(L)
return L[0]
alexander-brett
fonte
1
Se o back-end do GMP incluir uma função de deslocamento de bits, você poderá manter os números ainda menores dividindo cada número na lista por 2 até que esteja nivelado e, em seguida, fazendo um único turno no final.
Peter Taylor
De onde você veio 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 >>>
user80551
@ user80551: code.google.com/p/gmpy (o principal resultado de pesquisa do Google) possui instaladores para muitas plataformas diferentes.
Alexander-brett
Para a versão de golfe, você não poderia fazer em while len(L): ...vez de while len(L)>1: ...?
User80551
Não: a função dentro desse loop nunca terá a lista abaixo do comprimento 1 e, de qualquer forma, precisamos do primeiro elemento!
Alexander-brett
2

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

import java.math.BigInteger;

public class PrimeSieveFactorialSchoenhage {

    private static int[] primeList, multiList;

    public static BigInteger factorial(int n) {
        int log2n = 31 - Integer.numberOfLeadingZeros(n);
        int piN = log2n < 2 ? 1 : 2 + (15 * n) / (8 * (log2n - 1));

        primeList = new int[piN];
        multiList = new int[piN];

        int len = primeFactors(n);
        return nestedSquare(len).shiftLeft(n - Integer.bitCount(n));
    }

    private static BigInteger nestedSquare(int len) {
        if (len == 0) {
            return BigInteger.ONE;
        }

        int i = 0, mult = multiList[0];

        while (mult > 1) {
            if ((mult & 1) == 1) { // is mult odd ?
                primeList[len++] = primeList[i];
            }

            multiList[i++] = mult / 2;
            mult = multiList[i];
        }
        BigInteger ns = nestedSquare(i);
        if (len <= i) {
            return ns.multiply(ns);
        }

        return product(primeList, i, len - i).multiply(ns.multiply(ns));
    }

    private static BigInteger product(int[] a, int start, int length) {
        if (length == 0) {
            return BigInteger.ONE;
        }

        int len = (length + 1) / 2;
        long[] b = new long[len];

        int i, j, k;

        for (k = 0, i = start, j = start + length - 1; i < j; i++, k++, j--) {
            b[k] = a[i] * (long) a[j];
        }

        if (i == j) {
            b[k++] = a[j];
        }

        return recProduct(b, 0, k - 1);
    }

    private static BigInteger recProduct(long[] s, int n, int m) {
        if (n > m) {
            return BigInteger.ONE;
        }
        if (n == m) {
            return BigInteger.valueOf(s[n]);
        }
        int k = (n + m) >> 1;
        return recProduct(s, n, k).multiply(recProduct(s, k + 1, m));
    }

    private static int primeFactors(int n) {
        int[] primes = new int[n < 17 ? 6 : (int) Math.floor(n / (Math.log(n) - 1.5))];
        int numPrimes = makePrimeList(n, primes);

        int maxBound = n / 2, count = 0;

        int start = indexOf(primes, 2, 0, numPrimes - 1);
        int end = indexOf(primes, n, start, numPrimes);

        for (int i = start; i < end; i++) {
            int prime = primes[i];
            int m = prime > maxBound ? 1 : 0;

            if (prime <= maxBound) {
                int q = n;
                while (q >= prime) {
                    m += q /= prime;
                }
            }

            primeList[count] = prime;
            multiList[count++] = m;
        }
        return count;
    }

    private static int indexOf(final int[] data, int value, int low, int high) {
        while (low < high) {
            int mid = (low + high) >>> 1;

            if (data[mid] < value) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }

        if (low >= data.length) {
            return low;
        }

        if (data[low] == value) {
            low++;
        }

        return low;
    }

    private static int makePrimeList(int n, int[] prime) {
        boolean[] composite = new boolean[n / 3];

        sieveOfEratosthenes(composite);

        boolean toggle = false;
        int p = 5, i = 0, j = 2;

        prime[0] = 2;
        prime[1] = 3;

        while (p <= n) {
            if (!composite[i++]) {
                prime[j++] = p;
            }
            // -- never mind, it's ok.
            p += (toggle = !toggle) ? 2 : 4;
        }

        return j; // number of primes
    }

    private static void sieveOfEratosthenes(final boolean[] composite) {
        int d1 = 8;
        int d2 = 8;
        int p1 = 3;
        int p2 = 7;
        int s1 = 7;
        int s2 = 3;
        int n = 0;
        int len = composite.length;
        boolean toggle = false;

        while (s1 < len) { // -- scan sieve
            if (!composite[n++]) { // -- if a prime is found, cancel its multiples
                int inc = p1 + p2;

                for (int k = s1; k < len; k += inc) {
                    composite[k] = true;
                }

                for (int k = s1 + s2; k < len; k += inc) {
                    composite[k] = true;
                }
            }

            if (toggle = !toggle) { // Never mind, it's ok.
                s1 += d2;
                d1 += 16;
                p1 += 2;
                p2 += 2;
                s2 = p2;
            } else {
                s1 += d1;
                d2 += 8;
                p1 += 2;
                p2 += 6;
                s2 = p1;
            }
        }
    }

    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        long nanos = System.nanoTime();
        BigInteger fact = factorial(n);
        nanos = System.nanoTime() - nanos;
        // Commented out because it takes ages to print
        //System.out.println(fact);
        System.out.println(nanos / 1e9);
    }
}
14mRh4X0r
fonte
Compilado comgcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r
1

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.

import time

def factorial(n):
    result = 1
    while n > 0:
        result *= n
        n = n - 1
    return result

start = time.clock()
answer = factorial(100000)
end = time.clock()

print(answer)
print("Time:", end - start, "sec")

Obviamente, não é a resposta mais criativa, mas há realmente apenas uma maneira de fazer um fatorial ...

Maçaneta da porta
fonte
Por favor, dê a pontuação, veja minha edição. A colisão provavelmente seria porque a máquina é melhor do que o meu.
User80551
1

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.

use v5.14;
use Time::HiRes 'time';
use Math::BigInt only => 'GMP';

sub factorial { Math::BigInt::->new(@_)->bfac }

my $start  = time;
my $answer = factorial( 3_000_000 );
my $end    = time;

say $answer;
say "Time: ", $end - $start, " sec";

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.

tobyink
fonte
1
Eu acho que o GMP conta como um recurso externo. (Embora ele certamente torna as coisas um inferno de muito mais fácil do que implementar primeiro-factorização e Schönhage-Strassen multiplicação do zero.)
r3mainer
3
Eu estava assumindo que "recurso externo" a que se refere à procura de soluções a partir de um conjunto pré-calculado de respostas em um banco de dados ou um serviço web, etc.
tobyink
Squeamish: as bibliotecas normalmente não contam como recursos externos, a menos que tenham uma função que se enquadre na regra de brechas chatas.
Alexander-brett
1
Tobyink: você pode explicar o que o seu programa faz? parece que você está apenas usando um built-in função (bfac?)
alexander-Brett
Sim. Esta resposta é inválida, pois usa o método fatorial deMath::BigInt
14mRh4X0r
1

Python 2.7
5.94 = 1'200'000 / 202'000

def fast_fac(n):
    def prod(start, fin):
            if fin - start <= 50:
                    return reduce(lambda x,y: x*y, xrange(start, fin+1), 1)
            else:
                    mid = (start+fin) / 2
                    return prod(start, mid) * prod(mid+1, fin)
    return prod(1, n)

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.

Cthulhu
fonte
1

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.

static void Main(string[] args)
    {
        Console.WriteLine("Enter N for fatorial:");
        int n = Convert.ToInt32(Console.ReadLine());

        Stopwatch s = Stopwatch.StartNew();


        BigInteger result = 1;
        while (0 <-- n) result *= n;

        s.Stop();

        Console.WriteLine("Output: {0} ", result);

        Console.WriteLine("Completed in {0}", s.Elapsed);

    }

Quando n = 77000, é preciso 00:00:09:8708952calcular.

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

Ricardo Pieper
fonte
Por favor, indique a pontuação e não apenasn
user80551
1
Você poderia usar zero approached byoperador para torná-la mais bonita (como começar com n = ... + 1, em seguida, fazer while (0 <-- n) result *= n;)
Cthulhu
1
O BigInteger for .NET provavelmente não implementou algoritmos para multiplicar números maiores, como Karatsuba ou Toom-3. Nesse caso, este é um bom exemplo de como o Python é mais rápido.
kernigh
1

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:

n=read()
for(f=i=1;i<=n;i++)f*=i
f
quit

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.

Trauma Digital
fonte
1
O OpenBSD bc (1) é mais rápido. O seu programa marca 0,29 = 28000/98000. Não há read(), então eu corri time sed 's/read()/28000/' factorial.bc | bc.
kernigh
1

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.

#!/bin/bash


add() { # arbitrary-precision addition
  if (( ${#1} < ${#2} )); then
    local a="$2" b="$1" sum= carry=0
  else
    local a="$1" b="$2" sum= carry=0
  fi

  while (( ${#a} )); do
    local -i d1="${a##${a%?}}" d2="10#0${b##${b%?}}" s=carry+d1+d2
    sum="${s##${s%?}}$sum"
    carry="10#0${s%?}"
    a="${a%?}" b="${b%?}"
  done
  echo "$sum"
}

multiply() { # arbitrary-precision multiplication
  if (( ${#1} < ${#2} )); then
    local a="$2" b="$1" product=0
  else
    local a="$1" b="$2" product=0
  fi

  local zeroes=
  while (( ${#b} )); do
    local m1="$a"
    local m2="${b##${b%?}}"
    local partial=$zeroes 
    local -i carry=0
    while (( ${#m1} )); do 
      local -i d="${m1##${m1%?}}"
      m1="${m1%?}"
      local -i p=d*m2+carry
      partial="${p##${p%?}}$partial"
      carry="10#0${p%?}"
    done
    partial="${carry#0}$partial"
    product="$(add "$product" "$partial")"
    zeroes=0$zeroes
    b="${b%?}"
  done
  echo "$product"
}

# 'timerun' function
trap 'echo $((i -1)) $f; exit'  USR1  
(sleep 9.9; kill -USR1 $$)&

declare -i i 
f=1
for ((i=1; i< 10000 ; i++ ))   # 10000 is verry optimistic
do
    f=$(multiply $f $i)
done 
Emmanuel
fonte
1
Por favor, adicione a pontuação e não apenas o maior n
user80551
@ user80551 está pronto
Emmanuel
1

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.

#! /usr/bin/python3
import time
import bisect 

def Primes(n) : 
  primes = [2, 3] 
  lim, tog = n // 3, False 
  composite = [False for i in range(lim)] 

  d1 = 8; d2 = 8; p1 = 3; p2 = 7; s = 7; s2 = 3; m = -1 

  while s < lim :             # --  scan the sieve 
      m += 1                  # --  if a prime is found 
      if not composite[m] :   # --  cancel its multiples 
          inc = p1 + p2 
          for k in range(s,      lim, inc) : composite[k] = True 
          for k in range(s + s2, lim, inc) : composite[k] = True 

          tog = not tog 
          if tog: s += d2; d1 += 16; p1 += 2; p2 += 2; s2 = p2 
          else:   s += d1; d2 +=  8; p1 += 2; p2 += 6; s2 = p1 

  k, p, tog = 0, 5, False 
  while p <= n : 
      if not composite[k] : primes.append(p) 
      k += 1; 
      tog = not tog 
      p += 2 if tog else 4 

  return primes 

def isqrt(x): 
  ''' 
  Writing your own square root function
  ''' 
  if x < 0: raise ValueError('square root not defined for negative numbers') 
  n = int(x) 
  if n == 0: return 0 
  a, b = divmod(n.bit_length(), 2) 
  x = 2**(a + b) 
  while True: 
      y = (x + n // x) // 2 
      if y >= x: return x 
      x = y 

def product(s, n, m): 
  if n > m: return 1 
  if n == m: return s[n] 
  k = (n + m) // 2 
  return product(s, n, k) * product(s, k + 1, m) 

def factorialPS(n): 

  small_swing = [1,1,1,3,3,15,5,35,35,315,63,693,231,3003,429,6435,6435, 
          109395,12155,230945,46189,969969,88179,2028117,676039,16900975, 
          1300075,35102025,5014575,145422675,9694845,300540195,300540195] 

  def swing(m, primes): 
      if m < 33: return small_swing[m] 

      s = bisect.bisect_left(primes, 1 + isqrt(m)) 
      d = bisect.bisect_left(primes, 1 + m // 3) 
      e = bisect.bisect_left(primes, 1 + m // 2) 
      g = bisect.bisect_left(primes, 1 + m) 

      factors = primes[e:g] 
      factors += filter(lambda x: (m // x) & 1 == 1, primes[s:d]) 
      for prime in primes[1:s]:   
          p, q = 1, m 
          while True: 
              q //= prime 
              if q == 0: break 
              if q & 1 == 1: 
                  p *= prime 
          if p > 1: factors.append(p) 

      return product(factors, 0, len(factors) - 1) 

  def odd_factorial(n, primes): 
      if n < 2: return 1 
      return (odd_factorial(n // 2, primes)**2) * swing(n, primes) 

  def eval(n): 
      if n < 0: 
          raise ValueError('factorial not defined for negative numbers') 

      if n == 0: return 1 
      if n < 20: return product(range(2, n + 1), 0, n-2) 

      N, bits = n, n 
      while N != 0: 
          bits -= N & 1 
          N >>= 1 

      primes = Primes(n) 
      return odd_factorial(n, primes) * 2**bits 

  return eval(n)

start = time.time()
answer = factorialPS(1280000) 
print(time.time()-start)
semi-extrínseco
fonte
1

Java - 10.9

n = 885000

Mergesort-y.

import java.math.BigInteger;

public class Factorials {

    public static BigInteger fac;

    public static BigInteger two = BigInteger.valueOf(2);

    static BigInteger mul(BigInteger start, BigInteger end) {
        if(start.equals(end)) {
            return start;
        } else {
            BigInteger mid = start.add(end.subtract(start).divide(Factorials.two));
            return Factorials.mul(start, mid).multiply(Factorials.mul(mid.add(BigInteger.ONE), end));
        }
    }

    public static void main(String[] args) {
        Factorials.fac = BigInteger.valueOf(Integer.parseInt(args[0]));
        long t = System.nanoTime();
        BigInteger result = mul(BigInteger.ONE, fac);
        t = System.nanoTime() - t;
        System.out.print(String.valueOf(((float) t) / 1000000000)); //result.toString()+" @ "+
    }
}

BigIntegers são lentos.

Recomendações para bibliotecas inteiras Java de alta velocidade e precisão arbitrária? : P

cjfaure
fonte
Posso roubar seu código para torná-lo multithread?
Simon Kuang
@SimonKuang Vá em frente. : P Entradas multiencadeadas não são permitidas aqui, no entanto. Além disso, convém usar uma implementação mais eficiente do BigInteger.
Cjfaure
Mergesort-y É chamado de dividir e conquistar.
31418 John May90
1

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.

#include <vector>
#include <iostream>
#include <stdint.h>
#include <ctime>

typedef uint64_t digit;
typedef std::vector<digit> number;

std::ostream &operator<<(std::ostream &s, const number &x)
{
    std::vector<char> o;
    size_t size = x.size() * 21;
    o.resize(size);
    size_t lud = 0;
    for(number::const_reverse_iterator i = x.rbegin(), end = x.rend(); i != end; i++)
    {
        digit carry = 0;
        int j;
        for(j = 0; j <= lud || carry; j++)
        {
            digit r = o[j] * (1LL << 32) + carry;
            o[j] = r % 10;
            carry = r / 10;
        }
        lud = j;
        carry = 0;
        for(j = 0; j <= lud || carry; j++)
        {
            digit r = o[j] * (1LL << 32) + carry;
            o[j] = r % 10;
            carry = r / 10;
        }
        lud = j;
        carry = *i;
        for(j = 0; carry; j++)
        {
            digit r = o[j] + (carry % 10);
            carry /= 10;
            carry += r / 10;
            o[j] = r % 10;
        }
        if(j > lud)
            lud = j;
    }
    for(int j = lud; j--;)
        s.put(o[j] + '0');
    return s;
}

inline uint64_t dmul(uint64_t x, uint64_t y, uint64_t &carry)
{
    asm("mulq %2" : "+a"(x), "=d"(carry) : "r"(y));
    return x;
}
inline digit dadd(digit x, digit y, digit &carry)
{
    asm("movq $0, %1; addq %2, %0; adcq %1, %1" : "+r"(x), "=r"(carry), "+r"(y));
    return x;
}

void multiply(number &x, digit y)
{
    x.resize(x.size() + 2);
    digit carry = 0;
    for(number::iterator i = x.begin(), end = x.end(); i != end; i++)
    {
        digit nc, res = dmul(*i, y, nc);
        *i = dadd(res, carry, carry);
        carry += nc;
    }
    size_t sz = x.size();
    for(number::const_reverse_iterator i = x.rbegin(), end = x.rend(); i != end; i++)
    {
        if(*i)
            break;
        sz--;
    }
    x.resize(sz);
}

int main()
{
    const int r = 390000;
    clock_t start = clock();
    number n;
    digit mult = 1;
    n.push_back(1);
    for(digit a = 2; a <= r; a++)
    {
        digit carry, m = dmul(mult, a, carry);
        if(carry)
        {
            multiply(n, mult);
            mult = a;
        }
        else
            mult = m;
    }
    multiply(n, mult);
    std::cout << "Took: " << (clock() - start)/((double)CLOCKS_PER_SEC) << std::endl;
    std::cout << n << std::endl;
}
mniip
fonte
Verifique sua pontuação. Você precisa executar o programa Python 2.7 da pergunta no seu computador. Para o meu computador, eu compilado seu programa com g++ -O2 factorial.cc -o factoriale ela marca 3.90 = 382000 / 98000.
kernigh
Estranho, eu tenho 3,9 e você tem 3,0 para este programa. Eu acho que seu computador mais rápido é uma penalidade. Talvez o seu programa perca a vantagem sobre o Python à medida que raumenta. Nesse caso, e você pode fazer um aumento rem 10 segundos, sua pontuação diminui.
kernigh
0

Python 3: 280000/168000

Tempo executando seu programa: entre 9.87585953253e 10.3046453994. Tempo executando meu programa: about 10.35296977897559.

import time

def factorial(n):
    f = 1
    while n > 1:
        hn = n >> 1
        f = f * 2**hn * double_factorial(n) #dfl[hn + (n & 1) - 1]
        n = hn
    return f
def double_factorial(n):
    #dfl = [1]
    p = 1
    l = 3
    mh = n
    while l <= n:
        p *= l
        l += 2
        #dfl.append(p)
    return p

start = time.clock()
factorial(280000)
end = time.clock()

print(end - start)

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 = 169000em 10 segundos .:

def factorial(n):
    p=1
    for i in range(n):
        p*=i+1
    return p
Justin
fonte
0

Ruby 2.1

pontuação = 1,80 = 176_000 / 98_000

EDIT: aprimorado de 1,35 = 132_000 / 98_000

Peguei 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.

  1. Meu programa no Ruby 2.1: score = 1.80 = 176_000 / 98_000
  2. Algoritmo trivial em Python 2.7: score = 1 = 98_000 / 98_000
  3. Algoritmo trivial em Ruby 2.1: score = 0,878 = 86_000 / 98_000

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.

require 'benchmark'
require 'optparse'
require 'prime'

def factorial(n)
  # calculate primes up to n, drop the 2
  @odd_primes = Prime.each(n).drop(1)

  # count prime factors of factorial(n)
  @factors = Hash.new(0)
  factorial_recurse(n)

  shift = @factors.delete(2) || 0
  @factors.inject(1) {|product, (base, exp)|
    product * base**exp
  } << shift
end

def factorial_recurse(n)
  return if n < 2

  # collect prime factors of 2 * 4 * 6 * .. * n
  #  = (2 * 2 * 2 * .. * 2) * (1 * 2 * 3 * .. * exp)
  #  = 2**exp * factorial(exp) where exp = floor(n/2)
  exp = n >> 1
  factorial_recurse(exp)
  @factors[2] += exp

  # collect prime factors 3 * 5 * 7 * ... * n
  for prime in @odd_primes
    break if prime > n
    exp = 0
    # count occurences of prime, prime**2, prime**3, .. n
    prime_power = prime
    until prime_power > n
      # floor(n / prime_power) occurences in 1 * 2 * .. * n,
      # but only ceil(count / 2) occurences in 3 * 5 * .. * n
      @factors[prime] += (n / prime_power + 1) >> 1
      prime_power *= prime
    end
  end
end

# usage: factorial.rb [-ct] [number]
cflag = tflag = false
OptionParser.new {|opts|
  opts.on('-c', 'Check for bugs') { cflag = true }
  opts.on('-t', 'Use trivial algorithm') { tflag = true }
  opts.parse!
}
$*[1] and fail 'too many arguments'
n = Integer($*[0] || 176_000)

if cflag
  factorial(n) == (1..n).reduce(1, :*) or
    fail "bad program: factorial(#{n}) is wrong"
  puts "ok"
  exit
end

# measure processor time to calculate factorial
f = nil
if tflag
  time = Benchmark.measure { f = (1..n).reduce(1, :*) }
else
  time = Benchmark.measure { f = factorial(n) }
end
puts f
puts "Time #{time.total} sec"
Kernigh
fonte
0

Julia - Pontuação = 15.194

Utilizando exatamente a mesma abordagem que a do programa de referência ... ou seja,

f(n)=reduce(*,1:big(n))

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

julia> @time K=f(2340000);
elapsed time: 9.991324093 seconds (814552840 bytes allocated)

No meu computador, com o programa de referência em execução com a entrada 154000, recebo a Time: 10.041181 secsaída (execute usando python ./test.py, em que test.py é o nome do arquivo que contém o código de referência)

Glen O
fonte
0

tcl, 13757

Minha resposta é verificar os limites de tcl.

A primeira linha é apenas para definir um parâmetro de entrada:

set n 13757

Os outros são o próprio algoritmo:

set r 2
for {set i 3} {$i <= $n} {incr i} {set r [expr {$r*$i}]}   
puts $r

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.

sergiol
fonte