Quanto puoi moltiplicare rapidamente?


12

Con il recente bashing di Python , ecco un tentativo di mostrare i punti di forza di Python. La tua sfida è scrivere un programma che calcoli il fattoriale di un numero il più alto possibile entro 10 secondi.n

Il tuo punteggio sarà (highest n for your program on your machine)/(highest n for my program on your machine)

Regole

  • È necessario calcolare una soluzione intera esatta. Poiché il fattoriale sarebbe molto più alto di quello che può stare in un intero senza segno a 64 bit, puoi usare le stringhe se la tua lingua non supporta numeri interi grandi
  • Sono vietate le scappatoie standard. In particolare, non è possibile utilizzare risorse esterne.
  • Solo la parte di calcolo (include il tempo per eventuali soluzioni alternative che utilizzano stringhe) si aggiunge al tempo totale che dovrebbe essere in media inferiore a 10 secondi.
  • Solo programmi a thread singolo.
  • È necessario memorizzare l'output in una forma facilmente stampabile (poiché la stampa richiede tempo) (vedere il mio programma di seguito), stringa, variabile, matrice di caratteri, ecc.

MODIFICARE:

  • Il tuo programma deve fornire l'output corretto per tutti n:1 <= n <= (your highest n)

EDIT2:


Il mio programma

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

Il punteggio più alto vince. Per la cronaca, il mio codice può gestire n = 90000in circa 9.89secondi su un Pentium 4 3.0 GHz


EDIT: tutti possono aggiungere il punteggio piuttosto che solo il più alto n . Solo il più alto nnon ha alcun significato da solo in quanto dipende dal tuo hardware. Altrimenti è impossibile avere un criterio vincente oggettivo. ali0sha's anwer lo fa correttamente.


Abbiamo un vincitore. Non ho accettato la risposta java /codegolf//a/26974/8766 in quanto si tratta di gonne vicine a http://meta.codegolf.stackexchange.com/a/1080/8766


1
Puoi usare operator.mulinvece la funzione lambda
gnibbler

1
Un po 'sorpreso funziona, ma supponendo che io abbia letto correttamente le regole questa soluzione MATLAB sarebbe piacevole:, factorial(Inf)ritorna Infin una frazione di secondo.
Dennis Jaheruddin,

1
@Doorknob Adatto alle scappatoie standard.
Giustino,

1
@DennisJaheruddin, è un po 'complicato fare riferimento a "Inf" come "soluzione intera esatta".
tobyink

1
@Quincunx No, è consentita qualsiasi lingua.
user80551

Risposte:


7

C ++ con GMP, punteggio = 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;
}

8

Python 2.7

42.575 = (6.812.000 / 160.000) ca.


Codice:

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

Test:

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

Produzione:

10.0069999695
10.0729999542
10.0360000134
9.98699998856

Come funziona:

Le moltiplicazioni più grandi richiedono più tempo, quindi vogliamo fare il maggior numero possibile di piccole moltiplicazioni. Ciò è particolarmente vero in Python, dove per i numeri meno 2^64usiamo l'aritmetica hardware e soprattutto per il software. Quindi, in m(L), iniziamo con un elenco L; se è di lunghezza dispari, rimuoviamo un numero dalla considerazione per renderlo ancora di nuovo. Quindi moltiplichiamo l'elemento 1con l'elemento -2, l'elemento 3con -4ecc. In modo che

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]

Questo approccio ci consente di utilizzare l'aritmetica hardware il più a lungo possibile, dopodiché passiamo all'efficiente libreria aritmetica di gmc.

In fac2, adottiamo anche un approccio di divisione e conquista più classico, in cui suddividiamo ogni multiplo di 2 e li spostiamo alla fine per un banale aumento delle prestazioni. L'ho incluso qui perché di solito è circa lo 0,5% più veloce di fac1.

Versione fac1Golfed (perché 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]

1
Se il backend GMP include una funzione di spostamento dei bit, è possibile mantenere i numeri ancora più piccoli dividendo ciascun numero nell'elenco per 2 fino a quando è pari e quindi facendo un singolo turno alla fine.
Peter Taylor,

Da dove vieni gmpy2? $ python Python 2.7.3 (impostazione predefinita, 27 febbraio 2014, 19:58:35) [GCC 4.6.3] su linux2 Digitare "help", "copyright", "credits" o "licence" per ulteriori informazioni. >>> da gmpy2 import mpz Traceback (ultima chiamata più recente): file "<stdin>", linea 1, in <modulo> ImportError: nessun modulo chiamato gmpy2 >>>
user80551

@ user80551: code.google.com/p/gmpy (il miglior risultato di ricerca di Google) ha programmi di installazione per molte piattaforme diverse.
alexander-brett,

Per la versione giocata a golf, non potresti farlo while len(L): ...invece di while len(L)>1: ...?
user80551

No: la funzione all'interno di quel loop non prenderà mai la lista sotto la lunghezza 1 e comunque abbiamo bisogno del primo elemento!
alexander-brett,

2

Java - 125.15 (21.400.000 / 171.000)

