どれだけ素早く増殖できますか?


12

最近のPython バッシングで、Pythonの強みを示す試みがあります。あなたの課題は、n10秒以内にできるだけ大きな数の階乗を計算するプログラムを書くことです。

あなたのスコアは (highest n for your program on your machine)/(highest n for my program on your machine)

ルール

  • 正確な整数解を計算する必要があります。階乗は64ビットの符号なし整数に収まるものよりもはるかに高いため、言語が大きな整数をサポートしていない場合は文字列を使用できます
  • 標準的な抜け穴は禁止されています。特に、外部リソースは使用できません。
  • 計算部分(これには文字列を使用した回避策の時間が含まれます)のみが合計時間に追加され、平均で10秒未満になります。
  • シングルスレッドプログラムのみ。
  • 出力は簡単に印刷可能な形式(印刷に時間がかかるため)(以下のプログラムを参照)、文字列、変数、文字配列などに保存する必要があります。

編集:

  • プログラムは、すべてに対して正しい出力を提供する必要がありますn1 <= n <= (your highest n)

EDIT2:


私のプログラム

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

最高スコアが勝ちます。記録のために、私のコードはPentium 4 3.0 GHz n = 90000で約9.89数秒で管理できます


編集:誰もが最高のnではなくスコアを追加してください。ハードウェアに依存するため、最高のものだけでは意味がありません。そうでなければ、客観的な勝利基準を持つことは不可能です。ali0shaの答えnはこれを正しく行います。


勝者がいます。http://meta.codegolf.stackexchange.com/a/1080/8766に近いスカートのようなJavaの答え/codegolf//a/26974/8766を受け入れませんでした


1
あなたは使用することができますoperator.mulラムダ関数の代わりに
gnibbler

1
これは少し驚きましたが、ルールを正しく読んだと仮定すると、このMATLABソリューションは素晴らしいでしょう:factorial(Inf)、一瞬Infで戻ります。
デニスジャヘルディン

1
@Doorknob標準の抜け穴に収まる。
ジャスティン

1
@DennisJaheruddin、「Inf」を「正確な整数解」と呼ぶのは少しばかりです。
tobyink

1
@Quincunxいいえ、すべての言語が許可されています。
user80551

回答:


7

GMPを使用したC ++、スコア= 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)


コード:

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

テスト:

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

出力:

10.0069999695
10.0729999542
10.0360000134
9.98699998856

使い方:

大きな乗算はより多くの時間を要するため、できるだけ多くの小さな乗算を実行したいと考えています。これは、Pythonで特に当てはまり2^64ます。Pythonでは、ハードウェア算術演算を使用する数が少なく、それ以上ではソフトウェアを使用します。したがって、ではm(L)、リストから始めLます。長さが奇数の場合は、考慮から1つの数値を削除して、さらに均等にします。次に、要素1と要素-2、要素3-4などを乗算します。

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]

このアプローチにより、可能な限りハードウェア演算を使用し、その後、効率的なgmc算術ライブラリーに切り替えます。

fac2、より古典的な分割統治アプローチも採用しています。このアプローチでは、2の倍数ごとに分割し、最後にビットシフトして、些細なパフォーマンスを向上させます。通常は0.5%ほど高速であるため、ここに含めましたfac1

ゴルフ版fac1(できるから)、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バックエンドにビットシフト関数が含まれている場合、リスト内の各数値を偶数になるまで2で除算し、最後に1回シフトすることで、数値をさらに小さく保つことができます。
ピーターテイラー

どこgmpy2から来ましたか?$ python Python 2.7.3(デフォルト、2014年2月27日、19:58:35)[GCC 4.6.3] on linux2詳細については、「help」、「copyright」、「credits」または「license」と入力してください。>>> gmpy2インポートmpzトレースバックから(最後の最後の呼び出し):ファイル「<stdin>」、1行目、<module> ImportError:gmpy2という名前のモジュールなし>>>
user80551

@ user80551:code.google.com/p/gmpy(トップのGoogle検索結果)には、さまざまなプラットフォーム用のインストーラーがあります。
アレクサンダーブレット

ゴルフバージョンでは、while len(L): ...代わりにできませんwhile len(L)>1: ...か?
user80551

いいえ:そのループ内の関数は、長さ1未満のリストを取ることはありません。とにかく最初の要素が必要です!
アレクサンダー・ブレット

2

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

