Bao nhiêu bạn có thể nhanh chóng nhân lên?


12

Với lần bash Python gần đây , đây là một nỗ lực để thể hiện sức mạnh của Python. Thử thách của bạn là viết một chương trình tính giai thừa số càng cao càng tốt trong vòng 10 giây.n

Điểm của bạn sẽ là (highest n for your program on your machine)/(highest n for my program on your machine)

Quy tắc

  • Bạn phải tính toán một giải pháp số nguyên chính xác. Vì giai thừa sẽ cao hơn nhiều so với số nguyên không dấu 64 bit, nên bạn có thể sử dụng chuỗi nếu ngôn ngữ của bạn không hỗ trợ số nguyên lớn
  • Sơ hở tiêu chuẩn bị cấm. Đặc biệt, bạn không thể sử dụng bất kỳ tài nguyên bên ngoài.
  • Chỉ phần tính toán (phần này bao gồm thời gian cho bất kỳ cách giải quyết nào bằng cách sử dụng chuỗi) thêm vào tổng thời gian trung bình dưới 10 giây.
  • Chương trình đơn luồng duy nhất.
  • Bạn phải lưu trữ đầu ra ở dạng dễ in (vì việc in mất thời gian) (xem chương trình của tôi bên dưới), chuỗi, biến, mảng ký tự, v.v.

BIÊN TẬP:

  • Chương trình của bạn phải cung cấp đầu ra chính xác cho tất cả n:1 <= n <= (your highest n)

EDIT2:

  • Tôi ghét phải nói điều này một cách rõ ràng nhưng sử dụng các chức năng giai thừa tích hợp trong ngôn ngữ của bạn nằm trong các lỗ hổng tiêu chuẩn http://meta.codegolf.stackexchange.com/a/1078/8766 Xin lỗi Mathicala và Sage

Chương trình của tôi

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

Điểm số cao nhất chiến thắng. Đối với bản ghi, mã của tôi có thể quản lý n = 90000trong khoảng 9.89vài giây trên Pentium 4 3.0 GHz


EDIT: Mọi người có thể vui lòng thêm điểm thay vì chỉ n cao nhất . Chỉ cao nhất nkhông có ý nghĩa gì vì nó phụ thuộc vào phần cứng của bạn. Không thể có một tiêu chí chiến thắng khách quan nào khác. anwer của ali0sha làm điều này một cách chính xác.


Chúng ta có một người chiến thắng. Tôi đã không chấp nhận câu trả lời java /codegolf//a/26974/8766 vì đây là loại váy gần với http://meta.codegolf.stackexchange.com/a/1080/8766


1
Bạn có thể sử dụng operator.multhay cho chức năng lambda
gnibbler

1
Bit đã cho phép điều này hoạt động, nhưng giả sử tôi đọc chính xác các quy tắc thì giải pháp MATLAB này sẽ rất hay : factorial(Inf), trả về Inftrong một phần của giây.
Dennis Jaheruddin

1
@Doorknob Điều đó phù hợp với các sơ hở tiêu chuẩn.
Justin

1
@DennisJaheruddin, thật khó để nói "Inf" là một "giải pháp số nguyên chính xác".
tobyink

1
@Quincunx Không, bất kỳ ngôn ngữ nào cũng được cho phép.
user80551

Câu trả lời:


7

C ++ với GMP, điểm = 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) khoảng


Mã số:

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

Kiểm tra:

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

Đầu ra:

10.0069999695
10.0729999542
10.0360000134
9.98699998856

Làm thế nào nó hoạt động:

Phép nhân lớn hơn mất nhiều thời gian hơn, do đó chúng tôi muốn thực hiện càng nhiều phép nhân nhỏ càng tốt. Điều này đặc biệt đúng với Python khi 2^64chúng tôi sử dụng số học phần cứng ít hơn và trên đó chúng tôi sử dụng phần mềm. Vì vậy, trong m(L), chúng tôi bắt đầu với một danh sách L; nếu đó là chiều dài kỳ lạ, chúng tôi sẽ xóa một số khỏi xem xét để làm cho số đó trở lại. Sau đó, chúng tôi nhân phần tử 1với phần tử -2, phần tử 3với -4, v.v.

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]

Cách tiếp cận này đảm bảo chúng tôi sử dụng số học phần cứng càng lâu càng tốt, sau đó chúng tôi chuyển sang thư viện số học gmc hiệu quả.