Anche spudoratamente copiato dal repository Github di Peter Luschny (grazie @ semi-estrinseco) e concesso in licenza con la licenza MIT, utilizza l'algoritmo di "fattorizzazione primaria nidificata" come proposto da Albert Schönhage et al. (secondo la pagina di descrizione degli algoritmi fattoriali di Luschny ).

Ho leggermente adattato l'algoritmo per utilizzare Java BigInteger e non usare una tabella di ricerca per n <20.

Compilato con gcj, che utilizza GMP per la sua implementazione BigInteger, e funzionava su Linux 3.12.4 (Gentoo), su 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);
    }
}

Compilato congcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r

1

Python 3, n = 100000

Era sufficiente una semplice modifica dell'algoritmo per aumentare di 10000 il codice di esempio.

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

Ovviamente non è la risposta più creativa, ma c'è davvero solo un modo per fare un fattoriale ....


Per favore dai il punteggio, vedi la mia modifica. Il dosso sarebbe probabilmente perché la tua macchina è migliore della mia.
user80551

1

Perl + C, n = circa 3 milioni

Qui sto usando la libreria Math :: BigInt :: GMP disponibile su CPAN, che fornisce un enorme incremento di velocità per gli oggetti core Math :: BigInt di 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";

Tieni presente che il mio computer è probabilmente un po 'più lento del tuo. Utilizzando il tuo script Python originale, posso calcolare solo factorial(40000)in 10 secondi; factorial(90000)richiede molto più tempo. (Ho premuto Ctrl + C dopo un minuto.) Sul tuo hardware, usando Math :: BigInt :: GMP, potresti essere in grado di calcolare il fattoriale di 5 milioni o più in meno di 10 secondi.

Una cosa che potresti notare è che, sebbene il fattoriale sia calcolato in modo incredibilmente rapido, la stampa del risultato è molto lenta, impiegando circa tre volte di più del calcolo originale. Questo perché GMP utilizza internamente una rappresentazione binaria anziché decimale e la stampa richiede una conversione binaria in decimale.


1
Penso che GMP conti come una risorsa esterna. (Anche se sicuramente rende le cose molto più facili rispetto all'implementazione della fattorizzazione primaria e della moltiplicazione di Schönhage-Strassen da zero.)
r3mainer

3
Supponevo che "risorsa esterna" si riferisse alla ricerca di soluzioni da una serie precalcolata di risposte in un database, o un servizio web, ecc.
tobyink

Squeamish: le biblioteche normalmente non contano come risorse esterne a meno che non abbiano una funzione che rientra nella noiosa regola delle scappatoie.
alexander-brett,

1
Tobyink: puoi spiegare cosa fa il tuo programma? sembra che tu stia usando una funzione integrata (bfac?)
alexander-brett,

Sì. Questa risposta non è valida, poiché utilizza il metodo fattoriale diMath::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)

Fa uso della relativa facilità di moltiplicazione di molti gruppi di piccoli numeri e quindi moltiplicandoli rispetto al gran numero di moltiplicazioni che coinvolgono un numero enorme.


1

C #: 0,48 (77.000 / 160.000)

Non sono contento di questo.

C # è così lento?

Ma ecco la mia voce comunque.

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 ci vuole 00:00:09:8708952per calcolare.

Sto correndo in modalità Release, fuori da Visual Studio, usando un Core i3-2330M @ 2.2GHz.

Modifica: Dal momento che non sto facendo nulla di intelligente, accetto quel risultato. Forse .NET Framework 4.5 è un po 'sovraccarico (o BigInteger non è così veloce).


Si prega di dare il punteggio e non solon
user80551

1
Potresti usare l' zero approached byoperatore per renderlo più bello (come iniziare n = ... + 1poi fare while (0 <-- n) result *= n;)
Cthulhu,

1
BigInteger per .NET probabilmente non ha implementato algoritmi per moltiplicare numeri più grandi, come Karatsuba o Toom-3. In tal caso, questo è un buon esempio di come Python è più veloce.
kernigh,

1

bc, punteggio = 0,19

Che diamine, ecco il mio contendente per "Quanto puoi moltiplicare lentamente?"

bc è "Un linguaggio di calcolatrice di precisione arbitrario", ma sfortunatamente piuttosto lento:

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

In circa 10 secondi sulla mia metà del 2012 MacBook Pro (Intel Core i7 a 2,3 GHz) la risposta di Python di riferimento può calcolare 122000 !, ma questo script bc può calcolare solo 23600 !.

Al contrario 10000! richiede 1.5s con lo script di riferimento di Python, ma lo script bc richiede 50s.

Oh caro.


1
OpenBSD bc (1) è più veloce. Il tuo programma ottiene 0,29 = 28000/98000. Non c'è read(), quindi ho corso time sed 's/read()/28000/' factorial.bc | bc.
kernigh,

1

Bash: punteggio = 0,001206 (181/150000)

Ho rubato le funzioni matematiche da Rosettacode - Lunga moltiplicazione che non ho analizzato né cercato di ottimizzare.
Sei libero di modificare l'algoritmo o di provare un metodo di suddivisione delle stringhe diverso.

