¿Cuánto puedes multiplicar rápidamente?

12

Con el reciente ataque a Python , aquí hay un intento de mostrar las fortalezas de Python. Su desafío es escribir un programa que calcule el factorial de un número tan alto como sea posible en 10 segundos.n

Tu puntaje será (highest n for your program on your machine)/(highest n for my program on your machine)

Reglas

  • Debe calcular una solución entera exacta. Dado que el factorial sería mucho más alto de lo que cabe en un entero sin signo de 64 bits, puede usar cadenas si su idioma no admite enteros grandes
  • Las lagunas estándar están prohibidas. En particular, no puede utilizar ningún recurso externo.
  • Solo la parte de cálculo (esto incluye el tiempo para cualquier solución alternativa que use cadenas) se suma al tiempo total que debe ser inferior a 10 segundos en promedio.
  • Solo programas de subproceso único.
  • Debe almacenar la salida en una forma fácilmente imprimible (ya que la impresión lleva tiempo) (vea mi programa a continuación), cadena, variable, matriz de caracteres, etc.

EDITAR:

  • Su programa debe dar la salida correcta para todos n:1 <= n <= (your highest n)

EDIT2:


Mi 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" )

La puntuación más alta gana. Para el registro, mi código puede administrar n = 90000en unos 9.89segundos en un Pentium 4 3.0 GHz


EDITAR: ¿Pueden todos agregar la puntuación en lugar de solo la n más alta ? Solo lo más alto nno tiene sentido en sí mismo, ya que depende de su hardware. De lo contrario, es imposible tener un criterio ganador objetivo. La respuesta de ali0sha hace esto correctamente.


Tenemos un ganador. No acepté la respuesta de Java /codegolf//a/26974/8766, ya que se trata de faldas cercanas a http://meta.codegolf.stackexchange.com/a/1080/8766

usuario80551
fuente
1
Puede usar en operator.mullugar de la función lambda
gnibbler
1
Bit sorprendió que esto funcionara, pero suponiendo que leí las reglas correctamente, esta solución de MATLAB sería buena: factorial(Inf)regresa Infen una fracción de segundo.
Dennis Jaheruddin
1
@Doorknob Eso encaja en las lagunas estándar.
Justin
1
@DennisJaheruddin, es un poco exagerado referirse a "Inf" como una "solución entera exacta".
tobyink
1
@Quincunx No, se permite cualquier idioma.
user80551

Respuestas:

7

C ++ con GMP, puntaje = 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
fuente
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

Prueba:

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

Salida:

10.0069999695
10.0729999542
10.0360000134
9.98699998856

Cómo funciona:

Las multiplicaciones más grandes toman más tiempo, por lo tanto, queremos hacer tantas multiplicaciones pequeñas como sea posible. Esto es especialmente cierto en Python, donde para números menores 2^64usamos aritmética de hardware y, por encima, usamos software. Entonces, en m(L), comenzamos con una lista L; Si tiene una longitud impar, eliminamos un número de la consideración para que sea par nuevamente. Luego multiplicamos elemento 1con elemento -2, elemento 3con -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]

Este enfoque asegura que estemos usando aritmética de hardware durante el mayor tiempo posible, después de lo cual cambiamos a la eficiente biblioteca aritmética gmc.

En fac2, tomamos un enfoque más clásico de dividir y conquistar, donde dividimos cada múltiplo de 2 y los desplazamos al final para obtener un aumento de rendimiento trivial. Lo he incluido aquí porque generalmente es alrededor de 0.5% más rápido que fac1.

Versión de golf de fac1(porque puedo), 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
fuente
1
Si el backend GMP incluye una función de desplazamiento de bits, puede mantener los números aún más pequeños dividiendo cada número en la lista por 2 hasta que sea par y luego haciendo un solo cambio al final.
Peter Taylor
¿De dónde se obtiene gmpy2a partir de? $ python Python 2.7.3 (predeterminado, 27 de febrero de 2014, 19:58:35) [GCC 4.6.3] en linux2 Escriba "ayuda", "copyright", "créditos" o "licencia" para obtener más información. >>> desde gmpy2 import mpz Traceback (última llamada más reciente): Archivo "<stdin>", línea 1, en <module> ImportError: Ningún módulo llamado gmpy2 >>>
user80551
@ user80551: code.google.com/p/gmpy (el principal resultado de búsqueda de google) tiene instaladores para muchas plataformas diferentes.
alexander-brett
Para la versión de golf, ¿no podrías hacer en while len(L): ...lugar de while len(L)>1: ...?
user80551
No: ¡la función dentro de ese ciclo nunca tomará la lista debajo de la longitud 1 y de todos modos necesitamos el primer elemento!
alexander-brett
2

Java - 125.15 (21,400,000 / 171,000)

