Berapa banyak yang bisa Anda lipat cepat?

12

Dengan bashing Python baru-baru ini , inilah upaya untuk menunjukkan kekuatan Python. Tantangan Anda adalah menulis sebuah program yang menghitung faktorial angka setinggi mungkin dalam 10 detik.n

Skor Anda akan (highest n for your program on your machine)/(highest n for my program on your machine)

Aturan

  • Anda harus menghitung solusi integer yang tepat. Karena faktorial akan jauh lebih tinggi daripada apa yang dapat ditampung dalam integer 64 bit yang tidak ditandatangani, Anda dapat menggunakan string jika bahasa Anda tidak mendukung bilangan bulat besar
  • Celah standar dilarang. Khususnya, Anda tidak dapat menggunakan sumber daya eksternal apa pun.
  • Hanya bagian perhitungan (ini termasuk waktu untuk setiap penyelesaian masalah menggunakan string) menambah total waktu yang seharusnya di bawah 10 detik rata-rata.
  • Hanya program berulir tunggal.
  • Anda harus menyimpan output dalam bentuk yang mudah dicetak (karena pencetakan membutuhkan waktu) (lihat program saya di bawah), string, variabel, array karakter, dll.

EDIT:

  • Program Anda harus memberikan hasil yang benar untuk semua n:1 <= n <= (your highest n)

EDIT2:


Program saya

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

Kemenangan skor tertinggi. Sebagai catatan, kode saya dapat mengatur n = 90000dalam beberapa 9.89detik pada Pentium 4 3,0 GHz


EDIT: Can semua orang silahkan menambahkan skor bukan hanya tertinggi n . Hanya yang tertinggi ntidak memiliki arti karena tergantung pada perangkat keras Anda. Tidak mungkin memiliki kriteria kemenangan yang obyektif sebaliknya. Jawaban ali0sha melakukan ini dengan benar.


Kami punya pemenang. Saya tidak menerima jawaban java /codegolf//a/26974/8766 karena semacam rok dekat dengan http://meta.codegolf.stackexchange.com/a/1080/8766

pengguna80551
sumber
1
Anda dapat menggunakan operator.mulfungsi lambda
gnibbler
1
Agak terkejut ini berhasil, tetapi dengan asumsi saya membaca aturan dengan benar solusi MATLAB ini akan menyenangkan factorial(Inf):, kembali Infdalam sepersekian detik.
Dennis Jaheruddin
1
@ Doorknob Itu sesuai dengan celah standar.
Justin
1
@ DennisJaheruddin, ini sedikit berlebihan untuk menyebut "Inf" sebagai "solusi integer yang tepat".
tobyink
1
@ Quincunx Tidak, bahasa apa pun diizinkan.
user80551

Jawaban:

7

C ++ dengan GMP, skor = 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
sumber
8

Python 2.7

42.575 = (6.812.000 / 160.000) kira-kira


Kode:

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

Uji:

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

Keluaran:

10.0069999695
10.0729999542
10.0360000134
9.98699998856

Bagaimana itu bekerja:

Penggandaan yang lebih besar membutuhkan lebih banyak waktu, jadi kami ingin melakukan penggandaan sekecil mungkin. Ini terutama berlaku di Python di mana untuk angka yang lebih sedikit 2^64kita menggunakan perangkat keras aritmatika, dan di atas itu kita menggunakan perangkat lunak. Jadi, di m(L), kita mulai dengan daftar L; jika panjangnya aneh, kami menghapus satu nomor dari pertimbangan untuk membuatnya lebih lagi. Lalu kita kalikan elemen 1dengan elemen -2, elemen 3dengan -4, dll, sehingga

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]

Pendekatan ini memastikan kami menggunakan aritmatika perangkat keras selama mungkin, berikut ini kami beralih ke pustaka aritmatika gmc yang efisien.

Dalam fac2, kami mengambil pendekatan pembagian dan menaklukkan yang lebih klasik juga, di mana kami membagi setiap kelipatan dari 2 dan menggeser mereka pada akhirnya untuk meningkatkan kinerja sepele. Saya sudah memasukkannya di sini karena biasanya sekitar 0,5% lebih cepat daripada fac1.

