केंद्रीय द्विपद गुणांक की अंक राशि


13

कार्य बस यह देखने के लिए है कि आप अजगर में अंतर्निहित फ़ंक्शन की तुलना में n / 2 (n भी) के लिए n कितनी तेजी से गणना कर सकते हैं। निश्चित रूप से बड़े एन के लिए यह एक बड़ी संख्या है, इसलिए आउटपुट के बजाय पूरे अंक को आपको अंकों के योग का उत्पादन करना चाहिए। उदाहरण के लिए, n = 100000उत्तर के लिए , है 135702। इसके लिए n=1000000है1354815

यहाँ अजगर कोड है:

from scipy.misc import comb
def sum_digits(n):
   r = 0
   while n:
       r, n = r + n % 10, n / 10
   return r
sum_digits(comb(n,n/2,exact=True))

आपका स्कोर है (highest n on your machine using your code)/(highest n on your machine using my code) । आपका कोड 60 सेकंड या उससे कम समय में समाप्त होना चाहिए।

आपके प्रोग्राम को सभी n: 2 <= n <= (आपके उच्चतम n) के लिए सही आउटपुट देना चाहिए

आप किसी भी बिलिन कोड या पुस्तकालयों का उपयोग नहीं कर सकते हैं जो द्विपद गुणांक या मूल्यों की गणना करते हैं जो जल्दी से द्विपद गुणांक में परिवर्तित हो सकते हैं।

आप अपनी पसंद की किसी भी भाषा का उपयोग कर सकते हैं।


उत्तर देने वाला उत्तर एक अद्भुत 680.09 के साथ वर्तमान प्रमुख उत्तर है जस्टफॉल द्वारा।


2
क्या हमें अजगर या पसंद की भाषा में समाधान प्रस्तुत करना चाहिए?

यह एक ऐसी दिनचर्या लिखना संभव है जो एक आधुनिक कंप्यूटर पर ऐसा करता है और nलाखों लोगों को अच्छी तरह से लेता है , जबकि मुझे संदेह है कि पायथन फ़ंक्शन n = 1e5बिना घुट के बिना कुछ भी संभाल सकता है ।
COTO

@Aalandro आप अपनी पसंद की किसी भी भाषा का उपयोग कर सकते हैं। एकमात्र प्रतिबंध यह था कि आप गुणांक की गणना करने के लिए अंतर्निहित कार्यों का उपयोग नहीं कर सकते।

2
क्या तथ्यात्मक कार्यों की अनुमति है? मैंने यह नहीं माना कि चूंकि वे "बिनोमियल गुणांक में जल्दी से परिवर्तित हो सकते हैं" (पूरी बात सिर्फ एक तथ्य है जो एक अन्य तथ्यात्मक वर्ग से विभाजित है), लेकिन चूंकि एक उत्तर अब एक का उपयोग कर रहा है, इसलिए स्पष्टता अच्छी होगी।
3

1
@Comintern: मैंने सफलतापूर्वक 1 मिनट में 287mil, या 35 सेकंड में 169mil के साथ उस बिंदु को दोहराया है! :)
justhalf

जवाबों:


9

C ++ (GMP) - (287,000,000 / 422,000) = 680.09

बेशर्मी से कुमेर के प्रमेय को xnor और GMP द्वारा qwr द्वारा संयोजित करें। फिर भी गो सॉल्यूशन के करीब भी नहीं, पक्का नहीं कि क्यों।

संपादित करें: धन्यवाद कीथ रान्डेल याद दिलाने के लिए कि संख्या आकार में समान होने पर गुणा तेज है। मैंने मेमोरी प्रबंधन पर मेमोरी कोलेसिंग अवधारणा के समान बहु-स्तरीय गुणन लागू किया। और परिणाम प्रभावशाली है। जो पहले 51s लेता था, अब केवल 0.5s (यानी, 100 गुना सुधार) लेता है !!

OLD CODE (n = 14,000,000)
०.३४३ में किया गया
51.929 के दशक में द्विप की गणना की
0.901 में सम्‍मिलित हुआ
14000000: 18954729

असली 0m53.194s
उपयोगकर्ता 0m53.116s
sys 0m0.060s

नई CODE (n = 14,000,000)
०.३४३ में किया गया
0.552 के दशक में द्विपद की गणना की
0.902s में सम्‍मिलित किया
14000000: 18954729

वास्तविक 0m1.804s
उपयोगकर्ता 0m1.776s
sys 0m0.023s

के लिए दौड़ n=287,000,000

4.211s में किया गया
17.934 के दशक में द्विप की गणना की
37.677 में पूरा हुआ
287000000: 388788354

वास्तविक 0m59.928s
उपयोगकर्ता 0m58.759s
sys 0m1.116s

कोड। संकलन-lgmp -lgmpxx -O3

#include <gmpxx.h>
#include <iostream>
#include <time.h>
#include <cstdio>

const int MAX=287000000;
const int PRIME_COUNT=15700000;

int primes[PRIME_COUNT], factors[PRIME_COUNT], count;
bool sieve[MAX];
int max_idx=0;

void run_sieve(){
    sieve[2] = true;
    primes[0] = 2;
    count = 1;
    for(int i=3; i<MAX; i+=2){
        sieve[i] = true;
    }
    for(int i=3; i<17000; i+=2){
        if(!sieve[i]) continue;
        for(int j = i*i; j<MAX; j+=i){
            sieve[j] = false;
        }
    }
    for(int i=3; i<MAX; i+=2){
        if(sieve[i]) primes[count++] = i;
    }
}

mpz_class sum_digits(mpz_class n){
    clock_t t = clock();
    char* str = mpz_get_str(NULL, 10, n.get_mpz_t());
    int result = 0;
    for(int i=0;str[i]>0;i++){
        result+=str[i]-48;
    }
    printf("Done summing in %.3fs\n", ((float)(clock()-t))/CLOCKS_PER_SEC);
    return result;
}