Trong đó fac2, chúng tôi cũng thực hiện một cách tiếp cận phân chia và chinh phục cổ điển hơn, trong đó chúng tôi chia ra mọi bội số của 2 và chia nhỏ chúng ở cuối để tăng hiệu suất tầm thường. Tôi đã đưa nó vào đây vì nó thường nhanh hơn khoảng 0,5% fac1.

Phiên bản fac1chơi gôn của (vì tôi có thể), 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
Nếu phụ trợ GMP bao gồm chức năng bẻ khóa thì bạn có thể giữ các số nhỏ hơn bằng cách chia mỗi số trong danh sách cho đến khi nó chẵn và sau đó thực hiện một ca làm việc ở cuối.
Peter Taylor

Bạn đã gmpy2đến từ đâu? $ python Python 2.7.3 (mặc định, ngày 27 tháng 2 năm 2014, 19:58:35) [GCC 4.6.3] trên linux2 Loại "trợ giúp", "bản quyền", "tín dụng" hoặc "giấy phép" để biết thêm thông tin. >>> từ gmpy2 nhập mpz TracBack (cuộc gọi gần đây nhất): Tệp "<stdin>", dòng 1, trong <module> ImportError: Không có mô-đun nào có tên gmpy2 >>>
user80551

@ user80551: code.google.com/p/gmpy (kết quả tìm kiếm hàng đầu của google) có trình cài đặt cho nhiều nền tảng khác nhau.
alexander-brett

Đối với phiên bản chơi gôn, bạn không thể làm gì while len(L): ...thay vì while len(L)>1: ...?
user80551

Không: hàm bên trong vòng lặp đó sẽ không bao giờ lấy danh sách dưới độ dài 1 và dù sao chúng ta cũng cần phần tử đầu tiên!
alexander-brett

2

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

Cũng được sao chép một cách đáng xấu hổ từ repo Github của Peter Luschny (cảm ơn @ semi-extrinsic) và được cấp phép theo giấy phép MIT, điều này sử dụng thuật toán "bình phương nhân tố lồng nhau" theo đề xuất của Albert Schönhage et al. (theo trang mô tả thuật toán giai thừa của Luschny ).

Tôi đã điều chỉnh một chút thuật toán để sử dụng BigInteger của Java và không sử dụng bảng tra cứu cho n <20.

Được biên dịch với gcj, sử dụng GMP để triển khai BigInteger và chạy trên Linux 3.12.4 (Gentoo), trên Core i7 4700MQ với tốc độ 2.40GHz

import java.math.BigInteger;

public class PrimeSieveFactorialSchoenhage {

    private static int[] primeList, multiList;

    public static BigInteger factorial(int n) {
        int log2n = 31 - Integer.numberOfLeadingZeros(n);
        int piN = log2n < 2 ? 1 : 2 + (15 * n) / (8 * (log2n - 1));

        primeList = new int[piN];
        multiList = new int[piN];

        int len = primeFactors(n);
        return nestedSquare(len).shiftLeft(n - Integer.bitCount(n));
    }

    private static BigInteger nestedSquare(int len) {
        if (len == 0) {
            return BigInteger.ONE;
        }

        int i = 0, mult = multiList[0];

        while (mult > 1) {
            if ((mult & 1) == 1) { // is mult odd ?
                primeList[len++] = primeList[i];
            }

            multiList[i++] = mult / 2;
            mult = multiList[i];
        }
        BigInteger ns = nestedSquare(i);
        if (len <= i) {
            return ns.multiply(ns);
        }

        return product(primeList, i, len - i).multiply(ns.multiply(ns));
    }

    private static BigInteger product(int[] a, int start, int length) {
        if (length == 0) {
            return BigInteger.ONE;
        }

        int len = (length + 1) / 2;
        long[] b = new long[len];

        int i, j, k;

        for (k = 0, i = start, j = start + length - 1; i < j; i++, k++, j--) {
            b[k] = a[i] * (long) a[j];
        }

        if (i == j) {
            b[k++] = a[j];
        }

        return recProduct(b, 0, k - 1);
    }

    private static BigInteger recProduct(long[] s, int n, int m) {
        if (n > m) {
            return BigInteger.ONE;
        }
        if (n == m) {
            return BigInteger.valueOf(s[n]);
        }
        int k = (n + m) >> 1;
        return recProduct(s, n, k).multiply(recProduct(s, k + 1, m));
    }

