Ne kadar hızlı çoğalırsınız?


12

Son zamanlarda Python dayak ile Python'un güçlü yönlerini göstermeye çalışacağız. Zorluk faktörünüzü n10 saniye içinde olabildiğince yüksek bir sayıyı hesaplayan bir program yazmaktır .

Puanınız (highest n for your program on your machine)/(highest n for my program on your machine)

kurallar

  • Kesin bir tamsayı çözümü hesaplamanız gerekir. Faktöriyel, 64 bit işaretsiz bir tam sayıya sığabileceğinden çok daha yüksek olacağından, diliniz büyük tam sayıları desteklemiyorsa dizeleri kullanabilirsiniz
  • Standart boşluklar yasaktır. Özellikle, harici kaynakları kullanamazsınız.
  • Yalnızca hesaplama bölümü (dizeleri kullanan herhangi bir geçici çözüm için süre içerir), ortalama süreye ortalama 10 saniyenin altında olması gerekir.
  • Yalnızca tek iş parçacıklı programlar.
  • Çıktıyı kolayca yazdırılabilir bir biçimde (yazdırma zaman aldıkça) (aşağıdaki programıma bakın), dize, değişken, karakter dizisi vb.

DÜZENLE:

  • Programınız herkes için doğru çıktıyı vermelidir n:1 <= n <= (your highest n)

EDIT2:


Benim program

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

En yüksek puan kazanır. Kayıt için, kodum Pentium 4 3.0 GHz'de n = 90000yaklaşık 9.89saniyeler içinde yönetebilir


DÜZENLEME: Herkes en yüksek n yerine skoru ekleyebilir mi lütfen ? Sadece en yüksek olanın donanımınıza bağlı olduğu için tek başına bir anlamı yoktur. Aksi takdirde objektif kazanma ölçütüne sahip olmak imkansızdır. ali0sha'nın anwerı bunu doğru yapıyor.n


Bir kazananımız var. Http://meta.codegolf.stackexchange.com/a/1080/8766 gibi bir tür etek gibi java cevabını /codegolf//a/26974/8766 kabul etmedim


1
operator.mulLambda işlevi yerine kullanabilirsiniz
gnibbler

1
Bit bu çalışmaları şaşırttı, ancak kuralları doğru okuduğumu varsayarsak, bu MATLAB çözümü iyi olurdu:, bir saniyenin bir kısmında factorial(Inf)geri döner Inf.
Dennis Jaheruddin

1
@Doorknob Bu standart boşluklara uyuyor.
Justin

1
@DennisJaheruddin, "Inf" yi "tam tamsayı çözümü" olarak adlandırmak biraz zor.
14'te

1
@Quincunx Hayır, herhangi bir dile izin verilir.
user80551

Yanıtlar:


7

GMP ile C ++, puan = 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) yaklaşık


Kod:

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

Ölçek:

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

Çıktı:

10.0069999695
10.0729999542
10.0360000134
9.98699998856

Nasıl çalışır:

Daha büyük çarpmalar daha fazla zaman alır, bu nedenle mümkün olduğunca çok sayıda küçük çarpma işlemi yapmak istiyoruz. Bu özellikle Python için 2^64, donanım aritmetiği ve üstünde yazılım kullandığımız sayılar için geçerlidir . Böylece, m(L)bir liste ile başlıyoruz L; garip uzunluk ise, bir sayıyı tekrar yapmak için dikkate alınır. Sonra çarpma eleman 1eleman ile -2, eleman 3ile -4böylece vb,

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]

Bu yaklaşım, donanım aritmetiğini olabildiğince uzun süre kullanmamızı sağlar ve bunu takiben verimli gmc aritmetik kütüphanesine geçeriz.

Ayrıca fac2, daha klasik bir bölme ve fethetme yaklaşımını da ele alıyoruz, burada her iki katını ayırıyoruz ve sonunda önemli bir performans artışı için onları bitiriyoruz. Buraya ekledim çünkü genellikle yaklaşık% 0,5 daha hızlı fac1.