mpz_class nc2_fast(const mpz_class &x){
    clock_t t = clock();
    int prime;
    const unsigned int n = mpz_get_ui(x.get_mpz_t());
    const unsigned int n2 = n/2;
    unsigned int m;
    unsigned int digit;
    unsigned int carry=0;
    unsigned int carries=0;
    mpz_class result = 1;
    mpz_class prime_prods = 1;
    mpz_class tmp;
    mpz_class tmp_prods[32], tmp_prime_prods[32];
    for(int i=0; i<32; i++){
        tmp_prods[i] = (mpz_class)NULL;
        tmp_prime_prods[i] = (mpz_class)NULL;
    }
    for(int i=0; i< count; i++){
        prime = primes[i];
        carry=0;
        carries=0;
        if(prime > n) break;
        if(prime > n2){
            tmp = prime;
            for(int j=0; j<32; j++){
                if(tmp_prime_prods[j] == NULL){
                    tmp_prime_prods[j] = tmp;
                    break;
                } else {
                    mpz_mul(tmp.get_mpz_t(), tmp.get_mpz_t(), tmp_prime_prods[j].get_mpz_t());
                    tmp_prime_prods[j] = (mpz_class)NULL;
                }
            }
            continue;
        }
        m=n2;
        while(m>0){
            digit = m%prime;
            carry = (2*digit + carry >= prime) ? 1 : 0;
            carries += carry;
            m/=prime;
        }
        if(carries>0){
            tmp = 0;
            mpz_ui_pow_ui(tmp.get_mpz_t(), prime, carries);
            for(int j=0; j<32; j++){
                if(tmp_prods[j] == NULL){
                    tmp_prods[j] = tmp;
                    break;
                } else {
                    mpz_mul(tmp.get_mpz_t(), tmp.get_mpz_t(), tmp_prods[j].get_mpz_t());
                    tmp_prods[j] = (mpz_class)NULL;
                }
            }
        }
    }
    result = 1;
    prime_prods = 1;
    for(int j=0; j<32; j++){
        if(tmp_prods[j] != NULL){
            mpz_mul(result.get_mpz_t(), result.get_mpz_t(), tmp_prods[j].get_mpz_t());
        }
        if(tmp_prime_prods[j] != NULL){
            mpz_mul(prime_prods.get_mpz_t(), prime_prods.get_mpz_t(), tmp_prime_prods[j].get_mpz_t());
        }
    }
    mpz_mul(result.get_mpz_t(), result.get_mpz_t(), prime_prods.get_mpz_t());
    printf("Done calculating binom in %.3fs\n", ((float)(clock()-t))/CLOCKS_PER_SEC);
    return result;
}

int main(int argc, char* argv[]){
    const mpz_class n = atoi(argv[1]);
    clock_t t = clock();
    run_sieve();
    printf("Done sieving in %.3fs\n", ((float)(clock()-t))/CLOCKS_PER_SEC);
    std::cout << n << ": " << sum_digits(nc2_fast(n)) << std::endl;
    return 0;
}

2
यदि दोनों ऑपरेंड एक ही आकार के हों, तो गुणक अधिक कुशल होते हैं। आप हमेशा बड़ी संख्या को छोटी संख्या से गुणा कर रहे हैं। यदि आप बार-बार जोड़े में छोटी संख्याओं को जोड़ते हैं तो यह तेज हो सकता है (लेकिन अधिक मेमोरी लें)।
कीथ रान्डेल

वाह, इससे बहुत फर्क पड़ता है। यह तेजी से तेजी से बढ़ रहा है। मैं 35 सेकंड में अब 169mil तक पहुंच सकता हूं।
बजे

वाह सच! आपके कोड के विभिन्न भागों के लिए समय में क्या टूटना है?

मैंने पहले ही अपने उत्तर में डाल दिया है। 4 जी n, 18 से , केंद्रीय द्विपद गुणांक की गणना करने और शेष 37 को स्ट्रिंग में परिणाम में परिवर्तित करने और अंकों को समेटने के लिए प्राइम जनरेट करता है।
6

1
मुझे लगता है कि इस उत्तर को किसी भी ओपन सोर्स लाइब्रेरी में योगदान दिया जाना चाहिए जो द्विपद गुणांक की गणना करता है। मुझे विश्वास नहीं होता कि किसी और के पास यह कोड है!

7

गो, 33.96 = (16300000/480000)

package main

import "math/big"

const n = 16300000

var (
    sieve     [n + 1]bool
    remaining [n + 1]int
    count     [n + 1]int
)

func main() {
    println("finding primes")
    for p := 2; p <= n; p++ {
        if sieve[p] {
            continue
        }
        for i := p * p; i <= n; i += p {
            sieve[i] = true
        }
    }

    // count net number of times each prime appears in the result.
    println("counting factors")
    for i := 2; i <= n; i++ {
        remaining[i] = i
    }
    for p := 2; p <= n; p++ {
        if sieve[p] {
            continue
        }

        for i := p; i <= n; i += p {
            for remaining[i]%p == 0 { // may have multiple factors of p
                remaining[i] /= p

                // count positive for n!
                count[p]++
                // count negative twice for ((n/2)!)^2
                if i <= n/2 {
                    count[p] -= 2
                }
            }
        }
    }

    // ignore all the trailing zeros
    count[2] -= count[5]
    count[5] = 0

    println("listing factors")
    var m []uint64
    for i := 0; i <= n; i++ {
        for count[i] > 0 {
            m = append(m, uint64(i))
            count[i]--
        }
    }

    println("grouping factors")
    m = group(m)

    println("multiplying")
    x := mul(m)

    println("converting to base 10")
    d := 0
    for _, c := range x.String() {
        d += int(c - '0')
    }
    println("sum of digits:", d)
}