    private static int primeFactors(int n) {
        int[] primes = new int[n < 17 ? 6 : (int) Math.floor(n / (Math.log(n) - 1.5))];
        int numPrimes = makePrimeList(n, primes);

        int maxBound = n / 2, count = 0;

        int start = indexOf(primes, 2, 0, numPrimes - 1);
        int end = indexOf(primes, n, start, numPrimes);

        for (int i = start; i < end; i++) {
            int prime = primes[i];
            int m = prime > maxBound ? 1 : 0;

            if (prime <= maxBound) {
                int q = n;
                while (q >= prime) {
                    m += q /= prime;
                }
            }

            primeList[count] = prime;
            multiList[count++] = m;
        }
        return count;
    }

    private static int indexOf(final int[] data, int value, int low, int high) {
        while (low < high) {
            int mid = (low + high) >>> 1;

            if (data[mid] < value) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }

        if (low >= data.length) {
            return low;
        }

        if (data[low] == value) {
            low++;
        }

        return low;
    }

    private static int makePrimeList(int n, int[] prime) {
        boolean[] composite = new boolean[n / 3];

        sieveOfEratosthenes(composite);

        boolean toggle = false;
        int p = 5, i = 0, j = 2;

        prime[0] = 2;
        prime[1] = 3;

        while (p <= n) {
            if (!composite[i++]) {
                prime[j++] = p;
            }
            // -- never mind, it's ok.
            p += (toggle = !toggle) ? 2 : 4;
        }

        return j; // number of primes
    }

    private static void sieveOfEratosthenes(final boolean[] composite) {
        int d1 = 8;
        int d2 = 8;
        int p1 = 3;
        int p2 = 7;
        int s1 = 7;
        int s2 = 3;
        int n = 0;
        int len = composite.length;
        boolean toggle = false;

        while (s1 < len) { // -- scan sieve
            if (!composite[n++]) { // -- if a prime is found, cancel its multiples
                int inc = p1 + p2;

                for (int k = s1; k < len; k += inc) {
                    composite[k] = true;
                }

                for (int k = s1 + s2; k < len; k += inc) {
                    composite[k] = true;
                }
            }

            if (toggle = !toggle) { // Never mind, it's ok.
                s1 += d2;
                d1 += 16;
                p1 += 2;
                p2 += 2;
                s2 = p2;
            } else {
                s1 += d1;
                d2 += 8;
                p1 += 2;
                p2 += 6;
                s2 = p1;
            }
        }
    }

    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        long nanos = System.nanoTime();
        BigInteger fact = factorial(n);
        nanos = System.nanoTime() - nanos;
        // Commented out because it takes ages to print
        //System.out.println(fact);
        System.out.println(nanos / 1e9);
    }
}

Được biên dịch vớigcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r

1

Python 3, n = 100000

Một thay đổi thuật toán đơn giản là tất cả những gì cần thiết để tăng mã mẫu lên 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")

Rõ ràng không phải là câu trả lời sáng tạo nhất, nhưng thực sự chỉ có một cách để làm một nhân tố ....


Xin vui lòng cho điểm, xem chỉnh sửa của tôi. Các vết sưng có thể là do máy của bạn tốt hơn của tôi.
user80551

1

Perl + C, n = khoảng 3 triệu

Ở đây tôi đang sử dụng thư viện Math :: BigInt :: GMP có sẵn trên CPAN, cung cấp khả năng tăng tốc độ lớn cho các đối tượng Math :: BigInt cốt lõi của Perl.

use v5.14;
use Time::HiRes 'time';
use Math::BigInt only => 'GMP';

sub factorial { Math::BigInt::->new(@_)->bfac }

my $start  = time;
my $answer = factorial( 3_000_000 );
my $end    = time;

say $answer;
say "Time: ", $end - $start, " sec";

Hãy nhớ rằng máy tính của tôi có thể chậm hơn một chút so với máy tính của bạn. Sử dụng tập lệnh Python ban đầu của bạn, tôi chỉ có thể tính toán factorial(40000)trong 10 giây; factorial(90000)mất nhiều thời gian hơn (Tôi nhấn Ctrl + C sau một phút.) Trên phần cứng của bạn, sử dụng Math :: BigInt :: GMP, bạn cũng có thể tính được giai thừa từ 5 triệu trở lên trong vòng dưới 10 giây.

