কেন্দ্রীয় দ্বিপদী সহগের অঙ্কের যোগফল


13

অজগরের বিল্টিন ফাংশনের চেয়ে আপনি কত দ্রুত এন বেছে বেছে এন / 2 (এমনকি এন এর জন্যও) গণনা করতে পারবেন তা সহজভাবে কাজ। অবশ্যই বড় এন এর জন্য এটি একটি বৃহত্তর সংখ্যা তাই আউটপুট না করে পুরো সংখ্যাটি আপনার অঙ্কগুলির যোগফলকে আউটপুট করা উচিত। উদাহরণস্বরূপ, এর জন্য 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 <= (আপনার সর্বোচ্চ এন)

আপনি এমন কোন বিল্টিন কোড বা লাইব্রেরি ব্যবহার করতে পারবেন না যা দ্বিপদী সহগ বা মানগুলি গণনা করে যা দ্রুত দ্বিপদী সহগগুলিতে রূপান্তরিত হতে পারে।

আপনি আপনার পছন্দের যে কোনও ভাষা ব্যবহার করতে পারেন।


শীর্ষস্থানীয় উত্তর একটি চমত্কার 680.09 সহ বর্তমান শীর্ষস্থানীয় জাস্টহেলফ দ্বারা।


2
আমাদের কী অজগর বা পছন্দের ভাষায় সমাধান জমা দেওয়ার কথা?

একটি আধুনিক কম্পিউটারে এটি করে এমন একটি রুটিন লেখা সম্ভব এবং nলক্ষ লক্ষ লোকের মধ্যে ভাল লাগবে , যদিও আমি সন্দেহ করি পাইথন ফাংশনটি n = 1e5দমবন্ধ না করেই এর চেয়ে বড় আরও কিছু পরিচালনা করতে পারে ।
COTO

@ আলেসান্দ্রো আপনি আপনার পছন্দের যে কোনও ভাষা ব্যবহার করতে পারেন। কেবলমাত্র সীমাবদ্ধতা হ'ল আপনি সহগের গুণাগুণ গণনা করতে বিল্টিন ফাংশন ব্যবহার করতে পারবেন না।

2
ফ্যাক্টরিয়াল ফাংশন অনুমোদিত? আমি ধরে নিই যেহেতু তারা "দ্রুত দ্বিপদী সহগতে রূপান্তরিত হতে পারে" (পুরো জিনিসটি কেবল একটি ফ্যাকটোরিয়াল যা অন্য ফ্যাক্টরিয়াল স্কোয়ার দ্বারা বিভক্ত), তবে যেহেতু একটি উত্তর এখন একটি ব্যবহার করছে, তাই স্পষ্টতা সুন্দর হবে।
জিওবিটস

1
@ কমন্টার্ন: আমি রেফারেন্সটির সেই পয়েন্টটি সাফল্যের সাথে 1 মিনিটে 287 মিলিল বা 35 সেকেন্ডে 169 মিলিলের সাথে প্রতিলিপি করেছি! :)
justhalf

উত্তর:


9

সি ++ (জিএমপি) - (287,000,000 / 422,000) = 680.09

নির্লজ্জভাবে xnor দ্বারা কুমারের উপপাদ্য এবং qwr দ্বারা GMP যুক্ত করুন। এখনও গো সমাধানের কাছাকাছি নয়, কেন তা নিশ্চিত নয়।

সম্পাদনা করুন: কেইথ র্যান্ডালকে এই অনুস্মারকটির জন্য ধন্যবাদ যে সংখ্যাটি আকারে সমান হলে গুণগুলি দ্রুত হয়। আমি মেমরি ম্যানেজমেন্টে মেমরি কোয়েলসিং ধারণার অনুরূপ বহু-স্তরের গুণকে প্রয়োগ করেছি। এবং ফলাফল চিত্তাকর্ষক। 51 সেকেন্ডে যা ব্যবহৃত হত, এখন কেবল 0.5 সেমি লাগে (যেমন, 100-গুণ উন্নতি !!)