// Return product of elements in a.
func mul(a []uint64) *big.Int {
    if len(a) == 1 {
        x := big.NewInt(0)
        x.SetUint64(a[0])
        return x
    }
    m := len(a) / 2
    x := mul(a[:m])
    y := mul(a[m:])
    x.Mul(x, y) // fast because x and y are about the same length
    return x
}

// return a slice whose members have the same product
// as the input slice, but hopefully shorter.
func group(a []uint64) []uint64 {
    var g []uint64
    r := uint64(1)
    b := 1
    for _, x := range a {
        c := bits(x)
        if b+c <= 64 {
            r *= x
            b += c
        } else {
            g = append(g, r)
            r = x
            b = c
        }
    }
    g = append(g, r)
    return g
}

// bits returns the number of bits in the representation of x
func bits(x uint64) int {
    n := 0
    for x != 0 {
        n++
        x >>= 1
    }
    return n
}

अंश और हर में सभी प्रमुख कारकों की गिनती और मिलान कारकों को रद्द करके काम करता है। परिणाम प्राप्त करने के लिए बचे हुए को गुणा करें।

आधार को परिवर्तित करने में 80% से अधिक समय व्यतीत होता है। ऐसा करने का एक बेहतर तरीका है ...


उन समस्याओं के लिए जिन्हें आधार 10 में बड़ी संख्या में मुद्रण की आवश्यकता होती है, मुझे आमतौर पर अपना खुद का बिगइंटर क्लास लिखने में मदद मिलती है जो आधार 1E9 ~ 2 ^ 30 में संख्याओं को संग्रहीत करता है।
पीटर टेलर

आप वर्तमान में एक देश मील से जीत रहे हैं .. जैसा कि वे कहते हैं।

@PeterTaylor: मैंने कोशिश की कि, लेकिन इसे मल्टीप्ल कोड में बहुत सारे% 1e9 की आवश्यकता होती है, जो गुणा को धीमा कर देता है।
कीथ रान्डेल

6

अजगर 3 (8.8 = 2.2 मिलियन / 0.25 मिलियन)

यह पायथन में है, जिसे गति के लिए नहीं जाना जाता है, इसलिए आप शायद इसे किसी अन्य भाषा में बेहतर पोर्टिंग कर सकते हैं।

इस StackOverflow प्रतियोगिता से लिया गया प्राइम जनरेटर ।

import numpy
import time