Golf Sürümü fac1(çünkü yapabilirim), 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
GMP arka ucu bir bit kaydırma işlevi içeriyorsa, listedeki her sayıyı eşit olana kadar 2'ye bölerek ve sonunda tek bir kaydırma yaparak sayıları daha da küçük tutabilirsiniz.
Peter Taylor

Bunu nereden gmpy2mi? $ python Python 2.7.3 (varsayılan, 27 Şub 2014, 19:58:35) linux2'de [GCC 4.6.3] daha fazla bilgi için "yardım", "telif hakkı", "kredi" veya "lisans" yazın. >>> gmpy2 import mpz'den Traceback (en son çağrı son): <module> ImportError içinde "<stdin>" dosyası, satır 1, gmpy2 adlı modül yok >>>
user80551

@ user80551: code.google.com/p/gmpy (en iyi google arama sonucu) birçok farklı platform için yükleyicilere sahiptir.
alexander-brett

Golfçü versiyon için while len(L): ...bunun yerine yapamaz mıydınız while len(L)>1: ...?
user80551

Hayır: Bu döngü içindeki fonksiyon asla uzunluk 1'in altındaki listeyi almayacak ve her neyse ilk öğeye ihtiyacımız var!
alexander-brett

2

Java - 125.15 (21.400.000 / 171.000)

Ayrıca Peter Luschny'nin Github repo'sundan utanmadan kopyalandı (teşekkürler @ semi-extrinsic) ve MIT lisansı altında lisanslandı, bu, Albert Schönhage ve ark. (Luschny'nin faktöryel algoritmalar açıklama sayfasına göre ).

Algoritmayı Java'nın BigInteger'ini kullanmak ve n <20 için bir arama tablosu kullanmamak için biraz uyarladım.

BigInteger uygulaması için GMP kullanan ve Linux 3.12.4 (Gentoo) üzerinde çalışan ve 2.40GHz'de Core i7 4700MQ üzerinde çalışan gcj ile derlendi

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

İle Derleyengcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r

1

Python 3, n = 100000

Basit bir algoritma değişikliği, örnek kodu 10000 kadar artırmak için gerekliydi.

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

Açıkçası en yaratıcı cevap değil, ama gerçekten bir faktöriyel yapmanın tek yolu var ....


Lütfen skoru verin, düzenlememe bakın. Tümsek makineniz benimkinden daha iyi olduğu için muhtemelen olacaktır.
user80551

1

Perl + C, n = yaklaşık 3 milyon

Burada Perl'in çekirdek Math :: BigInt nesneleri için büyük bir hız artışı sağlayan CPAN'da bulunan Math :: BigInt :: GMP kütüphanesini kullanıyorum .

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

Bilgisayarımın muhtemelen sizinkinden biraz daha yavaş olduğunu unutmayın. Orijinal Python betiğinizi kullanarak sadece factorial(40000)10 saniyede hesaplayabilirim ; factorial(90000)çok daha uzun sürer. (Ben Ctrl + C bir dakika sonra geldi.) Donanımınız günü, Matematik :: BigInt :: GMP kullanarak, sen de faktöriyelini hesaplamak mümkün olabilir 5 milyon ya da daha fazla 10 saniyenin altında.

Fark edebileceğiniz bir şey, faktöriyel inanılmaz hızlı bir şekilde hesaplanmasına rağmen, sonucun yazdırılmasının çok yavaş olması ve orijinal hesaplamadan yaklaşık üç kat daha uzun sürmesidir. Bunun nedeni, GMP'nin ondalık gösterimden ziyade dahili bir ikili dosya kullanması ve çıktıyı yazdırmak için ikili-ondalık dönüştürme gerektirmesidir.


1
Bence GMP harici bir kaynak olarak sayılıyor. (Her şeyi asal çarpanlara ayırma ve Schönhage-Strassen çarpımını sıfırdan uygulamaktan kesinlikle çok daha kolay olmasına rağmen .)
r3mainer

3
"Dış kaynak" ın, bir veritabanındaki önceden hesaplanmış bir cevap kümesinden veya bir web hizmetinden vb.
Çözüm aramaya atıfta bulunduğunu varsayıyordum

Gıcırdayan: sıkıcı boşluklar kuralının altına düşen bir işlevi yoksa, kütüphaneler normalde harici kaynaklar olarak sayılmaz.
alexander-brett

1
Tobyink: Programınızın ne yaptığını açıklayabilir misiniz? Görünüşe göre, sadece yerleşik bir işlev kullanıyorsunuz (bfac?)
alexander-brett

Evet. Bu cevap geçersizdir, çünküMath::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)