Versi Golf fac1(karena saya bisa), 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
sumber
1
Jika backend GMP menyertakan fungsi bitshift, maka Anda dapat menjaga jumlahnya lebih kecil dengan membagi masing-masing angka dalam daftar dengan 2 hingga genap dan kemudian melakukan satu shift di akhir.
Peter Taylor
Darimana Anda mendapatkan gmpy2? $ python Python 2.7.3 (default, 27 Feb 2014, 19:58:35) [GCC 4.6.3] di linux2 Ketik "help", "hak cipta", "kredit" atau "lisensi" untuk informasi lebih lanjut. >>> dari gmpy2 import mpz Traceback (panggilan terakhir terakhir): File "<stdin>", baris 1, di <module> ImportError: Tidak ada modul bernama gmpy2 >>>
user80551
@ user80551: code.google.com/p/gmpy (hasil pencarian google teratas) memiliki installer untuk banyak platform berbeda.
alexander-brett
Untuk versi golfed, tidak bisa Anda lakukan while len(L): ...bukan while len(L)>1: ...?
user80551
Tidak: fungsi di dalam loop itu tidak akan pernah mengambil daftar di bawah panjang 1 dan bagaimanapun kita membutuhkan elemen pertama!
alexander-brett
2

Jawa - 125,15 (21,400,000 / 171,000)

Juga tanpa malu-malu disalin dari repo Peter Luschny's Github (terima kasih @ semi-ekstrinsik) dan dilisensikan di bawah lisensi MIT, ini menggunakan algoritma "prima faktorisasi bersarang kuadrat" seperti yang diusulkan oleh Albert Schönhage et al. (Menurut halaman deskripsi algoritma faktorial Luschny ).

Saya sedikit mengadaptasi algoritma untuk menggunakan BigInteger Java dan tidak menggunakan tabel pencarian untuk n <20.

Dikompilasi dengan gcj, yang menggunakan GMP untuk implementasi BigInteger, dan dijalankan di Linux 3.12.4 (Gentoo), pada Core i7 4700MQ di 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
sumber
Dikompilasi dengangcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r
1

Python 3, n = 100000

Perubahan algoritma sederhana adalah semua yang diperlukan untuk meningkatkan kode sampel hingga 10.000.

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

Jelas bukan jawaban yang paling kreatif, tetapi sebenarnya hanya ada satu cara untuk melakukan faktorial ....

Gagang pintu
sumber
Tolong beri skor, lihat edit saya. The benjolan mungkin akan menjadi karena mesin Anda lebih baik dari saya.
user80551
1

Perl + C, n = sekitar 3 juta

Di sini saya menggunakan perpustakaan Math :: BigInt :: GMP yang tersedia di CPAN, yang memberikan peningkatan kecepatan besar untuk objek inti Math :: BigInt 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";

Ingatlah bahwa komputer saya mungkin sedikit lebih lambat dari milik Anda. Menggunakan skrip Python asli Anda, saya hanya dapat menghitung factorial(40000)dalam 10 detik; factorial(90000)membutuhkan waktu lebih lama. (Saya menekan Ctrl + C setelah satu menit.) Pada perangkat keras Anda, menggunakan Math :: BigInt :: GMP, Anda mungkin dapat menghitung faktorial 5 juta atau lebih dalam waktu kurang dari 10 detik.

Satu hal yang mungkin Anda perhatikan adalah bahwa meskipun faktorial dihitung sangat cepat, mencetak hasilnya sangat lambat, memakan waktu sekitar tiga kali lebih lama dari perhitungan aslinya. Ini karena GMP secara internal menggunakan representasi biner daripada desimal, dan mencetaknya memerlukan konversi biner ke desimal.

tobyink
sumber
1
Saya pikir GMP dianggap sebagai sumber daya eksternal. (Meskipun tentu membuat segalanya jauh lebih mudah daripada menerapkan faktorisasi utama dan multiplikasi Schönhage-Strassen dari awal.)
r3mainer
3
Saya berasumsi bahwa "sumber daya eksternal" merujuk pada mencari solusi dari serangkaian jawaban yang telah dihitung sebelumnya dalam database, atau layanan web, dll.
tobyink
Mual: perpustakaan biasanya tidak dihitung sebagai sumber daya eksternal kecuali mereka memiliki fungsi yang berada di bawah aturan lubang membosankan.
alexander-brett
1
Tobyink: bisakah Anda menjelaskan apa yang program Anda lakukan? sepertinya Anda hanya menggunakan fungsi bawaan (bfac?)
alexander-brett
Ya. Jawaban ini tidak valid, karena menggunakan metode faktorialMath::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)