def primesfrom2to(n):
    """ Input n>=6, Returns a array of primes, 2 <= p < n """
    sieve = numpy.ones(n//3 + (n%6==2), dtype=numpy.bool)
    for i in range(1,int(n**0.5)//3+1):
        if sieve[i]:
            k=3*i+1|1
            sieve[       k*k/3     ::2*k] = False
            sieve[k*(k-2*(i&1)+4)/3::2*k] = False
    return numpy.r_[2,3,((3*numpy.nonzero(sieve)[0][1:]+1)|1)]

t0 = time.clock()

N=220*10**4
n=N//2

print("N = %d" % N)
print()

print("Generating primes.")
primes = primesfrom2to(N)

t1 = time.clock()
print ("Time taken: %f" % (t1-t0))

print("Computing product.")
product = 1

for p in primes:
    p=int(p)
    carries = 0 
    carry = 0

    if p>n:
        product*=p
        continue

    m=n

    #Count carries of n+n in base p as per Kummer's Theorem
    while m:
        digit = m%p
        carry = (2*digit + carry >= p)
        carries += carry
        m//=p

    if carries >0:
        for _ in range(carries):
            product *= p

    #print(p,carries,product)

t2 = time.clock()
print ("Time taken: %f" % (t2-t1))

print("Converting number to string.")

# digit_sum = 0
# result=product

# while result:
    # digit_sum+=result%10
    # result//=10

digit_sum = 0
digit_string = str(product)

t3 = time.clock()
print ("Time taken: %f" % (t3-t2))

print("Summing digits.")
for d in str(digit_string):digit_sum+=int(d)

t4 = time.clock()
print ("Time taken: %f" % (t4-t3))
print ()

print ("Total time: %f" % (t4-t0))
print()
print("Sum of digits = %d" % digit_sum)

एल्गोरिथ्म का मुख्य विचार द्विपद का प्रधान-कारकीकरण प्राप्त करने के लिए कुमेर के प्रमेय का उपयोग करना है। प्रत्येक प्राइम के लिए, हम इसकी उच्चतम शक्ति सीखते हैं जो उत्तर को विभाजित करता है, और प्राइम की उस शक्ति से चल रहे उत्पाद को गुणा करता है। इस तरह, हमें केवल उत्तर के प्राइम-फैक्टरलाइज़ेशन में प्रत्येक प्राइम के लिए एक बार गुणा करना होगा।

उत्पादन समय टूटने दिखा:

N = 2200000
Generating primes.
Time taken: 0.046408
Computing product.
Time taken: 17.931472
Converting number to string.
Time taken: 39.083390
Summing digits.
Time taken: 1.502393

Total time: 58.563664

Sum of digits = 2980107

हैरानी की बात यह है कि अधिकांश समय अपने अंकों को योग करने के लिए संख्या को स्ट्रिंग में परिवर्तित करने में व्यतीत होता है। इसके अलावा आश्चर्यजनक रूप से, एक स्ट्रिंग में कनवर्ट करने बहुत तेजी से बार-बार से अंक प्राप्त करने से था %10और //10, भले ही पूरी स्ट्रिंग शायद स्मृति में रखा जाना चाहिए।

अपराधों को उत्पन्न करने में नगण्य समय लगता है (और इसलिए मुझे मौजूदा कोड की अनुचित नकल नहीं लगती है)। सुमिंग अंक तेजी से होता है। वास्तविक गुणा समय का एक तिहाई होता है।

यह देखते हुए कि अंक योग सीमित कारक लगता है, शायद दशमलव प्रतिनिधित्व में संख्याओं को गुणा करने के लिए एक एल्गोरिथ्म बाइनरी / दशमलव रूपांतरण को शॉर्टकट करके कुल समय की बचत करेगा।


यह बहुत प्रभावशाली है और आपको आश्चर्य होता है कि कफ़न आपके कार्यान्वयन का उपयोग क्यों नहीं करता है!

3

जावा (स्कोर 22500/365000 = 0.062)

मेरे पास इस मशीन पर अजगर नहीं है, इसलिए अगर कोई इसे स्कोर कर सकता है तो मैं आभारी रहूंगा। यदि नहीं, तो यह इंतजार करना होगा।


(2nn)=k=0n(nk)2

अड़चन पास्कल के त्रिकोण के संबंधित अनुभाग (चलने के समय का 90%) की गणना करने के अलावा है, इसलिए बेहतर गुणा एल्गोरिथ्म का उपयोग करने से वास्तव में मदद नहीं मिलेगी।

ध्यान दें कि प्रश्न कॉल nक्या है जो मैं कहता हूं 2n। कमांड-लाइन तर्क वह है जिसे प्रश्न कहते हैं n

public class CodeGolf37270 {
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage: java CodeGolf37270 <n>");
            System.exit(1);
        }

        int two_n = Integer.parseInt(args[0]);
        // \binom{2n}{n} = \sum_{k=0}^n \binom{n}{k}^2
        // Two cases:
        //   n = 2m: \binom{4m}{2m} = \binom{2m}{m}^2 + 2\sum_{k=0}^{m-1} \binom{2m}{k}^2
        //   n = 2m+1: \binom{4m+2}{2m+1} = 2\sum_{k=0}^{m} \binom{2m+1}{k}^2
        int n = two_n / 2;
        BigInt[] nCk = new BigInt[n/2 + 1];
        nCk[0] = new BigInt(1);
        for (int k = 1; k < nCk.length; k++) nCk[k] = nCk[0];
        for (int row = 2; row <= n; row++) {
            BigInt tmp = nCk[0];
            for (int col = 1; col < row && col < nCk.length; col++) {
                BigInt replacement = tmp.add(nCk[col]);
                tmp = nCk[col];
                nCk[col] = replacement;
            }
        }

        BigInt central = nCk[0]; // 1^2 = 1
        int lim = (n & 1) == 1 ? nCk.length : (nCk.length - 1);
        for (int k = 1; k < lim; k++) central = central.add(nCk[k].sq());
        central = central.add(central);
        if ((n & 1) == 0) central = central.add(nCk[nCk.length - 1].sq());

        System.out.println(central.digsum());
    }

    private static class BigInt {
        static final int B = 1000000000;
        private int[] val;

        public BigInt(int x) {
            val = new int[] { x };
        }

        private BigInt(int[] val) {
            this.val = val;
        }

        public BigInt add(BigInt that) {
            int[] left, right;
            if (val.length < that.val.length) {
                left = that.val;
                right = val;
            }
            else {
                left = val;
                right = that.val;
            }

            int[] sum = left.clone();
            int carry = 0, k = 0;
            for (; k < right.length; k++) {
                int a = sum[k] + right[k] + carry;
                sum[k] = a % B;
                carry = a / B;
            }
            while (carry > 0 && k < sum.length) {
                int a = sum[k] + carry;
                sum[k] = a % B;
                carry = a / B;
                k++;
            }
            if (carry > 0) {
                int[] wider = new int[sum.length + 1];
                System.arraycopy(sum, 0, wider, 0, sum.length);
                wider[sum.length] = carry;
                sum = wider;
            }

            return new BigInt(sum);
        }

        public BigInt sq() {
            int[] rv = new int[2 * val.length];
            // Naive multiplication
            for (int i = 0; i < val.length; i++) {
                for (int j = i; j < val.length; j++) {
                    int k = i+j;
                    long c = val[i] * (long)val[j];
                    if (j > i) c <<= 1;
                    while (c > 0) {
                        c += rv[k];
                        rv[k] = (int)(c % B);
                        c /= B;
                        k++;
                    }
                }
            }

            int len = rv.length;
            while (len > 1 && rv[len - 1] == 0) len--;
            if (len < rv.length) {
                int[] rv2 = new int[len];
                System.arraycopy(rv, 0, rv2, 0, len);
                rv = rv2;
            }

            return new BigInt(rv);
        }

        public long digsum() {
            long rv = 0;
            for (int i = 0; i < val.length; i++) {
                int x = val[i];
                while (x > 0) {
                    rv += x % 10;
                    x /= 10;
                }
            }
            return rv;
        }
    }
}

मुझे आपके कार्यक्रम के लिए 29,500 और संदर्भ कार्यक्रम के लिए 440,000 मिलते हैं, इसलिए यह 0.067 का स्कोर होगा। यह जावा 1.7 ( javac CodeGolf37270.java) और जावा 1.8 ( java CodeGolf37270 n) के साथ निष्पादित हो रहा है । मुझे यकीन नहीं है कि कोई भी अनुकूलन विकल्प हैं जिससे मैं अनजान हूं। मैं जावा 1.8 के साथ संकलन करने का प्रयास नहीं कर सकता, क्योंकि यह मेरे जावा पैकेज के साथ स्थापित नहीं है ...
डेनिस

दिलचस्प दृष्टिकोण। आपको क्या लगता है कि साधारण सूत्र का उपयोग करने की तुलना में पुनरावृति की गणना अधिक तेजी से हो सकती है?
justhalf

