คุณสามารถคูณได้เร็วแค่ไหน?


12

ด้วยการทุบตีPython เมื่อเร็ว ๆนี้นี่เป็นความพยายามที่จะแสดงจุดแข็งของ Python ความท้าทายของคุณคือการเขียนโปรแกรมที่คำนวณแฟคทอเรียลของจำนวนที่สูงที่สุดเท่าที่จะทำได้ภายใน 10 วินาทีn

คะแนนของคุณจะเป็น (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:

  • ฉันเกลียดที่จะพูดแบบนี้อย่างชัดเจน แต่การใช้ฟังก์ชันแฟกทอเรียลในตัวของคุณตกอยู่ภายใต้ช่องโหว่มาตรฐานhttp://meta.codegolf.stackexchange.com/a/1078/8766 ขออภัย Mathematica และ Sage

โปรแกรมของฉัน

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 = 90000ใน9.89เวลาประมาณไม่กี่วินาทีใน Pentium 4 3.0 GHz


แก้ไข: Can ทุกคนโปรดเพิ่มคะแนนมากกว่าแค่สูงสุดn คนที่สูงที่สุดnไม่มีความหมายด้วยตัวเองเพราะมันขึ้นอยู่กับฮาร์ดแวร์ เป็นไปไม่ได้ที่จะมีเกณฑ์การชนะอย่างอื่น แอนต์เออร์ของ ali0shaทำสิ่งนี้อย่างถูกต้อง


เรามีผู้ชนะ ฉันไม่ยอมรับคำตอบจาวา/codegolf//a/26974/8766เพราะมันเป็นกระโปรงใกล้กับhttp://meta.codegolf.stackexchange.com/a/1080/8766


1
คุณสามารถใช้operator.mulแทนฟังก์ชัน lambda ได้
gnibbler

1
บิตแปลกใจกับงานนี้ แต่ถ้าฉันอ่านกฎอย่างถูกต้องโซลูชัน MATLAB นี้น่าจะดี: factorial(Inf)ผลตอบแทนInfในเสี้ยววินาที
Dennis Jaheruddin

1
@Dorknob ที่เหมาะกับช่องโหว่มาตรฐาน
Justin

1
@DennisJaheruddin มันเป็นเรื่องที่ค่อนข้างยืดยาวที่จะอ้างถึง "Inf" ในฐานะ "วิธีแก้ปัญหาจำนวนเต็ม"
tobyink

1
@Quincunx ไม่อนุญาตให้ใช้ภาษาใดก็ได้
user80551

คำตอบ:


7

C ++ พร้อม GMP, คะแนน = 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; หากมันมีความยาวคี่เราจะลบตัวเลขหนึ่งออกจากการพิจารณาเพื่อให้มันกลับมาอีกครั้ง จากนั้นเราก็คูณองค์ประกอบ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เราใช้วิธีแบ่งและพิชิตแบบคลาสสิกมากขึ้นเช่นกันโดยที่เราแบ่งออกเป็นสองส่วนและทุกบิตในตอนท้ายเพื่อเพิ่มประสิทธิภาพเล็กน้อย เราได้รวมไว้ที่นี่เพราะมันเป็นปกติประมาณ 0.5% fac1เร็วกว่า

เวอร์ชัน Golfed ของ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 จนกระทั่งมันเป็นเลขคู่แล้วทำกะเดียวในตอนท้าย
Peter Taylor

คุณgmpy2มาจากไหน $ python Python 2.7.3 (ค่าเริ่มต้น, 27 ก.พ. 2014, 19:58:35) [GCC 4.6.3] บน linux2 พิมพ์ "help", "ลิขสิทธิ์", "credit" หรือ "license" สำหรับข้อมูลเพิ่มเติม >>> จาก gmpy2 import mpz Traceback (การโทรล่าสุดครั้งล่าสุด): ไฟล์ "<stdin>", บรรทัด 1, ใน <module> ImportError: ไม่มีโมดูลชื่อ gmpy2 >>>
user80551

@ user80551: code.google.com/p/gmpy (ผลการค้นหา google อันดับต้น ๆ ) มีโปรแกรมติดตั้งสำหรับแพลตฟอร์มต่าง ๆ มากมาย
alexander-brett

สำหรับเวอร์ชั่น golfed คุณไม่สามารถทำwhile len(L): ...แทนได้while len(L)>1: ...หรือไม่?
user80551

ไม่: ฟังก์ชั่นภายในลูปนั้นจะไม่ใช้รายการด้านล่างความยาว 1 และเราต้องการองค์ประกอบแรก!
alexander-brett

2

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

คัดลอกมาจากrepo ปีเตอร์ Luschny ของ repith Github (ขอบคุณ @ กึ่ง extrinsic) และได้รับใบอนุญาตภายใต้ใบอนุญาต MIT นี้ใช้อัลกอริทึม (ตามหน้าคำอธิบายอัลกอริธึมแบบ Luschny ของ)

ฉันปรับอัลกอริทึมเล็กน้อยเพื่อใช้ BigInteger ของ Java และไม่ใช้ตารางการค้นหาสำหรับ n <20

คอมไพล์ด้วย gcj ซึ่งใช้ GMP สำหรับการติดตั้ง BigInteger และรันบน Linux 3.12.4 (Gentoo) บน Core i7 4700MQ ที่ 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);
    }
}