Một điều bạn có thể nhận thấy là mặc dù giai thừa được tính toán cực kỳ nhanh chóng, nhưng việc in ra kết quả rất chậm, mất khoảng ba lần so với tính toán ban đầu. Điều này là do GMP sử dụng nội bộ nhị phân thay vì biểu diễn thập phân và việc in ra đòi hỏi phải chuyển đổi nhị phân sang thập phân.


1
Tôi nghĩ rằng GMP được coi là một nguồn lực bên ngoài. (Mặc dù nó chắc chắn làm cho mọi thứ trở nên dễ dàng hơn rất nhiều so với việc thực hiện phép nhân tố chínhphép nhân Schönhage-Strassen từ đầu.)
r3mainer

3
Tôi đã giả định rằng "tài nguyên bên ngoài" đề cập đến việc tìm kiếm các giải pháp từ một bộ câu trả lời được tính toán trước trong cơ sở dữ liệu hoặc dịch vụ web, v.v.
tobyink

Squeamish: các thư viện thường không được tính là tài nguyên bên ngoài trừ khi chúng có chức năng nằm dưới quy tắc sơ hở nhàm chán.
alexander-brett

1
Tobyink: bạn có thể giải thích chương trình của bạn làm gì không? có vẻ như bạn chỉ đang sử dụng chức năng tích hợp (bfac?)
alexander-brett

Vâng Câu trả lời này không hợp lệ, vì nó sử dụng phương pháp giai thừa củaMath::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)

Làm cho việc sử dụng dễ dàng nhân tương đối của nhiều nhóm số nhỏ và sau đó nhân chúng so với số lượng lớn nhân với số lượng lớn.


1

C #: 0,48 (77.000 / 160.000)

Tôi không hài lòng với điều này.

C # có chậm không?

Nhưng đây là mục của tôi.

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

    }

Khi n = 77000 thì mất 00:00:09:8708952 tính toán.

Tôi đang chạy ở chế độ Phát hành, bên ngoài Visual Studio, sử dụng Core i3-2330M @ 2.2GHz.

Chỉnh sửa: Vì tôi không làm gì thông minh, tôi chấp nhận kết quả đó. Có thể .NET Framework 4.5 bổ sung một số chi phí hoạt động (hoặc BigInteger không nhanh như vậy).


Vui lòng cho điểm và không chỉn
user80551

1
Bạn đã có thể sử dụng zero approached by toán tử để làm cho nó đẹp hơn (như bắt đầu với n = ... + 1sau đó while (0 <-- n) result *= n;)
Cthulhu

1
BigInteger cho .NET có lẽ đã không thực hiện các thuật toán để nhân số lớn hơn, như Karatsuba hoặc Toom-3. Nếu vậy, đây là một ví dụ tốt về cách Python nhanh hơn.
hạt nhân

1

bc, điểm = 0,19

Cái quái gì thế, đây là ứng cử viên của tôi cho "Bạn có thể nhân lên bao nhiêu?"

bc "Một ngôn ngữ máy tính chính xác tùy ý", nhưng không may là khá chậm:

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

Trong khoảng 10 giây trên MacBook Pro giữa năm 2012 của tôi (Intel Core i7 2,3 GHz), câu trả lời python tham chiếu có thể tính toán 122000!, Nhưng tập lệnh bc này chỉ có thể tính được 23600!.

Ngược lại 10000! mất 1,5 giây với tập lệnh tham chiếu python, nhưng tập lệnh bc mất 50 giây.

Trời ơi.


1
OpenBSD bc (1) nhanh hơn. Chương trình của bạn đạt 0,29 = 28000/98000. Không có read(), vì vậy tôi chạy time sed 's/read()/28000/' factorial.bc | bc.
hạt nhân

1

Bash: điểm = 0,001206 (181/150000)

Tôi đã đánh cắp các hàm toán học từ Rosettacode - Phép nhân dài mà tôi không phân tích cũng không cố gắng tối ưu hóa.
Bạn có thể tự do thay đổi thuật toán hoặc thử một phương pháp phân tách chuỗi khác nhau.

#!/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
Vui lòng thêm điểm số và không chỉ n cao nhất
user80551

@ user80551 đã xong
Emmanuel

1

Python 3, thuật toán nâng cao của Peter Luschny: 8,25x (1 280 000/155 000)

Sao chép một cách đáng xấu hổ từ Peter Luschny,
http://www.luschny.de/math/factorial/FastFactorialFiances.htmlm ,
người cung cấp mã này theo giấy phép "Creative Commons Attribution-ShareAlike 3.0".