@ अन्याय, मुझे इस बात का आभास नहीं था कि यह तेज होगा या नहीं, और मैंने जटिलता गणना करने की कोशिश नहीं की। मैंने केंद्रीय द्विपद गुणांक के लिए पहचान की सूचियों के माध्यम से उन सूत्रों को खोजने की कोशिश की, जो आधार -10 अंकों को निकालने के लिए अनुकूलित एक कस्टम बड़े पूर्णांक वर्ग के साथ लागू करना सरल होगा। और पता चला कि यह बहुत कुशल नहीं है, मैं इसे पोस्ट कर सकता हूं और किसी और को प्रयोग को दोहराने से बचा सकता हूं। (एफडब्ल्यूआईडब्ल्यू मैं टूम गुणा पर काम कर रहा हूं, लेकिन मुझे यकीन नहीं है कि जब मैंने इसे परीक्षण और डिबग किया होगा)।
पीटर टेलर

2

GMP - 1500000/300000 = 5.0

हालांकि यह जवाब sieves के खिलाफ प्रतिस्पर्धा नहीं करेगा, कभी-कभी छोटे कोड अभी भी परिणाम प्राप्त कर सकते हैं।

#include <gmpxx.h>
#include <iostream>

mpz_class sum_digits(mpz_class n)
{
    char* str = mpz_get_str(NULL, 10, n.get_mpz_t());
    int result = 0;
    for(int i=0; str[i]>0; i++)

    result += str[i] - 48;

    return result;
}


mpz_class comb_2(const mpz_class &x)
{
    const unsigned int k = mpz_get_ui(x.get_mpz_t()) / 2;
    mpz_class result = k + 1;

    for(int i=2; i<=k; i++)
    {
        result *= k + i;
        mpz_divexact_ui(result.get_mpz_t(), result.get_mpz_t(), i);
    }

    return result;
}

int main()
{
    const mpz_class n = 1500000;
    std::cout << sum_digits(comb_2(n)) << std::endl;

    return 0;
}

2

जावा, कस्टम बड़ा पूर्णांक वर्ग: 32.9 (120000000/365000)

मुख्य वर्ग बहुत सीधा है:

import java.util.*;

public class PPCG37270 {
    public static void main(String[] args) {
        long start = System.nanoTime();

        int n = 12000000;
        if (args.length == 1) n = Integer.parseInt(args[0]);

        boolean[] sieve = new boolean[n + 1];
        int[] remaining = new int[n + 1];
        int[] count = new int[n + 1];

        for (int p = 2; p <= n; p++) {
            if (sieve[p]) continue;
            long p2 = p * (long)p;
            if (p2 > n) continue;
            for (int i = (int)p2; i <= n; i += p) sieve[i] = true;
        }

        for (int i = 2; i <= n; i++) remaining[i] = i;
        for (int p = 2; p <= n; p++) {
            if (sieve[p]) continue;
            for (int i = p; i <= n; i += p) {
                while (remaining[i] % p == 0) {
                    remaining[i] /= p;
                    count[p]++;
                    if (i <= n/2) count[p] -= 2;
                }
            }
        }

        count[2] -= count[5];
        count[5] = 0;

        List<BigInt> partialProd = new ArrayList<BigInt>();
        long accum = 1;
        for (int i = 2; i <= n; i++) {
            for (int j = count[i]; j > 0; j--) {
                long tmp = accum * i;
                if (tmp < 1000000000L) accum = tmp;
                else {
                    partialProd.add(new BigInt((int)accum));
                    accum = i;
                }
            }
        }
        partialProd.add(new BigInt((int)accum));
        System.out.println(prod(partialProd).digsum());
        System.out.println((System.nanoTime() - start) / 1000000 + "ms");
    }

    private static BigInt prod(List<BigInt> vals) {
        while (vals.size() > 1) {
            int n = vals.size();
            List<BigInt> next = new ArrayList<BigInt>();
            for (int i = 0; i < n; i += 2) {
                if (i == n - 1) next.add(vals.get(i));
                else next.add(vals.get(i).mul(vals.get(i+1)));
            }
            vals = next;
        }
        return vals.get(0);
    }
}

यह एक बड़े पूर्णांक वर्ग पर निर्भर करता है जो गुणन के लिए अनुकूलित है और toString(), जिसके कार्यान्वयन में दोनों महत्वपूर्ण अड़चनें हैं java.math.BigInteger

/**
 * A big integer class which is optimised for conversion to decimal.
 * For use in simple applications where BigInteger.toString() is a bottleneck.
 */
public class BigInt {
    // The base of the representation.
    private static final int B = 1000000000;
    // The number of decimal digits per digit of the representation.
    private static final int LOG10_B = 9;

    public static final BigInt ZERO = new BigInt(0);
    public static final BigInt ONE = new BigInt(1);

    // We use sign-magnitude representation.
    private final boolean negative;

    // Least significant digit is at val[off]; most significant is at val[off + len - 1]
    // Unless len == 1 we guarantee that val[off + len - 1] is non-zero.
    private final int[] val;
    private final int off;
    private final int len;

    // Toom-style multiplication parameters from
    // Zuras, D. (1994). More on squaring and multiplying large integers. IEEE Transactions on Computers, 43(8), 899-908.
    private static final int[][][] Q = new int[][][]{
        {},
        {},
        {{1, -1}},
        {{4, 2, 1}, {1, 1, 1}, {1, 2, 4}},
        {{8, 4, 2, 1}, {-8, 4, -2, 1}, {1, 1, 1, 1}, {1, -2, 4, -8}, {1, 2, 4, 8}}
    };
    private static final int[][][] R = new int[][][]{
        {},
        {},
        {{1, -1, 1}},
        {{-21, 2, -12, 1, -6}, {7, -1, 10, -1, 7}, {-6, 1, -12, 2, -21}},
        {{-180, 6, 2, -80, 1, 3, -180}, {-510, 4, 4, 0, -1, -1, 120}, {1530, -27, -7, 680, -7, -27, 1530}, {120, -1, -1, 0, 4, 4, -510}, {-180, 3, 1, -80, 2, 6, -180}}
    };
    private static final int[][] S = new int[][]{
        {},
        {},
        {1, 1, 1},
        {1, 6, 2, 6, 1},
        {1, 180, 120, 360, 120, 180, 1}
    };