คอมไพล์ด้วยgcj -O3 --main=PrimeSieveFactorialSchoenhage PrimeSieveFactorialSchoenhage.java -o pf_nest_square_fact
14mRh4X0r

1

Python 3, n = 100000

การเปลี่ยนแปลงอัลกอริทึมอย่างง่ายคือสิ่งที่จำเป็นในการชนตัวอย่างโค้ดขึ้น 10,000

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 ล้าน

ที่นี่ฉันใช้Math :: BigInt :: GMP library ที่มีอยู่ใน CPAN ซึ่งให้การเพิ่มความเร็วอย่างมากสำหรับวัตถุ Math :: BigInt ของ 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";

โปรดจำไว้ว่าคอมพิวเตอร์ของฉันอาจจะช้ากว่าของคุณเล็กน้อย การใช้สคริปต์ Python ดั้งเดิมของคุณฉันสามารถคำนวณได้factorial(40000)ภายใน 10 วินาทีเท่านั้น factorial(90000)ใช้เวลานานกว่ามาก (ฉันกด Ctrl + C หลังจากนั้นหนึ่งนาที) บนฮาร์ดแวร์ของคุณโดยใช้ Math :: BigInt :: GMP คุณสามารถคำนวณแฟคทอเรียล5 ล้านหรือมากกว่าภายใน 10 วินาที

สิ่งหนึ่งที่คุณอาจสังเกตเห็นคือถึงแม้ว่าแฟคทอเรียลจะถูกคำนวณอย่างรวดเร็วอย่างไม่น่าเชื่อการพิมพ์ผลลัพธ์ช้ามากโดยใช้เวลานานกว่าการคำนวณเดิมประมาณสามเท่า นี่เป็นเพราะ GMP ภายในใช้ไบนารีแทนที่จะเป็นทศนิยมและการพิมพ์ออกมานั้นต้องการการแปลงไบนารีเป็นทศนิยม


1
ฉันคิดว่า GMP นับเป็นทรัพยากรภายนอก (แม้ว่ามันจะทำให้สิ่งต่าง ๆ เป็นเรื่องง่ายกว่าการใช้ตัวประกอบเฉพาะตัวและการคูณSchönhage-Strassenตั้งแต่ต้น)
r3mainer

3
ฉันสันนิษฐานว่า "ทรัพยากรภายนอก" อ้างถึงการค้นหาโซลูชันจากชุดคำตอบที่คำนวณล่วงหน้าในฐานข้อมูลหรือบริการบนเว็บ ฯลฯ
tobyink

Squeamish: ปกติแล้วห้องสมุดจะไม่นับเป็นทรัพยากรภายนอกเว้นแต่ว่าจะมีฟังก์ชั่นที่อยู่ภายใต้กฎของช่องโหว่ที่น่าเบื่อ
alexander-brett

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ในการคำนวณ

ฉันทำงานในโหมด Release นอก Visual Studio โดยใช้ Core i3-2330M @ 2.2GHz

แก้ไข:เนื่องจากฉันไม่ได้ทำอะไรที่ชาญฉลาดฉันจึงยอมรับผลลัพธ์นั้น บางที. NET Framework 4.5 อาจเพิ่มค่าใช้จ่ายบางส่วน (หรือ BigInteger นั้นเร็วเกินไป)


กรุณาให้คะแนนและไม่เพียงn
80551