পুরানো কোড (n = 14,000,000)
0.343 এর দশকে ছাঁটাই করা শেষ
51.929 এর মধ্যে বিনম গণনা শেষ হয়েছে
0.901 সেকেন্ডে সমষ্টি শেষ হয়েছে
14000000: 18954729

বাস্তব 0m53.194s
ব্যবহারকারী 0m53.116s
ss 0m0.060s

নতুন কোড (এন = 14,000,000)
0.343 এর দশকে ছাঁটাই করা শেষ
0.552 সেকেন্ডে বিনোমের গণনা শেষ হয়েছে
0.902 সেকেন্ডে সমষ্টি শেষ হয়েছে
14000000: 18954729

আসল 0 মি 1.804 এস
ব্যবহারকারী 0 মি 1.776 এস
sys 0m0.023s

জন্য রান n=287,000,000

4.211 সেকেন্ডে কাজ শেষ হয়েছে
17.934 এর মধ্যে বিনম গণনা সম্পন্ন হয়েছে
37.677 এর দশকে যোগফল শেষ হয়েছে
287000000: 388788354

আসল 0m59.928s
ব্যবহারকারী 0m58.759s
ss 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
উভয় অপারেন্ড একই আকারের হলে গুণকগুলি আরও কার্যকর efficient আপনি সর্বদা অল্প সংখ্যায় বহু সংখ্যার গুণ করছেন। যদি আপনি বারে সংখ্যায় সংখ্যক সংখ্যক সংমিশ্রণ করেন তবে এটি দ্রুততর হতে পারে (তবে আরও স্মৃতি গ্রহণ করুন)।
কিথ র্যান্ডাল

বাহ, এটি একটি সম্পূর্ণ অনেক পার্থক্য করে। এটি দ্রুততর দ্রুত। আমি 35 সেকেন্ডে এখন 169 মিলি পৌঁছাতে পারি।
justhalf

বাহ! আপনার কোডের বিভিন্ন অংশের জন্য সময়ে ব্রেকডাউন কী?

আমি ইতিমধ্যে আমার উত্তরে এটি রেখেছি। nকেন্দ্রীয় দ্বিপদী সহগের গণনা করে 18s অবধি প্রাইম তৈরিতে 4s এবং ফলাফলটি স্ট্রিংয়ে রূপান্তর করতে এবং অঙ্কের সংমিশ্রণে বাকি 37 গুলি।
justhalf

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
}

অংক এবং ডিনোমিনেটরে সমস্ত মৌলিক গুণক গণনা করে এবং ম্যাচিংয়ের কারণগুলি বাতিল করে কাজ করে। ফলাফল পেতে বাম ওভারকে গুণিত করে।

১০% বেসে রূপান্তরিত করতে ৮০% এরও বেশি সময় ব্যয় করা হয়, এটি করার আরও একটি ভাল উপায় হতে পারে ...


বেস 10 এ বড় সংখ্যক মুদ্রণের প্রয়োজন হয় এমন সমস্যাগুলির জন্য আমি সাধারণত আমার নিজের বিগইন্টিজার ক্লাসটি লিখতে সহায়ক বলে মনে করি যা সংখ্যা 1E9 ~ 2 ^ 30 ভিত্তিতে নম্বর সঞ্চয় করে।
পিটার টেলর

আপনি বর্তমানে একটি দেশের মাইল জিতেছেন .. তারা যেমন বলেছে।

@ পিটারটেলর: আমি চেষ্টা করেছিলাম, তবে এর জন্য বহুগুণ কোডে প্রচুর% 1e9 প্রয়োজন, যা গুণকে ধীর করে তোলে।
কিথ র্যান্ডাল

6

পাইথন 3 (8.8 = 2.2 মিলিয়ন / 0.25 মিলিয়ন)