    /**
     * Constructs a big version of an integer value.
     * @param x The value to represent.
     */
    public BigInt(int x) {
        this(Integer.toString(x));
    }

    /**
     * Constructs a big version of a long value.
     * @param x The value to represent.
     */
    public BigInt(long x) {
        this(Long.toString(x));
    }

    /**
     * Parses a decimal representation of an integer.
     * @param str The value to represent.
     */
    public BigInt(String str) {
        this(str.charAt(0) == '-', split(str));
    }

    /**
     * Constructs a sign-magnitude representation taking the entire span of the array as the range of interest.
     * @param neg Is the value negative?
     * @param val The base-B digits, least significant first.
     */
    private BigInt(boolean neg, int[] val) {
        this(neg, val, 0, val.length);
    }

    /**
     * Constructs a sign-magnitude representation taking a range of an array as the magnitude.
     * @param neg Is the value negative?
     * @param val The base-B digits, least significant at offset off, most significant at off + val - 1.
     * @param off The offset within the array.
     * @param len The number of base-B digits.
     */
    private BigInt(boolean neg, int[] val, int off, int len) {
        // Bounds checks
        if (val == null) throw new IllegalArgumentException("val");
        if (off < 0 || off >= val.length) throw new IllegalArgumentException("off");
        if (len < 1 || off + len > val.length) throw new IllegalArgumentException("len");

        this.negative = neg;
        this.val = val;
        this.off = off;
        // Enforce the invariant that this.len is 1 or val[off + len - 1] is non-zero.
        while (len > 1 && val[off + len - 1] == 0) len--;
        this.len = len;

        // Sanity check
        for (int i = 0; i < len; i++) {
            if (val[off + i] < 0) throw new IllegalArgumentException("val contains negative digits");
        }
    }

    /**
     * Splits a string into base-B digits.
     * @param str The string to parse.
     * @return An array which can be passed to the (boolean, int[]) constructor.
     */
    private static int[] split(String str) {
        if (str.charAt(0) == '-') str = str.substring(1);

        int[] arr = new int[(str.length() + LOG10_B - 1) / LOG10_B];
        int i, off;
        // Each element of arr represents LOG10_B characters except (probably) the last one.
        for (i = 0, off = str.length() - LOG10_B; off > 0; off -= LOG10_B) {
            arr[i++] = Integer.parseInt(str.substring(off, off + LOG10_B));
        }
        arr[i] = Integer.parseInt(str.substring(0, off + LOG10_B));
        return arr;
    }

    public boolean isZero() {
        return len == 1 && val[off] == 0;
    }

    public BigInt negate() {
        return new BigInt(!negative, val, off, len);
    }

    public BigInt add(BigInt that) {
        // If the signs differ, then since we use sign-magnitude representation we want to do a subtraction.
        boolean isSubtraction = negative ^ that.negative;

        BigInt left, right;
        if (len < that.len) {
            left = that;
            right = this;
        }
        else {
            left = this;
            right = that;

            // For addition I just care about the lengths of the arrays.
            // For subtraction I want the largest absolute value on the left.
            if (isSubtraction && len == that.len) {
                int cmp = compareAbsolute(that);
                if (cmp == 0) return ZERO; // Cheap special case
                if (cmp < 0) {
                    left = that;
                    right = this;
                }
            }
        }

        if (right.isZero()) return left;

        BigInt result;
        if (!isSubtraction) {
            int[] sum = new int[left.len + 1];
            // A copy here rather than using left.val in the main loops and copying remaining values
            // at the end gives a small performance boost, probably due to cache locality.
            System.arraycopy(left.val, left.off, sum, 0, left.len);

            int carry = 0, k = 0;
            for (; k < right.len; k++) {
                int a = sum[k] + right.val[right.off + k] + carry;
                sum[k] = a % B;
                carry = a / B;
            }
            for (; carry > 0 && k < left.len; k++) {
                int a = sum[k] + carry;
                sum[k] = a % B;
                carry = a / B;
            }
            sum[left.len] = carry;

            result = new BigInt(negative, sum);
        }
        else {
            int[] diff = new int[left.len];
            System.arraycopy(left.val, left.off, diff, 0, left.len);

            int carry = 0, k = 0;
            for (; k < right.len; k++) {
                int a = diff[k] - right.val[right.off + k] + carry;
                // Why did anyone ever think that rounding positive and negative divisions differently made sense?
                if (a < 0) {
                    diff[k] = a + B;
                    carry = -1;
                }
                else {
                    diff[k] = a % B;
                    carry = a / B;
                }
            }
            for (; carry != 0 && k < left.len; k++) {
                int a = diff[k] + carry;
                if (a < 0) {
                    diff[k] = a + B;
                    carry = -1;
                }
                else {
                    diff[k] = a % B;
                    carry = a / B;
                }
            }

            result = new BigInt(left.negative, diff, 0, k > left.len ? k : left.len);
        }

        return result;
    }

    private int compareAbsolute(BigInt that) {
        if (len > that.len) return 1;
        if (len < that.len) return -1;

        for (int i = len - 1; i >= 0; i--) {
            if (val[off + i] > that.val[that.off + i]) return 1;
            if (val[off + i] < that.val[that.off + i]) return -1;
        }

        return 0;
    }

