얼마나 빨리 곱할 수 있습니까?


12

최근 Python bashing 과 함께 Python의 장점을 보여 주려는 시도가 있습니다. 당신의 도전은 n10 초 안에 가능한 한 많은 수의 계승을 계산하는 프로그램을 작성하는 것 입니다.

당신의 점수는 (highest n for your program on your machine)/(highest n for my program on your machine)

규칙

  • 정확한 정수 솔루션을 계산해야합니다. 계승은 부호없는 64 비트 정수에 들어갈 수있는 것보다 훨씬 높으므로 언어가 큰 정수를 지원하지 않으면 문자열을 사용할 수 있습니다
  • 표준 허점은 금지되어 있습니다. 특히 외부 리소스를 사용할 수 없습니다.
  • 계산 부분 (문자열을 사용하는 해결 방법에 대한 시간 포함) 만 총 시간에 평균 10 초 미만이어야합니다.
  • 단일 스레드 프로그램 만.
  • 출력은 쉽게 인쇄 할 수있는 형식 (인쇄에 시간이 걸리므로) (아래의 내 프로그램 참조), 문자열, 변수, 문자 배열 등으로 저장해야합니다.

편집하다:

  • 여러분의 프로그램은 모두 정확한 출력을 제공해야합니다 n:1 <= 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의 anwer 가 올바르게 수행합니다.n


우리는 승자가 있습니다. http://meta.codegolf.stackexchange.com/a/1080/8766에 가까운 치마의 종류이므로 Java 답변 /codegolf//a/26974/8766 을 수락하지 않았습니다 .


1
operator.mul람다 함수 대신 사용할 수 있습니다
gnibbler

1
비트이 작품을 놀라게하지만 제대로이 MATLAB 솔루션 좋을 텐데 규칙을 읽어 가정하면 factorial(Inf), 반환 Inf순식간에 이루어집니다.
Dennis Jaheruddin

1
@Doorknob 표준 허점에 맞습니다.
Justin

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

파이썬 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

작동 방식 :

곱셈이 클수록 시간이 오래 걸리므로 가능한 한 작은 곱셈을 원합니다. 이것은 특히 2^64우리가 하드웨어 산술을 사용 하는 것 보다 적은 수의 소프트웨어를 사용하는 파이썬에서 사실입니다 . 그래서 m(L)우리는리스트로 시작한다 L. 길이가 홀수이면 고려에서 하나의 숫자를 제거하여 다시 만듭니다. 그런 다음 element 1와 element -2, element 3with -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 Traceback (가장 최근 호출) : 파일 "<stdin>", <module>의 행 1 ImportError : 이름이 gmpy2 인 모듈 없음 >>>
user80551

@ user80551 : code.google.com/p/gmpy (최고의 Google 검색 결과)에는 다양한 플랫폼을위한 설치 프로그램이 있습니다.
alexander-brett

골프 버전의 경우 while len(L): ...대신에 할 수 없었 while len(L)>1: ...습니까?
user80551

아니오 : 해당 루프 내부의 함수는 길이 1 아래의 목록을 절대로 사용하지 않으며 어쨌든 첫 번째 요소가 필요합니다!
alexander-brett

2

자바-125.15 (21,400,000 / 171,000)

또한 Peter Luschny의 Github 저장소 (@ semi-extrinsic 덕분에) 에서 뻔뻔스럽게 복사 하고 MIT 라이센스에 따라 라이센스를 부여한이 기술은 Albert Schönhage et al. (Luschny의 계승 알고리즘 설명 페이지 에 따름 ).

Java의 BigInteger를 사용하고 n <20에 대한 조회 테이블을 사용하지 않도록 알고리즘을 약간 조정했습니다.

BigInteger 구현에 GMP를 사용하고 2.40GHz의 Core i7 4700MQ에서 Linux 3.12.4 (Gentoo)에서 실행되는 gcj로 컴파일되었습니다.

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

파이썬 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

1

Perl + C, n = 약 3 백만

여기서는 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 초 이내에 5 백만 이상의 계승을 계산할 수 있습니다 .

계승이 엄청나게 빠르게 계산 되더라도 결과를 인쇄하는 것은 원래 계산보다 약 3 배 더 오래 걸리는 점이 매우 느립니다. GMP는 내부적으로 십진 표현이 아닌 이진을 사용하기 때문에 출력하려면 이진에서 십진으로 변환해야합니다.


1
GMP는 외부 리소스로 간주됩니다. ( 처음부터 소인수 분해Schönhage-Strassen 곱셈 을 구현하는 것보다 훨씬 쉬운 일이 될 수는 있지만 )
r3mainer