এটি পাইথনে, যা গতির জন্য পরিচিত নয়, তাই আপনি সম্ভবত এটি অন্য ভাষায় পোর্টিং করতে আরও ভাল করতে পারেন।

এই স্ট্যাক ওভারফ্লো প্রতিযোগিতা থেকে নেওয়া প্রাইমস জেনারেটর ।

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যদিও পুরো স্ট্রিংটি সম্ভবত মেমরিতে রাখা উচিত।

প্রাইমগুলি তৈরি করা নগণ্য সময় নেয় (এবং তাই আমি বিদ্যমান কোডটি অনুলিপি করে অনুভব করি না)। সামিং অঙ্কগুলি দ্রুত is আসল গুণটি সময়ের এক তৃতীয়াংশ সময় নেয়।

অঙ্কের যোগফলটি সীমাবদ্ধ ফ্যাক্টর হিসাবে মনে হচ্ছে, সম্ভবত দশমিক উপস্থাপনায় সংখ্যাগুলি গুণিত করার জন্য একটি অ্যালগরিদম বাইনারি / দশমিক রূপান্তরকে শর্টকাট করে মোট সময় সাশ্রয় করবে।


এটি অত্যন্ত চিত্তাকর্ষক এবং আপনাকে আশ্চর্য করে তোলে যে কেন সিপিথন আপনার বাস্তবায়ন ব্যবহার করে না!

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.07 এর স্কোর হবে। এটি জাভা 1.7 ( javac CodeGolf37270.java) এর সাথে সংকলন করছে এবং জাভা 1.8 ( java CodeGolf37270 n) এর সাথে নির্বাহ করছে । আমি নিশ্চিত নই যে এমন কোনও অপ্টিমাইজেশন বিকল্প রয়েছে যা সম্পর্কে আমি অজানা। আমি জাভা ১.৮ এর সাথে সংকলনের চেষ্টা করতে পারি না, কারণ এটি আমার জাভা প্যাকেজের সাথে ইনস্টল হয় না ...
ডেনিস

আকর্ষণীয় পদ্ধতির। আপনি কেন ভাবেন যে এটিকে পুনরাবৃত্তভাবে গণনা করা সহজ সূত্রটি ব্যবহার করার চেয়ে দ্রুততর হতে পারে?
justhalf

@ অ্যাডেজাল্ফ, এটি দ্রুত হবে কি না সে সম্পর্কে আমার কোনও অন্তর্দৃষ্টি ছিল না এবং আমি জটিলতার গণনা করার চেষ্টা করিনি। বেস -10 সংখ্যার উত্তোলনের জন্য অনুকূলিত কাস্টম বড় পূর্ণসংখ্যার শ্রেণীর সাথে প্রয়োগ করা সহজ হবে এমন সূত্রগুলি সন্ধান করার জন্য আমি কেন্দ্রীয় দ্বিপদী সহগের জন্য পরিচয়ের তালিকাগুলি সন্ধান করেছি। এবং এটি খুব দক্ষ নয় বলে আবিষ্কার করে আমি এটি পোস্ট করে এবং অন্য কাউকে পরীক্ষার পুনরাবৃত্তি থেকে বাঁচাতে পারি। (এফডাব্লুআইডাব্লু আমি টুম গুণনের কাজ করছি, তবে আমি কখন এটি পরীক্ষা করব এবং ডিবাগ করব তা নিশ্চিত নই)।
পিটার টেলর

2

জিএমপি - 1500000/300000 = 5.0

যদিও এই উত্তরটি চালকদের বিরুদ্ধে প্রতিদ্বন্দ্বিতা করবে না, কখনও কখনও সংক্ষিপ্ত কোড এখনও ফলাফল পেতে পারে।

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

বড় বাধা হ'ল নির্বিঘ্ন গুণ (%০%), তার পরে অন্যান্য গুণ (% 37%) এবং গ্রেপ্তার (৩%) হয়। digsum()কল নগণ্য।