    public BigInt mul(BigInt that) {
        if (isZero() || that.isZero()) return ZERO;

        if (len == 1) return that.mulSmall(negative ? -val[off] : val[off]);
        if (that.len == 1) return mulSmall(that.negative ? -that.val[that.off] : that.val[that.off]);

        int shorter = len < that.len ? len : that.len;
        BigInt result;
        // Cutoffs have been hand-tuned.
        if (shorter > 300) result = mulToom(3, that);
        else if (shorter > 28) result = mulToom(2, that);
        else result = mulNaive(that);

        return result;
    }

    BigInt mulSmall(int m) {
        if (m == 0) return ZERO;
        if (m == 1) return this;
        if (m == -1) return negate();

        // We want to do the magnitude calculation with a positive multiplicand.
        boolean neg = negative;
        if (m < 0) {
            neg = !neg;
            m = -m;
        }

        int[] pr = new int[len + 1];
        int carry = 0;
        for (int i = 0; i < len; i++) {
            long t = val[off + i] * (long)m + carry;
            pr[i] = (int)(t % B);
            carry = (int)(t / B);
        }
        pr[len] = carry;
        return new BigInt(neg, pr);
    }

    // NB This truncates.
    BigInt divSmall(int d) {
        if (d == 0) throw new ArithmeticException();
        if (d == 1) return this;
        if (d == -1) return negate();

        // We want to do the magnitude calculation with a positive divisor.
        boolean neg = negative;
        if (d < 0) {
            neg = !neg;
            d = -d;
        }

        int[] div = new int[len];
        int rem = 0;
        for (int i = len - 1; i >= 0; i--) {
            long t = val[off + i] + rem * (long)B;
            div[i] = (int)(t / d);
            rem = (int)(t % d);
        }

        return new BigInt(neg, div);
    }

    BigInt mulNaive(BigInt that) {
        int[] rv = new int[len + that.len];
        // Naive multiplication
        for (int i = 0; i < len; i++) {
            for (int j = 0; j < that.len; j++) {
                int k = i + j;
                long c = val[off + i] * (long)that.val[that.off + j];
                while (c > 0) {
                    c += rv[k];
                    rv[k] = (int)(c % B);
                    c /= B;
                    k++;
                }
            }
        }

        return new BigInt(this.negative ^ that.negative, rv);
    }

    private BigInt mulToom(int k, BigInt that) {
        // We split each number into k parts of m base-B digits each.
        // m = ceil(longer / k)
        int m = ((len > that.len ? len : that.len) + k - 1) / k;

        // Perform the splitting and evaluation steps of Toom-Cook.
        BigInt[] f1 = this.toomFwd(k, m);
        BigInt[] f2 = that.toomFwd(k, m);

        // Pointwise multiplication.
        for (int i = 0; i < f1.length; i++) f1[i] = f1[i].mul(f2[i]);

        // Inverse (or interpolation) and recomposition.
        return toomBk(k, m, f1, negative ^ that.negative, val[off], that.val[that.off]);
    }

    // Splits a number into k parts of m base-B digits each and does the polynomial evaluation.
    private BigInt[] toomFwd(int k, int m) {
        // Split.
        BigInt[] a = new BigInt[k];
        for (int i = 0; i < k; i++) {
            int o = i * m;
            if (o >= len) a[i] = ZERO;
            else {
                int l = m;
                if (o + l > len) l = len - o;
                // Ignore signs for now.
                a[i] = new BigInt(false, val, off + o, l);
            }
        }

        // Evaluate
        return transform(Q[k], a);
    }

    private BigInt toomBk(int k, int m, BigInt[] f, boolean neg, int lsd1, int lsd2) {
        // Inverse (or interpolation).
        BigInt[] b = transform(R[k], f);

        // Recomposition: add at suitable offsets, dividing by the normalisation factors
        BigInt prod = ZERO;
        int[] s = S[k];
        for (int i = 0; i < b.length; i++) {
            int[] shifted = new int[i * m + b[i].len];
            System.arraycopy(b[i].val, b[i].off, shifted, i * m, b[i].len);
            prod = prod.add(new BigInt(neg ^ b[i].negative, shifted).divSmall(s[i]));
        }

        // Handle the remainders.
        // In the worst case the absolute value of the sum of the remainders is s.length, so pretty small.
        // It should be easy enough to work out whether to go up or down.
        int lsd = (int)((lsd1 * (long)lsd2) % B);
        int err = lsd - prod.val[prod.off];
        if (err > B / 2) err -= B / 2;
        if (err < -B / 2) err += B / 2;
        return prod.add(new BigInt(err));
    }

    /**
     * Multiplies a matrix of small integers and a vector of big ones.
     * The matrix has a implicit leading row [1 0 ... 0] and an implicit trailing row [0 ... 0 1].
     * @param m The matrix.
     * @param v The vector.
     * @return m v
     */
    private BigInt[] transform(int[][] m, BigInt[] v) {
        BigInt[] b = new BigInt[m.length + 2];
        b[0] = v[0];
        for (int i = 0; i < m.length; i++) {
            BigInt s = ZERO;
            for (int j = 0; j < m[i].length; j++) s = s.add(v[j].mulSmall(m[i][j]));
            b[i + 1] = s;
        }
        b[b.length - 1] = v[v.length - 1];

        return b;
    }

    /**
     * Sums the digits of this integer.
     * @return The sum of the digits of this integer.
     */
    public long digsum() {
        long rv = 0;
        for (int i = 0; i < len; i++) {
            int x = val[off + i];
            while (x > 0) {
                rv += x % 10;
                x /= 10;
            }
        }
        return rv;
    }
}

बड़ी अड़चन भोले गुणन (60%) है, इसके बाद अन्य गुणन (37%) और sieving (3%) है। digsum()कॉल नगण्य है।

OpenJDK 7 (64 बिट) के साथ मापा गया प्रदर्शन।


बहुत अच्छा। धन्यवाद।