1
คุณสามารถใช้zero approached byโอเปอเรเตอร์เพื่อทำให้มันสวยขึ้น (เช่นเริ่มต้นn = ... + 1จากนั้นทำwhile (0 <-- n) result *= n;)
Cthulhu

1
BigInteger สำหรับ. NET อาจไม่ได้ใช้อัลกอริธึมสำหรับการคูณจำนวนที่มากขึ้นเช่น Karatsuba หรือ Toom-3 ถ้าเป็นเช่นนั้นนี่เป็นตัวอย่างที่ดีว่า Python เร็วแค่ไหน
kernigh

1

bc, คะแนน = 0.19

ห่านี่คือคู่แข่งของฉันสำหรับ"คุณจะทวีคูณช้าแค่ไหน?"

bc คือ "ภาษาเครื่องคำนวณความแม่นยำตามอำเภอใจ" แต่น่าเสียดายที่ค่อนข้างช้า:

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

ในประมาณ 10 วินาทีในกลางปี ​​2012 MacBook Pro ของฉัน (2.3 GHz Intel Core i7) คำตอบของไพ ธ อนสามารถคำนวณ 122000 !, แต่สคริปต์ bc นี้สามารถคำนวณ 23,600 เท่านั้น!

ในทางกลับกัน 10,000! ใช้เวลา 1.5 วินาทีด้วยสคริปต์อ้างอิงหลาม แต่สคริปต์ bc ใช้เวลา 50 วินาที

โอ้ที่รัก


1
OpenBSD bc (1) เร็วขึ้น โปรแกรมของคุณได้คะแนน0.29 = 28000/98000 ไม่มีดังนั้นฉันวิ่งread() time sed 's/read()/28000/' factorial.bc | bc
kernigh

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 เสร็จแล้ว
Emmanuel

1

Python 3, algo ขั้นสูงโดย Peter Luschny: 8.25x (1 280 000/155 000)

คัดลอกลงคอจากปีเตอร์ Luschny,
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm ,
ที่ให้รหัสนี้ภายใต้หัวข้อ "Creative Commons Attribution แบบเดียวกัน 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


ฉันสามารถขโมยรหัสของคุณเพื่อให้เป็นแบบมัลติเธรดได้หรือไม่
Simon Kuang

@SimonKuang ไปข้างหน้า แม้ว่าจะไม่อนุญาตให้ป้อนข้อมูลแบบมัลติเธรดที่นี่ นอกจากนี้คุณอาจต้องการใช้การใช้ 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 = 382000/98000
kernigh

แปลกฉันได้ 3.9 และคุณได้ 3.0 สำหรับโปรแกรมนี้ ฉันเดาว่าคอมพิวเตอร์ที่เร็วขึ้นของคุณคือการลงโทษ บางทีโปรแกรมของคุณอาจเสียประโยชน์จาก Python เมื่อมีการrเพิ่ม ถ้าเป็นเช่นนั้นและคุณสามารถทำคะแนนได้สูงกว่า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!!(หมายเหตุ: !!เป็นแฟคทอเรียลสองเท่า ) ดังนั้นฉันจึงแปลงมันเป็นรูปแบบที่ไม่เรียกซ้ำ

ความคิดเห็นแสดงให้เห็นว่าฉันพยายามหลีกเลี่ยงการคำนวณแฟคทอเรียลสองครั้ง แต่ฉันค้นพบว่าการจัดเก็บทุกค่านั้นมีค่าใช้จ่ายมากเกินไปสำหรับหน่วยความจำซึ่งทำให้คอมพิวเตอร์ของฉันทำงานช้าลง ฉันสามารถปรับปรุงสิ่งนี้ได้โดยการเก็บสิ่งที่จำเป็นเท่านั้น

ฉันใช้การคูณแบบไร้เดียงสาแบบตรงใน 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 โปรแกรมนี้ใช้ไลบรารีมาตรฐานเพื่อสร้างหมายเลขเฉพาะ 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

Julia - คะแนน = 15.194

ใช้วิธีการเดียวกันกับโปรแกรมอ้างอิง ... นั่นคือ

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

ดังนั้นจึงใช้การลดลงการดำเนินการคูณไบนารีพื้นฐานและช่วง (ในกรณีนี้ใช้บิ๊ก (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 ; ถ้าฉันยิ่งใหญ่ขึ้นฉันจะได้ SIGKILL; อาจ n สามารถใหญ่ขึ้นในล่าม tcl ท้องถิ่นซึ่งฉันไม่ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.