#!/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 

1
Si prega di aggiungere il punteggio e non solo il più alto n
user80551

@ user80551 è fatto
Emmanuel

1

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

Spudoratamente copiato da Peter Luschny,
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm ,
che fornisce questo codice con la licenza "Creative Commons Attribution-ShareAlike 3.0".

Questo è in realtà un algoritmo piuttosto avanzato, che utilizza qualcosa chiamato "fattoriale oscillante" e un elenco di numeri primi. Ho il sospetto che potrebbe essere ancora più veloce se gradisse molte delle altre risposte ed eseguisse la maggior parte delle moltiplicazioni con numeri interi a 32 bit.

#! /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)

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()+" @ "+
    }
}

BigIntegersono lenti.

Consigli per le librerie di interi Java ad alta velocità a precisione arbitraria? : P


Posso rubare il tuo codice per renderlo multithread?
Simon Kuang,

@SimonKuang Vai avanti. : P Le voci multi-thread non sono consentite qui, tuttavia. Inoltre, potresti voler utilizzare un'implementazione BigInteger più efficiente.
cjfaure,

Mergesort-y Si chiama divide-and-conquer.
johnchen902,

1

C ++ (specifico per x86_64) - 3.0 (390000/130000)

(facilmente trasportabile su x86-32, il porting su altre architetture implica una significativa perdita di velocità)

Ecco la mia micro-implementazione della lunga aritmetica.
Il calcolo stesso richiede 10 secondi e mentre l'output è in forma facilmente stampabile (vedere il operator<<sovraccarico), ci vuole del tempo in più per stamparlo.

#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;
}

Controlla il tuo punteggio. Devi eseguire il programma Python 2.7 della domanda sul tuo computer. Per il mio computer, ho compilato il tuo programma con g++ -O2 factorial.cc -o factorialun punteggio di 3.90 = 382000 / 98000.
kernigh,

Strano, ho 3.9 e hai 3.0 per questo programma. Immagino che il tuo computer più veloce sia una penalità. Forse il tuo programma perde i suoi vantaggi rispetto a Python man mano che raumenta. Se è così, e puoi fare un rpunteggio più alto in 10 secondi, il tuo punteggio diminuisce.
kernigh,

0

Python 3: 280000/168000

Tempo di esecuzione del programma: tra 9.87585953253e 10.3046453994. Tempo di esecuzione del mio programma: circa 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)

Ho letto questa risposta su cs.SE e ho deciso di provare a implementarla in Python. Tuttavia, l'ho scoperto per caso n! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!(nota: !!è il doppio fattoriale ). Quindi l'ho convertito in una forma non ricorsiva.

I commenti mostrano il mio tentativo di evitare di ricalcolare il doppio fattoriale, ma ho scoperto che l'archiviazione di ogni valore era troppo costosa in termini di memoria da causare un rallentamento del mio computer. Posso migliorare questo memorizzando solo ciò che è necessario.

Stranamente, ho implementato l'ingenua moltiplicazione diretta in Python 3 e funziona meglio del tuo programma: n = 169000in 10 secondi .:

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

0

Ruby 2.1

punteggio = 1,80 = 176_000 / 98_000

EDIT: migliorato da 1,35 = 132_000 / 98_000

Ho preso idee dall'algoritmo fattoriale GMP . Questo programma utilizza la libreria standard per generare numeri primi. Ruby è una cattiva scelta perché la moltiplicazione sembra più lenta in Ruby che in Python.

  1. Il mio programma in Ruby 2.1: punteggio = 1.80 = 176_000 / 98_000
  2. Algoritmo di Trivial in Python 2.7: punteggio = 1 = 98_000 / 98_000
  3. Algoritmo di Trivial in Ruby 2.1: punteggio = 0.878 = 86_000 / 98_000

Sì, il mio binario di ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-openbsd]collegamenti contro GMP. Ruby 2.1 ha aggiunto una funzione per usare GMP per una grande moltiplicazione, ma sembra ancora più lento di 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"

0

Julia - Punteggio = 15.194

Utilizzando lo stesso identico approccio del programma di riferimento ... ovvero

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

Quindi utilizza la riduzione, l'operazione di moltiplicazione binaria di base e un intervallo (in questo caso, usando big (n) per forzare il calcolo da eseguire in BigInt piuttosto che Int64). Da questo, ho capito

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

Sul mio computer, con il programma di riferimento in esecuzione con input di 154000, ottengo l' Time: 10.041181 secoutput (eseguito utilizzando python ./test.py, dove test.py è il nome del file contenente il codice di riferimento)


0

tcl, 13757

La mia risposta è verificare i limiti di tcl.

La prima riga è solo per impostare un parametro di input:

set n 13757

Gli altri sono l'algoritmo stesso:

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

Ho testato il mio codice su http://rextester.com/live/WEL36956 ; Se ne ingrandisco, ottengo un SIGKILL; non posso ingrandirmi su un interprete tcl locale, che non ho.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.