Birçok küçük sayı grubunun göreceli çoğalma kolaylığını kullanır ve daha sonra bunları çok sayıda içeren çok sayıda çarpma ile karşılaştırır.


1

C #: 0,48 (77.000 / 160.000)

Bundan memnun değilim.

C # bu kadar yavaş mı?

Ama işte zaten benim girişim.

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

    }

N = 77000 00:00:09:8708952olduğunda hesaplanması gerekir.

Core i3-2330M @ 2.2GHz kullanarak Visual Studio dışında Release modunda çalışıyorum.

Edit: Akıllı bir şey yapmıyorum beri, bu sonucu kabul ediyorum. Belki de .NET Framework 4.5 yük ekler (veya BigInteger o kadar hızlı değil).


Lütfen skoru verin ve sadece değiln
user80551

1
zero approached byOperatörü daha güzel hale getirmek için kullanabilirsiniz ( n = ... + 1o zaman ile başlayın gibi while (0 <-- n) result *= n;)
Cthulhu

1
.NET için BigInteger muhtemelen Karatsuba veya Toom-3 gibi daha büyük sayıları çoğaltmak için algoritma uygulamadı. Öyleyse, bu Python'un nasıl daha hızlı olduğuna iyi bir örnektir.
kernigh

1

bc, skor = 0.19

Bu da benim yarışmacım "Ne kadar yavaşça çoğalırsın?"

bc olduğunu ne yazık ki oldukça yavaş "Gelişigüzel bir duyarlılıkta hesaplama dili" değil,:

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

2012 ortası MacBook Pro'mda (2.3 GHz Intel Core i7) yaklaşık 10 saniye içinde referans python cevabı 122000! Hesaplayabilir, ancak bu bc betiği sadece 23600!

Tersine 10000! python başvuru komut dosyası ile 1,5 sn alır, ancak bc komut dosyası 50 sn alır.

Ah hayatım.


1
OpenBSD bc (1) daha hızlıdır. Programınızın puanı 0.29 = 28000/98000. Yok read(), bu yüzden koştum time sed 's/read()/28000/' factorial.bc | bc.
kernigh

1

Bash: skor = 0.001206 (181/150000)

Rosettacode'dan matematik fonksiyonlarını çaldım - Uzun çarpma analiz etmedim ve optimize etmeye çalışmadım.
Algoritmayı değiştirmek veya farklı bir dizeler bölme yöntemi denemekte özgürsünüz.

#!/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
Lütfen puanı ve sadece en yüksek n'yi ekleyin
user80551

@ user80551 bitti
Emmanuel

1

Python 3, Peter Luschny tarafından geliştirilen algo: 8.25x (1 280 000/155 000)

Utanmadan Peter Luschny, kopyalanan
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm ,
"Creative Commons Attribution-ShareAlike 3.0" lisansı altında bu kodu sağlar.

Bu aslında oldukça gelişmiş bir algoritmadır. Diğer cevapların çoğunu beğenirse ve çarpmaların çoğunu 32 bit tamsayılarla gerçekleştirirse daha hızlı olabileceğinden şüpheleniyorum.

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

BigIntegers yavaş.

Keyfi hassasiyetli yüksek hızlı Java tamsayı kütüphaneleri için öneriler? : P


Kodunuzu çok iş parçacıklı hale getirmek için çalabilir miyim?
Simon Kuang

@SimonKuang Devam et. : P Ancak burada çok iş parçacıklı girişlere izin verilmiyor. Ayrıca, daha verimli bir BigInteger uygulaması kullanmak isteyebilirsiniz.
cjfaure