また、Peter LuschnyのGithubリポジトリ(@ semi-extrinsicに感謝します)から恥知らずにコピーされ、MITライセンスの下でライセンスされています。(Luschnyの階乗アルゴリズムの説明ページによると)。

JavaのBigIntegerを使用し、n <20のルックアップテーブルを使用しないようにアルゴリズムをわずかに適合させました。

BigIntegerの実装にGMPを使用するgcjでコンパイルされ、2.40 GHzのCore i7 4700MQでLinux 3.12.4(Gentoo)で実行されました

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

コンパイルgcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r

1

Python 3、n = 100000

サンプルコードを10000上げるために必要なのは、単純なアルゴリズムの変更だけでした。

import time

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

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

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

明らかに最も創造的な答えではありませんが、階乗を行う方法は実際には1つしかありません。


スコアを与えてください、私の編集を参照してください。バンプは、あなたのマシンが私より優れているためでしょう。
user80551

1

Perl + C、n =約300万

ここでは、CPANで利用可能なMath :: BigInt :: GMPライブラリを使用しています。これにより、PerlのコアMath :: BigIntオブジェクトの速度が大幅に向上します。

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

私のコンピューターはおそらくあなたのものよりもかなり遅いことに注意してください。オリジナルのPythonスクリプトを使用するとfactorial(40000)、10秒でしか計算できません。factorial(90000)時間がかかります。(1分後にCtrl + Cを押します。)ハードウェアで、Math :: BigInt :: GMPを使用すると、10秒以内に500万以上の階乗を計算できる場合があります。

お気づきかもしれませんが、階乗は非常に高速に計算されますが、結果の出力は非常に遅く、元の計算の約3倍の時間がかかります。これは、GMPが内部で10進表現ではなくバイナリを使用し、それを印刷するにはバイナリから10進数への変換が必要だからです。


1
GMPは外部リソースとしてカウントされると思います。(確かに、素因数分解シェーン
ハージュ

3
「外部リソース」とは、データベースやWebサービスなどで事前に計算された一連の回答からソリューションを検索することを指すと想定していました。
tobyink

Squeamish:ライブラリは、退屈な抜け穴のルールに該当する機能がない限り、通常外部リソースとしてカウントされません。
アレクサンダー・ブレット

1
Tobyink:あなたのプログラムが何をするのか説明できますか?組み込み関数(bfac?)を使用しているように見える
alexander-brett

うん。それは使用していますので、この答えは、無効であるの階乗方法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)

小さい数の多数のグループの乗算の比較的容易さを利用し、巨大な数を伴う多数の乗算と比較してそれらを乗算します。


1

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

私はこれに満足していません。

C#は遅いですか?

とにかくここに私のエントリーがあります。

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:8708952、計算ます。

Core i3-2330M @ 2.2GHzを使用して、Visual Studioの外部でリリースモードで実行しています。

編集:私は何もインテリジェントなことをしていないので、私はその結果を受け入れます。たぶん、.NET Framework 4.5にはオーバーヘッドがあります(またはBigIntegerはそれほど高速ではありません)。


スコアだけでなく、与えてくださいn
user80551