También copiado descaradamente del repositorio Github de Peter Luschny (gracias @ semi-extrínseco) y con licencia bajo la licencia MIT, esto utiliza el algoritmo de "cuadratura anidada de factorización principal" propuesto por Albert Schönhage et al. (según la página de descripción de algoritmos factoriales de Luschny ).

Adapte ligeramente el algoritmo para usar BigInteger de Java y no usar una tabla de búsqueda para n <20.

Compilado con gcj, que usa GMP para su implementación BigInteger, y se ejecutó en Linux 3.12.4 (Gentoo), en un 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
fuente
Compilado congcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r
1

Python 3, n = 100000

Un simple cambio de algoritmo fue todo lo que se necesitó para aumentar el código de muestra en 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 no es la respuesta más creativa, pero en realidad solo hay una forma de hacer un factorial ...

Pomo de la puerta
fuente
Por favor, da la puntuación, mira mi edición. El golpe probablemente se deba a que su máquina es mejor que la mía.
user80551
1

Perl + C, n = aproximadamente 3 millones

Aquí estoy usando la biblioteca Math :: BigInt :: GMP disponible en CPAN, que proporciona un aumento de velocidad masivo para los objetos básicos Math :: BigInt de 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";

Tenga en cuenta que mi computadora es probablemente un poco más lenta que la suya. Usando su script Python original, solo puedo calcular factorial(40000)en 10 segundos; factorial(90000)Toma mucho más tiempo. (Golpeé Ctrl + C después de un minuto.) En su hardware, usando Math :: BigInt :: GMP, es posible que pueda calcular el factorial de 5 millones o más en menos de 10 segundos.

Una cosa que puede notar es que, aunque el factorial se calcula increíblemente rápido, la impresión del resultado es muy lenta, ya que tarda aproximadamente tres veces más que el cálculo original. Esto se debe a que GMP usa internamente una representación binaria en lugar de decimal, e imprimirla requiere una conversión binaria a decimal.

tobyink
fuente
1
Creo que GMP cuenta como un recurso externo. (Aunque ciertamente hace las cosas mucho más fáciles que implementar la factorización prima y la multiplicación de Schönhage-Strassen desde cero.)
r3mainer
3
Estaba asumiendo que "recurso externo" se refería a buscar soluciones de un conjunto de respuestas
precalculadas
Squeamish: las bibliotecas normalmente no cuentan como recursos externos a menos que tengan una función que caiga bajo la regla de las lagunas aburridas.
alexander-brett
1
Tobyink: ¿puedes explicar qué hace tu programa? parece que solo estás usando una función incorporada (bfac?)
alexander-brett
Sip. Esta respuesta no es válida, ya que utiliza el método factorial 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)

Hace uso de la relativa facilidad de multiplicación de muchos grupos de números pequeños y luego los multiplica en comparación con un gran número de multiplicaciones que involucran un gran número.

Cthulhu
fuente
1

C #: 0,48 (77,000 / 160,000)

No estoy contento con esto.

¿C # es tan lento?

Pero aquí está mi entrada de todos modos.

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

    }

Cuando n = 77000 se tarda 00:00:09:8708952en calcular.

Estoy corriendo en modo Release, fuera de Visual Studio, usando un Core i3-2330M @ 2.2GHz.

Editar: Como no estoy haciendo nada inteligente, acepto ese resultado. Tal vez .NET Framework 4.5 está agregando algunos gastos generales (o BigInteger no es tan rápido).

Ricardo Pieper
fuente
Indique el puntaje y no solon
user80551
1
Puede usar el zero approached byoperador para hacerlo más bonito (como comenzar con n = ... + 1luego hacer while (0 <-- n) result *= n;)
Cthulhu
1
BigInteger para .NET probablemente no implementó algoritmos para multiplicar números más grandes, como Karatsuba o Toom-3. Si es así, este es un buen ejemplo de cómo Python es más rápido.
Kernigh
1

bc, puntaje = 0.19

Qué diablos, aquí está mi contendiente para "¿Cuánto puedes multiplicar lentamente?"

bc es "Un lenguaje de calculador de precisión arbitrario", pero desafortunadamente bastante lento:

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

En aproximadamente 10 segundos en mi MacBook Pro de mediados de 2012 (Intel Core i7 de 2.3 GHz), la respuesta de Python de referencia puede calcular 122000, ¡pero este script bc solo puede calcular 23600 !.

Por el contrario 10000! toma 1.5s con el script de referencia de python, pero el script bc toma 50s.

Oh querido.

Trauma digital
fuente
1
OpenBSD bc (1) es más rápido. Su programa tiene una puntuación de 0.29 = 28000/98000. No hay read(), así que corrí time sed 's/read()/28000/' factorial.bc | bc.
Kernigh
1

Bash: puntaje = 0.001206 (181/150000)

Robé las funciones matemáticas de Rosettacode: multiplicación larga que no analicé ni intenté optimizar.
Puede cambiar el algoritmo o probar un método diferente de división de cadenas.

