你能快速繁殖多少?


12

随着最近对Python的 猛烈抨击,这是尝试展示Python的优势。您面临的挑战是编写一个程序,n在10秒钟内计算尽可能高的阶乘。

您的分数将是 (highest n for your program on your machine)/(highest n for my program on your machine)

规则

  • 您必须计算一个精确的整数解。由于阶乘将远远大于64位无符号整数所能容纳的整数,因此如果您的语言不支持大整数,则可以使用字符串
  • 禁止出现标准漏洞。特别是,您不能使用任何外部资源。
  • 仅计算部分(这包括使用字符串的任何变通方法所花费的时间)平均总时间应少于10秒。
  • 仅单线程程序。
  • 您必须以易于打印的形式存储输出(因为打印需要时间)(请参见下面的程序),字符串,变量,字符数组等。

编辑:

  • 您的程序必须为所有程序提供正确的输出n1 <= n <= (your highest n)

编辑2:


我的程序

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

最高分获胜。作为记录,我的代码可以n = 900009.89奔腾4 3.0 GHz上在几秒钟内完成管理


编辑:能否每个人都请添加分数,而不只是最高的n。仅仅最高n的本身就没有意义,因为它取决于您的硬件。否则,不可能有一个客观的获胜标准。ali0sha的答案正确执行了此操作。


我们有一个赢家。我不接受Java答复/codegolf//a/26974/8766,因为它有点像http://meta.codegolf.stackexchange.com/a/1080/8766的裙子


1
您可以使用operator.mul代替lambda函数的方式
gnibbler,2014年

1
比特让这个工作可行,但是假设我正确地阅读了规则,那么这个MATLAB解决方案将是不错的:factorial(Inf)Inf在不到一秒钟的时间内就会返回。
丹尼斯·贾赫鲁丁

1
@Doorknob适合标准漏洞。
贾斯汀

1
@DennisJaheruddin,将“ Inf”称为“精确整数解”有点麻烦。
tobyink 2014年

1
@Quincunx不,可以使用任何语言。
user80551 2014年

Answers:


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我们使用硬件算术的次数更少,而我们使用软件的次数更高。因此,在中m(L),我们从一个列表开始L;如果长度是奇数,我们会从考虑中删除一个数字,使之再次相等。然后我们将element 1与element -2,element 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直到其为偶数,然后最后进行单个移位,来使数字更小。
彼得·泰勒

你从哪里来gmpy2的?$ python Python 2.7.3(默认值,2014年2月27日,19:58:35)linux2上的[GCC 4.6.3]键入“ help”,“ copyright”,“ credits”或“ license”以获取更多信息。>>>从gmpy2导入mpz追溯(最近一次调用):文件“ <stdin>”,在<module>中的第1行,I​​mportError:没有名为gmpy2的模块>>>
user80551

@ user80551:code.google.com/p/gmpy(谷歌搜索结果排名第一)具有适用于许多不同平台的安装程序。
亚历山大·布雷特

对于高尔夫版本,您不能while len(L): ...代替while len(L)>1: ...吗?
user80551 2014年

否:该循环内的函数永远不会接受长度小于1的列表,无论如何我们需要第一个元素!
亚历山大·布雷特

2

爪哇-125.15(21,400,000 / 171,000)

同样从Peter Luschny的Github存储库(感谢@ semi-extrinsic)中无耻地复制并获得MIT许可,它使用了AlbertSchönhage等人提出的“素数分解嵌套平方”算法。(根据Luschny的阶乘算法说明页面)。

我稍微修改了该算法以使用Java的BigInteger,并且不使用n <20的查找表。

与gcj编译,后者使用GMP进行BigInteger实施,并在Linux 3.12.4(Gentoo)上,在2.40GHz的Core i7 4700MQ上运行

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,2014年

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

显然这不是最有创意的答案,但实际上只有一种方法可以做阶乘...。


请给分数,看我的编辑。该隆起很可能是因为你的机器比我的好。
user80551 2014年

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)需要更长的时间。(我在一分钟后按Ctrl + C。)在您的硬件上,使用Math :: BigInt :: GMP,很可能能够在10秒内计算500万或更多的阶乘。

您可能会注意到的一件事是,尽管阶乘的计算速度非常快,但打印结果却非常缓慢,所需时间比原始计算长三倍。这是因为GMP在内部使用二进制而不是十进制表示,并且打印出来需要将二进制转换为十进制。


1
我认为GMP可以算作外部资源。(虽然它确实使事情了很多的地狱比实现更容易因式分解Schönhage-Strassen的乘法从头开始。)
r3mainer

3
我假设“外部资源”是指从数据库或Web服务等中预先计算的一组答案中查找解决方案
。– tobyink

吱吱作响:库通常不算作外部资源,除非它们具有属于无聊漏洞规则的功能。
亚历山大·布雷特

1
Tobyink:您能解释一下程序的作用吗?看起来您只是在使用内置函数(bfac?)
亚历山大·

对。该答案无效,因为它使用了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 = ... + 1while (0 <-- n) result *= n;
Cthulhu 2014年

1
适用于.NET的BigInteger可能未实现用于将大数相乘的算法,例如Karatsuba或Toom-3。如果是这样,那么这就是Python更快的一个很好的例子。
kernigh 2014年

1

公元前,分数= 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
kernigh 2014年

1

重击:分数= 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
请添加分数,而不仅仅是最高分数
user80551

@ user80551完成
伊曼纽尔(Emmanuel)

1

Python 3,Peter Luschny的高级算法:8.25倍(1280 000/155 000)

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

BigIntegers很慢。

对任意精度高速Java整数库的建议?:P


我可以窃取您的代码以使其成为多线程吗?
西蒙匡2014年

@SimonKuang继续。:P但是,这里不允许多线程条目。另外,您可能想使用更有效的BigInteger实现。
cjfaure 2014年

Mergesort-y称为分而治之。
johnchen902 '18

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程序。对于我的计算机,我使用编译了您的程序,g++ -O2 factorial.cc -o factorial它的得分为3.90 = 382000 /98000。–
kernigh

很奇怪,我为此程序获得3.9,而您获得3.0。我想您的速度更快的计算机是一种惩罚。随着程序的r增加,您的程序也许会失去其相对于Python的优势。如果是这样,那么您可以r在10秒内提高得分,那么您的分数就会下降。
kernigh

0

Python 3:280000/168000

运行程序的时间:9.87585953253和之间10.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!!(请注意:!!double factorial)。因此,我将其转换为非递归形式。

注释显示了我为避免重新计算双阶乘数而进行的尝试,但是我发现存储每个值的内存成本过高,导致我的计算机运行得更慢。我可以通过仅存储需要的内容来改进它。

奇怪的是,我在Python 3中实现了朴素的直接乘法,它比您的程序要好:n = 169000在10秒内:

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是一个不好的选择,因为在Ruby中乘法似乎比在Python中慢。

  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)强制在BigInt而不是Int64中进行计算)。由此,我得到

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可能会变得更大,而我没有。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.