Mergesort-y Buna bölün ve fethet denir.
johnchen902

1

C ++ (x86_64'e özgü) - 3.0 (390000/130000)

(kolayca x86-32'ye taşınabilir, diğer mimarilere taşımak önemli bir hız kaybı anlamına gelir)

İşte benim uzun aritmetik mikro uygulaması.
Hesaplamanın kendisi 10 saniye sürer ve çıktı kolayca yazdırılabilir formdayken ( operator<<aşırı yüklemeye bakın ), yazdırmak biraz daha zaman alır.

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

Puanınızı kontrol edin. Sorunun Python 2.7 programını bilgisayarınızda çalıştırmanız gerekiyor. Bilgisayarım için programınızı derledim g++ -O2 factorial.cc -o factorialve puanları 3.90 = 382000 / 98000.
kernigh

Tuhaf, 3.9 aldım ve bu program için 3.0 aldın. Sanırım daha hızlı bilgisayarınız bir ceza. Belki de programınız rarttıkça Python'a olan avantajını kaybeder . Eğer öyleyse ve r10 saniye içinde daha yüksek bir sonuç yapabilirsiniz , o zaman puanınız düşer.
kernigh

0

Python 3: 280000/168000

Zaman Programınızı çalıştıran: arası 9.87585953253ve 10.3046453994. Programımı çalıştırma zamanı: hakkında 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)

Okuduğum cs.SE bu cevabı ve Python bunu uygulamak için denemeye karar verdi. Ancak, ben yanlışlıkla keşfetti n! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!(not: !!bir çift faktöriyel ). Bu yüzden bunu özyinelemesiz bir forma dönüştürdüm.

Yorumlar, çift faktöriyeli yeniden hesaplamaktan kaçınmaya çalıştığımı gösteriyor, ancak her değeri depolamanın, bilgisayarımın daha da yavaş çalışmasına neden olacak kadar bellek maliyetli olduğunu keşfettim. Bunu sadece gerekli olanı depolayarak geliştirebilirim.

Tuhaf bir şekilde, Python 3'te saf düz çarpımı uyguladım ve programınızdan daha iyi yapıyor: n = 16900010 saniye içinde .:

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

0

Yakut 2.1

puan = 1,80 = 176_000 / 98_000

EDIT: 1.35 = 132_000 / 98_000'den geliştirildi

GMP faktöriyel algoritmasından fikirler aldım . Bu program asal sayılar üretmek için standart kütüphaneyi kullanır. Ruby kötü bir seçimdir çünkü Ruby'de çarpma Python'dan daha yavaş görünür.

  1. Ruby 2.1'deki programım: puan = 1.80 = 176_000 / 98_000
  2. Python 2.7'de önemsiz algoritma: skor = 1 = 98_000 / 98_000
  3. Ruby 2.1'de önemsiz algoritma: skor = 0.878 = 86_000 / 98_000

Evet, ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-openbsd]GMP'ye karşı olan ikili linklerim. Ruby 2.1, büyük çarpma için GMP kullanma özelliği ekledi, ancak yine de Python 2.7'den daha yavaş görünüyor.

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 - Puan = 15.194

Referans programdaki yaklaşımın aynısı kullanılarak ...

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

Bu nedenle, indirgeme, temel ikili çarpma işlemi ve bir aralık kullanır (bu durumda, hesaplamayı Int64 yerine BigInt'te yapılmaya zorlamak için big (n) kullanarak). Bundan anladım

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

Bilgisayarımda, referans program 154000 girişi ile çalışıyor, Time: 10.041181 secçıktı alıyorum ( python ./test.pytest.py referans kodunu içeren dosyanın adıdır.


0

tcl, 13757

Cevabım tcl sınırlarını kontrol etmek.

İlk satır yalnızca bir giriş parametresi ayarlamaktır:

set n 13757

Diğerleri algoritmanın kendisidir:

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

Kodumu http://rextester.com/live/WEL36956 adresinde test ettim ; Eğer n'i büyütürsem, SIGKILL alırım; n, sahip olmadığım yerel bir tcl yorumlayıcıda büyüyebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.