Đây thực sự là một thuật toán khá tiên tiến, sử dụng một thứ gọi là "giai thừa lắc lư" và một danh sách các số nguyên tố. Tôi nghi ngờ nó có thể còn nhanh hơn nếu nó giống như nhiều câu trả lời khác và thực hiện hầu hết các phép nhân với số nguyên 32 bit.

#! /usr/bin/python3
import time
import bisect 

def Primes(n) : 
  primes = [2, 3] 
  lim, tog = n // 3, False 
  composite = [False for i in range(lim)] 

  d1 = 8; d2 = 8; p1 = 3; p2 = 7; s = 7; s2 = 3; m = -1 

  while s < lim :             # --  scan the sieve 
      m += 1                  # --  if a prime is found 
      if not composite[m] :   # --  cancel its multiples 
          inc = p1 + p2 
          for k in range(s,      lim, inc) : composite[k] = True 
          for k in range(s + s2, lim, inc) : composite[k] = True 

          tog = not tog 
          if tog: s += d2; d1 += 16; p1 += 2; p2 += 2; s2 = p2 
          else:   s += d1; d2 +=  8; p1 += 2; p2 += 6; s2 = p1 

  k, p, tog = 0, 5, False 
  while p <= n : 
      if not composite[k] : primes.append(p) 
      k += 1; 
      tog = not tog 
      p += 2 if tog else 4 

  return primes 