#!/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
fuente
1
Agregue el puntaje y no solo el n más alto
user80551
@ user80551 está hecho
Emmanuel
1

Python 3, algo avanzado de Peter Luschny: 8.25x (1 280 000/155 000)

Copiado descaradamente de Peter Luschny,
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm ,
quien proporciona este código bajo la licencia "Creative Commons Attribution-ShareAlike 3.0".

Este es en realidad un algoritmo bastante avanzado, que utiliza algo llamado "factorial oscilante" y una lista de números primos. Sospecho que podría ser aún más rápido si le gustaran muchas de las otras respuestas y realizara la mayoría de las multiplicaciones con enteros 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
fuente
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 son lentos.

¿Recomendaciones para bibliotecas de enteros Java de alta velocidad y precisión arbitraria? :PAG

cjfaure
fuente
¿Puedo robar tu código para que sea multiproceso?
Simon Kuang
@SimonKuang Adelante. : P Sin embargo, aquí no se permiten entradas multiproceso. Además, es posible que desee utilizar una implementación de BigInteger más eficiente.
cjfaure
Mergesort-y Se llama divide-and-conquer.
johnchen902
1

C ++ (específico x86_64) - 3.0 (390000/130000)

(fácilmente portátil a x86-32, portar a otras arquitecturas implica una pérdida de velocidad significativa)

Aquí está mi propia micro implementación de aritmética larga.
El cálculo en sí mismo toma 10 segundos, y aunque la salida está en forma fácilmente imprimible (vea la operator<<sobrecarga), toma más tiempo imprimirla.

#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
fuente
Comprueba tu puntuación. Debe ejecutar el programa Python 2.7 de la pregunta en su computadora. Para mi computadora, compilé su programa con g++ -O2 factorial.cc -o factorialun puntaje de 3.90 = 382000 / 98000.
Kernigh
Extraño, obtuve 3.9 y usted obtuvo 3.0 para este programa. Supongo que tu computadora más rápida es una pena. Quizás su programa pierde su ventaja sobre Python a medida que raumenta. Si es así, y puede hacer un mayor ren 10 segundos, entonces su puntaje baja.
Kernigh
0

Python 3: 280000/168000

Tiempo de ejecución de su programa: entre 9.87585953253y 10.3046453994. Tiempo de ejecución de mi programa: aproximadamente 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)

Leí esta respuesta en cs.SE y decidí intentar implementarla en Python. Sin embargo, descubrí accidentalmente que n! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!(nota: !!es el factorial doble ). Entonces lo convertí a una forma no recursiva.

Los comentarios muestran mi intento de evitar volver a calcular el factorial doble, pero descubrí que almacenar cada valor era demasiado costoso para la memoria y hacía que mi computadora funcionara aún más lentamente. Puedo mejorar esto almacenando solo lo que se necesita.

Curiosamente, implementé la ingenua multiplicación directa en Python 3, y funciona mejor que su programa: n = 169000en 10 segundos .:

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

Ruby 2.1

puntuación = 1.80 = 176_000 / 98_000

EDITAR: mejorado de 1.35 = 132_000 / 98_000

Tomé ideas del algoritmo factorial GMP . Este programa usa la biblioteca estándar para generar números primos. Ruby es una mala elección porque la multiplicación parece más lenta en Ruby que en Python.

  1. Mi programa en Ruby 2.1: puntaje = 1.80 = 176_000 / 98_000
  2. Algoritmo trivial en Python 2.7: puntuación = 1 = 98_000 / 98_000
  3. Algoritmo trivial en Ruby 2.1: puntaje = 0.878 = 86_000 / 98_000

Sí, mi binario de ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-openbsd]enlaces contra GMP. Ruby 2.1 agregó una función para usar GMP para una multiplicación grande, pero aún parece más lenta que 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
fuente
0

Julia - Puntuación = 15.194

Utilizando exactamente el mismo enfoque que el del programa de referencia ... es decir,

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

Por lo tanto, utiliza reducir, la operación básica de multiplicación binaria y un rango (en este caso, usar big (n) para forzar el cálculo a realizar en BigInt en lugar de Int64). De esto, obtengo

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

En mi computadora, con el programa de referencia ejecutándose con una entrada de 154000, obtengo Time: 10.041181 secsalida (ejecutar usando python ./test.py, donde test.py es el nombre del archivo que contiene el código de referencia)

Glen O
fuente
0

tcl, 13757

Mi respuesta es verificar los límites de tcl.

La primera línea es solo para establecer un parámetro de entrada:

set n 13757

Los otros son el algoritmo mismo:

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

Probé mi código en http://rextester.com/live/WEL36956 ; Si hago n más grande, obtengo un SIGKILL; may n puede crecer en un intérprete tcl local, que no tengo.

sergiol
fuente