Buatlah penggunaan relatif mudah penggandaan banyak kelompok dengan jumlah kecil dan kemudian mengalikannya dibandingkan dengan jumlah besar penggandaan yang melibatkan jumlah besar.

Cthulhu
sumber
1

C #: 0,48 (77.000 / 160.000)

Saya tidak senang dengan ini.

Apakah C # itu lambat?

Tapi ini adalah entri saya.

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

    }

Ketika n = 77000 dibutuhkan 00:00:09:8708952untuk menghitung.

Saya sedang menjalankan dalam mode Rilis, di luar Visual Studio, menggunakan Core i3-2330M @ 2.2GHz.

Sunting: Karena saya tidak melakukan sesuatu yang cerdas, saya menerima hasil itu. Mungkin .NET Framework 4.5 adalah addind overhead (atau BigInteger tidak secepat itu).

Ricardo Pieper
sumber
Tolong beri skor dan bukan hanyan
user80551
1
Anda bisa menggunakan zero approached byoperator untuk membuatnya lebih cantik (seperti mulai dengan n = ... + 1kemudian lakukan while (0 <-- n) result *= n;)
Cthulhu
1
BigInteger untuk .NET mungkin tidak menerapkan algoritma untuk mengalikan angka yang lebih besar, seperti Karatsuba atau Toom-3. Jika demikian, ini adalah contoh yang baik tentang bagaimana Python lebih cepat.
kernigh
1

bc, skor = 0,19

Apa-apaan ini, inilah penantang saya untuk "Berapa banyak yang bisa Anda gandakan secara perlahan?"

bc adalah "Bahasa kalkulator presisi arbitrer", tapi sayangnya agak lambat:

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

Dalam sekitar 10 detik pada pertengahan 2012 MacBook Pro saya (2,3 GHz Intel Core i7) jawaban python referensi dapat menghitung 122000 !, tetapi skrip bc ini hanya dapat menghitung 23600 !.

Sebaliknya 10.000! membutuhkan 1,5 detik dengan skrip referensi python, tetapi skrip bc membutuhkan 50 detik.

Oh sayang.

Trauma Digital
sumber
1
OpenBSD bc (1) lebih cepat. Nilai program Anda 0,29 = 28000/98000. Tidak ada read(), jadi saya berlari time sed 's/read()/28000/' factorial.bc | bc.
kernigh
1

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

Saya mencuri fungsi matematika dari Rosettacode - Perkalian panjang yang tidak saya analisis atau coba optimalkan.
Anda bebas mengubah algoritme atau mencoba metode pemisahan string yang berbeda.

#!/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
sumber
1
Silakan tambahkan skor dan bukan hanya n
user80551
@ user80551 selesai
Emmanuel
1

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

Tanpa malu-malu disalin dari Peter Luschny,
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm ,
yang menyediakan kode ini di bawah lisensi "Creative Commons Attribution-ShareAlike 3.0" lisensi.

Ini sebenarnya adalah algoritma yang cukup canggih, menggunakan sesuatu yang disebut "faktor ayunan" dan daftar bilangan prima. Saya menduga itu bisa lebih cepat jika memang seperti banyak jawaban lain dan melakukan sebagian besar perkalian dengan bilangan bulat 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)
semi ekstrinsik
sumber
1

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

BigIntegerItu lambat.

Rekomendasi untuk perpustakaan Java integer berkecepatan tinggi yang sewenang-wenang? : P

cjfaure
sumber
Bisakah saya mencuri kode Anda untuk membuatnya multithreaded?
Simon Kuang
@SimonKuang Silakan. : P Entri multi-utas tidak diizinkan di sini. Juga, Anda mungkin ingin menggunakan implementasi BigInteger yang lebih efisien.
cjfaure
Mergesort-y Ini disebut divide-and-conquer.
johnchen902
1