3
"외부 리소스"는 데이터베이스 또는 웹 서비스 등에서 미리 계산 된 답변 세트에서 솔루션을 찾는 것을 의미한다고 가정했습니다.
tobyink

Squeamish : 라이브러리는 지루한 허점 규칙에 해당하는 기능이 없으면 일반적으로 외부 리소스로 계산되지 않습니다.
alexander-brett

1
Tobyink : 프로그램의 기능을 설명 할 수 있습니까? 내장 함수 (bfac?)를 사용하고있는 것 같습니다
alexander-brett

예. 그것은 사용하기 때문에이 답변은 무효 의 계승 방법Math::BigInt
14mRh4X0r을

1

파이썬 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다음 수행 while (0 <-- n) result *= n;)
크 툴루

1
.NET 용 BigInteger는 Karatsuba 또는 Toom-3과 같이 더 큰 수를 곱하는 알고리즘을 구현하지 않았을 것입니다. 그렇다면, 이것은 파이썬이 더 빠른 방법의 좋은 예입니다.
kernigh

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 초 안에 참조 파이썬 답변은 122000을 계산할 수 있지만이 bc 스크립트는 23600을 계산할 수 있습니다!

반대로 10000! 파이썬 참조 스크립트에서는 1.5 초가 걸리지 만 bc 스크립트는 50 초가 걸립니다.

이런.


1
OpenBSD bc (1)이 더 빠릅니다. 프로그램 점수는 0.29 = 28000/98000입니다. 없습니다 read(), 그래서 나는 달렸다 time sed 's/read()/28000/' factorial.bc | bc.
kernigh

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
최고 n뿐만 아니라 점수를 추가하십시오
user80551

@ user80551 끝났습니다
Emmanuel

1

Python 3, Peter Luschny의 고급 알고리즘 : 8.25x (1280 000/155 000)

Peter Luschny (
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm )
에서 " Creating Commons Attribution-ShareAlike 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

자바-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 정수 라이브러리에 대한 권장 사항? :피


멀티 스레드로 만들기 위해 코드를 훔칠 수 있습니까?
Simon Kuang

@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 프로그램을 실행해야합니다. 내 컴퓨터를 위해, 나는 당신의 프로그램을 컴파일 g++ -O2 factorial.cc -o factorial하고 점수 3.90 = 382,000 / 98000.
kernigh

이상하게도 나는 3.9를 얻었고 당신은이 프로그램에 대해 3.0을 얻었습니다. 당신의 빠른 컴퓨터가 형벌 인 것 같습니다. 아마도 귀하의 프로그램은 파이썬에 비해 장점이 없어 질 것 r입니다. 그렇다면 r10 초 안에 더 높게 할 수 있으면 점수가 내려갑니다.
kernigh

0

파이썬 3 : 280000/168000

시간은 당신의 프로그램을 실행 : 사이 9.8758595325310.3046453994. 내 프로그램을 실행하는 시간 : about 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 에서이 답변을 읽고 파이썬으로 구현하기로 결정했습니다. 그러나, 나는 우연히 발견 n! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!(참고 : !!는 IS 이중 계승 ). 그래서 나는 이것을 비 재귀 형식으로 변환했습니다.

의견은 이중 계승을 다시 계산하지 않으려는 시도를 보여 주지만 모든 값을 저장하면 메모리가 너무 많이 소모되어 컴퓨터가 더 느리게 실행된다는 것을 알았습니다. 필요한 것만 저장하여이를 개선 할 수 있습니다.

이상하게도, 나는 Python 3에서 순진한 곱셈을 구현 n = 169000했으며 10 초 안에 프로그램보다 더 좋습니다 .

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

0

루비 2.1

점수 = 1.80 = 176_000 / 98_000

편집 : 1.35 = 132_000 / 98_000 에서 개선되었습니다.

나는 GMP factorial algorithm 에서 아이디어를 얻었다. 이 프로그램은 표준 라이브러리를 사용하여 소수를 생성합니다. 루비는 파이썬보다 곱셈이 느리게 보이기 때문에 나쁜 선택입니다.

  1. Ruby 2.1의 내 프로그램 : score = 1.80 = 176_000 / 98_000
  2. Python 2.7의 간단한 알고리즘 : score = 1 = 98_000 / 98_000
  3. 루비 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)

참조 프로그램은 154,000의 입력을 실행하는 컴퓨터,에, 나는 수 Time: 10.041181 sec(사용하여 런타임 출력을 python ./test.pytest.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.