ওপেনজেডকে 7 (64 বিট) দিয়ে পরিমাপকৃত পারফরম্যান্স।


খুব সুন্দর. ধন্যবাদ.

1

পাইথন 2 (পাইপাই), 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 উভয়ই এটি বলে। এটি আমাকে কৌতূহলী করে তোলে, তাই আমি আমারকে আটকে রেখেছিলাম। যোগফল (জাভা) এর জন্য এটি এক সেকেন্ডেরও কম সময়ে এসেছিল।
জিওবিটস

@ জিওবিটস হুঁ, কৌতূহলী। জাভা কি একইভাবে দ্রুত বাইনারি-দশমিক রূপান্তর করতে সক্ষম? এটি বাইনারি মধ্যে পূর্ণসংখ্যার প্রতিনিধিত্ব করে, তাই না?
xnor

এটা একটা ভালো প্রশ্ন. পূর্ণসংখ্যা / পূর্ণসংখ্যার / দীর্ঘ / দীর্ঘ জন্য আমি জানি এটি বাইনারি। বিগইন্টিজারের অভ্যন্তরীণ উপস্থাপনা কী তা আমি ঠিক নিশ্চিত নই। যদি এটি দশমিক হয়, তবে এটি অবশ্যই ব্যাখ্যা করবে যে এটি কেন গণিতে ধীরে ধীরে তবে স্ট্রিনে রূপান্তরিত করতে দ্রুত। আগামীকাল এটি দেখতে পারে।
জিওবিটস

@ জিওবিটস, বিগইন্টিজারের অভ্যন্তরীণ উপস্থাপনা বেস 2
পিটার টেলর

আমি সবসময় এটি ধরে নিয়েছিলাম তবে এটি আমাকে অবাক করে দিয়েছে। দেখে মনে হচ্ছে এটি দীর্ঘ মাপের অংশগুলিতে এটি ভেঙে চলেছে এবং কমপক্ষে ওপেনজেডিকে way ভাবে রূপান্তর করছে।
জিওবিটস

1

জাভা (2,020,000 / 491,000) = 4.11

আপডেট হয়েছে, পূর্বে 2.24

জাভা BigIntegerদ্রুততম নম্বর ক্রাঙ্কার নয়, তবে এটি কোনও কিছুর চেয়ে ভাল।

এটির জন্য প্রাথমিক সূত্রটি মনে হয় n! / ((n/2)!^2)তবে এটি অপ্রয়োজনীয় গুণকের একগুচ্ছ বলে মনে হয়।

আপনি উভয় অংকের এবং ডিনোমিনেটরে পাওয়া সমস্ত মৌলিক বিষয়গুলি বাদ দিয়ে একটি গুরুত্বপূর্ণ গতি অর্জন করতে পারেন। এটি করার জন্য, আমি প্রথমে একটি সাধারণ প্রাইম চালুনি চালাই। তারপরে প্রতিটি প্রধানমন্ত্রীর জন্য, এটিকে কী উত্থাপন করা উচিত তা আমি একটি গণনা রাখি। প্রতিবার আমি অঙ্কের একটি ফ্যাক্টর দেখি, বর্ধনের জন্য হ্রাস বৃদ্ধি করে।

আমি পৃথকভাবে দুটি (এবং প্রথম) পরিচালনা করি, কারণ ফ্যাক্টরিংয়ের আগে এগুলি গণনা / নির্মূল করা সহজ easy

এটি শেষ হয়ে গেলে, আপনার কাছে ন্যূনতম পরিমাণে প্রয়োজনীয় গুণাগুলি রয়েছে, যা ভাল কারণ বিগিয়ান্টের গুণটি ধীর

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

ওহ, এবং এন = 2020000 এর আউটপুট যোগফল 2735298যাচাইকরণের জন্য।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.