C ++ (spesifik x86_64) - 3.0 (390000/130000)

(mudah dibawa ke x86-32, porting ke arsitektur lain menyiratkan kehilangan kecepatan yang signifikan)

Inilah implementasi mikro dari aritmatika panjang saya sendiri.
Perhitungannya sendiri membutuhkan waktu 10 detik, dan sementara hasilnya dalam bentuk yang mudah dicetak (lihat operator<<kelebihannya), dibutuhkan waktu lebih lama untuk mencetaknya.

#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
sumber
Periksa skor Anda. Anda perlu menjalankan program Python 2.7 di komputer Anda. Untuk komputer saya, saya mengkompilasi program Anda dengan g++ -O2 factorial.cc -o factorialdan skor 3,90 = 382000 / 98000.
kernigh
Aneh, saya mendapat 3,9 dan Anda mendapat 3,0 untuk program ini. Saya kira komputer Anda yang lebih cepat adalah penalti. Mungkin program Anda kehilangan keunggulannya atas Python karena rpeningkatan. Jika demikian, dan Anda dapat melakukan lebih tinggi rdalam 10 detik, maka skor Anda turun.
kernigh
0

Python 3: 280000/168000

Waktu menjalankan program Anda: antara 9.87585953253dan 10.3046453994. Waktu menjalankan program saya: tentang 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)

Saya membaca jawaban ini di cs.SE dan memutuskan untuk mencoba mengimplementasikannya dengan Python. Namun, saya tidak sengaja menemukan itu n! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!(catatan: !!adalah faktorial ganda ). Jadi saya mengubahnya menjadi bentuk non-rekursif.

Komentar menunjukkan upaya saya untuk menghindari penghitungan ulang faktorial ganda, tetapi saya menemukan bahwa menyimpan setiap nilai terlalu mahal karena memori membuat komputer saya berjalan lebih lambat. Saya dapat meningkatkan ini dengan hanya menyimpan apa yang dibutuhkan.

Anehnya, saya menerapkan perkalian naif langsung di Python 3, dan itu lebih baik daripada program Anda: n = 169000dalam 10 detik .:

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

Ruby 2.1

skor = 1,80 = 176_000 / 98_000

EDIT: ditingkatkan dari 1,35 = 132_000 / 98_000

Saya mengambil ide dari algoritma faktorial GMP . Program ini menggunakan perpustakaan standar untuk menghasilkan bilangan prima. Ruby adalah pilihan yang buruk karena multiplikasi tampaknya lebih lambat di Ruby daripada di Python.

  1. Program saya di Ruby 2.1: score = 1.80 = 176_000 / 98_000
  2. Algoritma trivial dalam Python 2.7: skor = 1 = 98_000 / 98_000
  3. Algoritma sepele dalam Ruby 2.1: skor = 0,878 = 86_000 / 98_000

Ya, biner ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-openbsd]tautan saya terhadap GMP. Ruby 2.1 menambahkan fitur untuk menggunakan GMP untuk multiplikasi besar, tetapi tampaknya masih lebih lambat dari 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
sumber
0

Julia - Skor = 15.194

Memanfaatkan pendekatan yang sama persis dengan program referensi ... yaitu,

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

Jadi ia menggunakan mengurangi, operasi perkalian biner dasar, dan rentang (dalam hal ini, menggunakan besar (n) untuk memaksa perhitungan dilakukan di BigInt daripada Int64). Dari ini, saya mengerti

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

Di komputer saya, dengan program referensi berjalan dengan input 154000, saya mendapatkan Time: 10.041181 secoutput (jalankan menggunakan python ./test.py, di mana test.py adalah nama file yang berisi kode referensi)

Glen O
sumber
0

tcl, 13757

Jawaban saya adalah memeriksa batas tcl.

Baris pertama hanya untuk mengatur parameter input:

set n 13757

Yang lain adalah algoritma itu sendiri:

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

Saya menguji kode saya di http://rextester.com/live/WEL36956 ; Jika saya membuat n lebih besar, saya mendapatkan SIGKILL; mungkin n bisa menjadi lebih besar pada penerjemah tcl lokal, yang tidak saya miliki.

sergiol
sumber