1
zero approached by演算子を使用して、それをよりきれいにすることができます(n = ... + 1その後、doで開始するなどwhile (0 <-- n) result *= n;
クトゥルフ

1
BigInteger for .NETは、おそらくKaratsubaやToom-3のような大きな数を乗算するアルゴリズムを実装していませんでした。そうである場合、これはPythonの高速化の良い例です。
カーニグ

1

bc、スコア= 0.19

一体何だ、「どれだけゆっくりと増殖できるのか」に対する私の候補です。

bc 「任意精度の計算機言語」ですが、残念ながらかなり遅いです:

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

2012年中頃のMacBook Pro(2.3 GHz Intel Core i7)で約10秒で、参照Pythonの答えは122000!を計算できますが、このbcスクリプトは23600!しか計算できません。

逆に10000!Pythonリファレンススクリプトでは1.5秒かかりますが、bcスクリプトでは50秒かかります。

まあ。


1
OpenBSD bc(1)は高速です。プログラムのスコアは0.29 = 28000/98000です。ないread()ので、走りましたtime sed 's/read()/28000/' factorial.bc | bc
カーニグ

1

Bash:スコア= 0.001206(181/150000)

Rosettacodeから数学関数を盗みました-長い乗算 は分析も最適化も試みませんでした。
アルゴリズムを変更したり、別の文字列の分割方法を試してみてください。

#!/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
最高のnだけでなく、スコアを追加してください
user80551

@ user80551完了です
エマニュエル

1

Python 3、高度なアルゴリズム、Peter Luschny:8.25x(1 280 000/155 000)

Peter Luschnyから恥知らずにコピー、http: //www.luschny.de/math/factorial/FastFactorialFunctions.htm

「クリエイティブ・コモンズ表示-継承3.0」ライセンスの下でこのコードを提供します。

これは実際には非常に高度なアルゴリズムで、「スイング階乗」と呼ばれるものと素数のリストを使用します。他の多くの回答と同様に、32ビット整数でほとんどの乗算を実行すると、さらに高速になると思います。

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

BigInteger遅いです。

任意精度の高速Java整数ライブラリの推奨事項 :P


コードを盗んでマルチスレッドにできますか?
サイモンクアン

@SimonKuangどうぞ。:Pただし、ここではマルチスレッドエントリは許可されていません。また、より効率的なBigInteger実装を使用することもできます。
cjfaure

Mergesort-y分割統治と呼ばれます。
johnchen902

1

C ++(x86_64固有)-3.0(390000/130000)

(x86-32への移植が容易で、他のアーキテクチャへの移植は大幅な速度低下を意味します)

これは、長い算術演算の私の独自のマイクロ実装です。
計算自体は10秒かかり、出力が簡単に印刷可能な形式(operator<<オーバーロードを参照)である間、印刷にはさらに時間がかかります。

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

スコアを確認してください。質問のPython 2.7プログラムをコンピューターで実行する必要があります。私のコンピューターでは、プログラムをコンパイルして3.90 = 382000/98000をg++ -O2 factorial.cc -o factorial記録しました。
kernigh

奇妙なことに、私は3.9を得て、このプログラムで3.0を得ました。高速なコンピュータはペナルティになると思います。おそらく、あなたのプログラムはPythonに比べて利点が失われますr。もしそうなら、そしてあなたはr10秒でより高いことをすることができ、あなたのスコアは下がります。
カーニグ

0

Python 3:280000/168000

あなたのプログラムを実行している時間:間9.8758595325310.3046453994。プログラムの実行時間:について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)

はcs.SEでこの回答を読み、Pythonで実装することを決めました。しかし、私は誤ってそれを発見しましたn! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!(注:!!二重階乗です)。そこで、それを非再帰的な形式に変換しました。

コメントは、二重階乗の再計算を回避しようとする試みを示していますが、すべての値を保存するとメモリのコストがかかりすぎて、コンピューターの実行がさらに遅くなることがわかりました。必要なものだけを保存することでこれを改善できます。

奇妙なことに、Python 3で単純な乗算を実装しましたが、プログラムよりも優れています:n = 16900010秒で。

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

0

Ruby 2.1

スコア= 1.80 = 176_000 / 98_000

編集:1.35 = 132_000 / 98_000から改善

GMP階乗法アルゴリズムからアイデアを取りました。このプログラムは、標準ライブラリを使用して素数を生成します。Rubyの乗算はPythonよりも遅いように見えるため、Rubyは悪い選択です。

  1. Ruby 2.1での私のプログラム:スコア= 1.80 = 176_000 / 98_000
  2. Python 2.7の自明なアルゴリズム:スコア= 1 = 98_000 / 98_000
  3. Ruby 2.1の簡単なアルゴリズム:スコア= 0.878 = 86_000 / 98_000

はい、ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-openbsd]GMPに対するリンクの私のバイナリ。Ruby 2.1は大規模な乗算にGMPを使用する機能を追加しましたが、それでも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

ジュリア-スコア= 15.194

参照プログラムとまったく同じアプローチを利用します...つまり、

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

したがって、reduce、基本的なバイナリ乗算演算、および範囲を使用します(この場合、big(n)を使用して、Int64ではなくBigIntで計算を強制的に実行します)。これから、私は得る

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

コンピューターで、154000の入力で実行されている参照プログラムで、Time: 10.041181 sec出力を取得します(を使用して実行しますpython ./test.py。ここで、test.pyは参照コードを含むファイルの名前です)


0

tcl、13757

私の答えは、tclの制限を確認することです。

最初の行は、入力パラメーターを設定することのみです。

set n 13757

その他はアルゴリズム自体です:

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

http://rextester.com/live/WEL36956でコードをテストしました。nを大きくすると、SIGKILLが得られます。私が持っていないローカルtclインタプリタでnが大きくなる可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.