def isqrt(x): 
  ''' 
  Writing your own square root function
  ''' 
  if x < 0: raise ValueError('square root not defined for negative numbers') 
  n = int(x) 
  if n == 0: return 0 
  a, b = divmod(n.bit_length(), 2) 
  x = 2**(a + b) 
  while True: 
      y = (x + n // x) // 2 
      if y >= x: return x 
      x = y 

def product(s, n, m): 
  if n > m: return 1 
  if n == m: return s[n] 
  k = (n + m) // 2 
  return product(s, n, k) * product(s, k + 1, m) 

def factorialPS(n): 

  small_swing = [1,1,1,3,3,15,5,35,35,315,63,693,231,3003,429,6435,6435, 
          109395,12155,230945,46189,969969,88179,2028117,676039,16900975, 
          1300075,35102025,5014575,145422675,9694845,300540195,300540195] 

  def swing(m, primes): 
      if m < 33: return small_swing[m] 

      s = bisect.bisect_left(primes, 1 + isqrt(m)) 
      d = bisect.bisect_left(primes, 1 + m // 3) 
      e = bisect.bisect_left(primes, 1 + m // 2) 
      g = bisect.bisect_left(primes, 1 + m) 

      factors = primes[e:g] 
      factors += filter(lambda x: (m // x) & 1 == 1, primes[s:d]) 
      for prime in primes[1:s]:   
          p, q = 1, m 
          while True: 
              q //= prime 
              if q == 0: break 
              if q & 1 == 1: 
                  p *= prime 
          if p > 1: factors.append(p) 

      return product(factors, 0, len(factors) - 1) 

  def odd_factorial(n, primes): 
      if n < 2: return 1 
      return (odd_factorial(n // 2, primes)**2) * swing(n, primes) 

  def eval(n): 
      if n < 0: 
          raise ValueError('factorial not defined for negative numbers') 

      if n == 0: return 1 
      if n < 20: return product(range(2, n + 1), 0, n-2) 

      N, bits = n, n 
      while N != 0: 
          bits -= N & 1 
          N >>= 1 

      primes = Primes(n) 
      return odd_factorial(n, primes) * 2**bits 

  return eval(n)

start = time.time()
answer = factorialPS(1280000) 
print(time.time()-start)

1

Java - 10.9

n = 885000

Sáp nhập-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 chậm

Các đề xuất cho các thư viện số nguyên Java tốc độ cao chính xác tùy ý? : P


Tôi có thể đánh cắp mã của bạn để làm cho nó đa luồng không?
Simon Kuang

@SimonKuang Đi trước. : P, mặc dù các mục đa luồng không được phép ở đây. Ngoài ra, bạn có thể muốn sử dụng triển khai BigInteger hiệu quả hơn.
cjfaure

Mergesort-y Nó được gọi là chia và chinh phục.
johnchen902

1

C ++ (đặc thù x86_64) - 3.0 (390000/130000)

(dễ dàng di chuyển đến x86-32, chuyển sang các kiến ​​trúc khác ngụ ý mất tốc độ đáng kể)

Đây là việc tôi thực hiện vi mô số học dài.
Việc tính toán tự nó mất 10 giây và trong khi đầu ra ở dạng dễ in (xem phần operator<<quá tải), phải mất thêm thời gian để in.

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

Kiểm tra điểm số của bạn. Bạn cần chạy chương trình Python 2.7 của câu hỏi trên máy tính của mình. Đối với máy tính của tôi, tôi đã biên dịch chương trình của bạn g++ -O2 factorial.cc -o factorialvà nó đạt 3,90 = 382000 / 98000.
kernigh

Thật kỳ lạ, tôi có 3.9 và bạn có 3.0 cho chương trình này. Tôi đoán máy tính nhanh hơn của bạn là một hình phạt. Có lẽ chương trình của bạn mất lợi thế so với Python khi rtăng. Nếu vậy, và bạn có thể làm cao hơn rtrong 10 giây, thì điểm của bạn sẽ giảm xuống.
nhân

0

Con trăn 3: 280000/ 168000

Thời gian chạy chương trình của bạn: giữa 9.8758595325310.3046453994. Thời gian chạy chương trình của tôi: về 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)

Tôi đã đọc câu trả lời này trên cs.SE và quyết định thử thực hiện nó trong Python. Tuy nhiên, tôi vô tình phát hiện ra rằng n! = (⌊n / 2⌋)! * 2**(⌊n / 2⌋) * n!!(lưu ý: !!là nhân tử kép ). Vì vậy, tôi đã chuyển đổi nó thành một hình thức không đệ quy.

Các ý kiến ​​cho thấy nỗ lực của tôi để tránh tính toán lại nhân tử kép, nhưng tôi phát hiện ra rằng việc lưu trữ mọi giá trị là quá tốn bộ nhớ khiến nó khiến máy tính của tôi chạy chậm hơn. Tôi có thể cải thiện điều này bằng cách chỉ lưu trữ những gì cần thiết.

Thật kỳ lạ, tôi đã triển khai phép nhân thẳng ngây thơ trong Python 3 và nó hoạt động tốt hơn chương trình của bạn: n = 169000 trong 10 giây.:

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

0

Ruby 2.1

điểm = 1,80 = 176_000 / 98_000

EDIT: được cải thiện từ 1,35 = 132_000 / 98_000

Tôi lấy ý tưởng từ thuật toán giai thừa GMP . Chương trình này sử dụng thư viện tiêu chuẩn để tạo số nguyên tố. Ruby là một lựa chọn tồi vì phép nhân có vẻ chậm hơn trong Ruby so với Python.

  1. Chương trình của tôi trong Ruby 2.1: điểm = 1,80 = 176_000 / 98_000
  2. Thuật toán tầm thường trong Python 2.7: điểm = 1 = 98_000 / 98_000
  3. Thuật toán tầm thường trong Ruby 2.1: điểm = 0.878 = 86_000 / 98_000

Vâng, nhị phân của tôi ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-openbsd]liên kết chống lại GMP. Ruby 2.1 đã thêm một tính năng để sử dụng GMP cho phép nhân lớn, nhưng nó vẫn có vẻ chậm hơn 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

Julia - Điểm = 15.194

Sử dụng cách tiếp cận chính xác giống như phương pháp của chương trình tham chiếu ... đó là,

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

Vì vậy, nó sử dụng giảm, hoạt động nhân nhị phân cơ bản và một phạm vi (trong trường hợp này, sử dụng big (n) để buộc phép tính được thực hiện trong BigInt thay vì Int64). Từ đây, tôi nhận được

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

Trên máy tính của tôi, với chương trình tham chiếu đang chạy với đầu vào 154000, tôi nhận được Time: 10.041181 secđầu ra (chạy bằng cách sử dụng python ./test.py, trong đó test.txt là tên của tệp chứa mã tham chiếu)


0

tcl, 1357

Câu trả lời của tôi là kiểm tra giới hạn của tcl.

Dòng đầu tiên chỉ để đặt tham số đầu vào:

set n 13757

Các thuật toán khác là chính nó:

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

Tôi đã kiểm tra mã của mình trên http://rextester.com/live/WEL36956 ; Nếu tôi làm cho n lớn hơn, tôi nhận được SIGKILL; n có thể trở nên lớn hơn trên một trình thông dịch tcl cục bộ mà tôi không có.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.