1

पायथन 2 (PyPy), 1,134,000 / 486,000 = 2.32

#/!usr/bin/pypy
n=input(); a, b, c=1, 1, 2**((n+2)/4)
for i in range(n-1, n/2, -2): a*=i
for i in range(2, n/4+1): b*=i
print sum(map(int, str(a*c/b)))

परिणाम: 1,537,506

मजेदार तथ्य: आपके कोड की अड़चन अंकों को जोड़ रही है, द्विपद गुणांक की गणना नहीं कर रही है।


अजगर अंकों को जोड़ने में इतना धीमा क्यों है? तुम और xnor दोनों कहते हैं कि यह है। इसने मुझे जिज्ञासु बनाया, इसलिए मैंने उसे देखा। यह योग भाग (जावा) के लिए एक सेकंड से भी कम समय में आया।
ज्योबिट्स

@Geobits हम्म, उत्सुक। क्या जावा भी बाइनरी-दशमलव रूपांतरण समान रूप से तेज करने में सक्षम है? यह बाइनरी में पूर्णांक का प्रतिनिधित्व करता है, है ना?
5

यह एक अच्छा सवाल है। पूर्णांक / पूर्णांक / लंबे / लंबे समय के लिए मुझे पता है कि यह द्विआधारी है। मुझे पूरी तरह से यकीन नहीं है कि बिगइन्टेगर का आंतरिक प्रतिनिधित्व क्या है। यदि यह दशमलव है, तो यह निश्चित रूप से समझाएगा कि यह गणित में धीमा क्यों है लेकिन एक स्ट्रिंग में बदलने के लिए तेज़ है। हो सकता है कि कल देखो।
ज्योबिट्स 4'14

@Geobits, BigInteger का आंतरिक प्रतिनिधित्व आधार 2 है।
पीटर टेलर

मैंने हमेशा ऐसा माना, लेकिन इसने मुझे आश्चर्यचकित कर दिया। ऐसा लगता है कि यह इसे लंबे आकार के टुकड़ों में तोड़ रहा है और इसे इस तरह परिवर्तित कर रहा है, कम से कम ओपनजेडके में।
ज्योबिट्स

1

जावा (2,020,000 / 491,000) = 4.11

अद्यतन, पहले 2.24

जावा BigIntegerसबसे तेज़ नंबर क्रंचर नहीं है, लेकिन यह कुछ भी नहीं से बेहतर है।

इसके लिए मूल सूत्र प्रतीत होता है n! / ((n/2)!^2), लेकिन यह निरर्थक गुणा का एक गुच्छा जैसा लगता है।

आप अंश और हर दोनों में पाए जाने वाले सभी प्रमुख कारकों को समाप्त करके एक महत्वपूर्ण गति प्राप्त कर सकते हैं। ऐसा करने के लिए, मैं सबसे पहले एक साधारण प्राइम छलनी चलाता हूं। फिर प्रत्येक प्रधान के लिए, मैं इस बात की गिनती रखता हूं कि उसे किस शक्ति को बढ़ाने की आवश्यकता है। वृद्धि हर बार मुझे अंश में एक कारक दिखाई देता है, हर के लिए वेतन वृद्धि।

मैं टवीस को अलग से (और पहले) संभालता हूं, क्योंकि फैक्टरिंग से पहले उन्हें गिनना / खत्म करना आसान है।

एक बार ऐसा करने के बाद, आपके पास आवश्यक न्यूनतम मात्रा में गुणा है, जो कि अच्छा है क्योंकि BigInt कई गुना धीमा है

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

public class CentBiCo {
    public static void main(String[] args) {
        int n = 2020000;
        long time = System.currentTimeMillis();
        sieve(n);
        System.out.println(sumDigits(cbc(n)));
        System.out.println(System.currentTimeMillis()-time);
    }

    static boolean[] sieve;
    static List<Integer> primes;
    static void sieve(int n){
        primes = new ArrayList<Integer>((int)(Math.sqrt(n)));
        sieve = new boolean[n];
        sieve[2]=true;
        for(int i=3;i<sieve.length;i+=2)
            if(i%2==1)
                sieve[i] = true;
        for(int i=3;i<sieve.length;i+=2){
            if(!sieve[i])
                continue;
            for(int j=i*2;j<sieve.length;j+=i)
                sieve[j] = false;
        }
        for(int i=2;i<sieve.length;i++)
            if(sieve[i])
                primes.add(i);
    }

    static int[] factors;
    static void addFactors(int n, int flip){
        for(int k=0;primes.get(k)<=n;){
            int i = primes.get(k);
            if(n%i==0){
                factors[i] += flip;
                n /= i;
            } else {
                if(++k == primes.size())
                    break;
            }
        }
        factors[n]++;
    }

    static BigInteger cbc(int n){
        factors = new int[n+1];
        int x = n/2;
        for(int i=x%2<1?x+1:x+2;i<n;i+=2)
            addFactors(i,1);
        factors[2] = x;
        for(int i=1;i<=x/2;i++){
            int j=i;
            while(j%2<1 && factors[2] > 1){
                j=j/2;
                factors[2]--;
            }
            addFactors(j,-1);
            factors[2]--;
        }
        BigInteger cbc = BigInteger.ONE;
        for(int i=3;i<factors.length;i++){
            if(factors[i]>0)
                cbc = cbc.multiply(BigInteger.valueOf(i).pow(factors[i]));
        }
        return cbc.shiftLeft(factors[2]);
    }

    static long sumDigits(BigInteger in){
        long sum = 0;
        String str = in.toString();
        for(int i=0;i<str.length();i++)
            sum += str.charAt(i)-'0';
        return sum;
    }
}

ओह, और 2735298सत्यापन प्रयोजनों के लिए n = 2020000 के लिए आउटपुट योग है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.