Fastest way to generate binomial coefficients

后端 未结 11 1651
[愿得一人]
[愿得一人] 2020-12-13 02:20

I need to calculate combinations for a number.

What is the fastest way to calculate nCp where n>>p?

I need a fast way to generate binomial coefficients for a

相关标签:
11条回答
  • 2020-12-13 02:58

    If you really only need the case where n is much larger than p, one way to go would be to use the Stirling's formula for the factorials. (if n>>1 and p is order one, Stirling approximate n! and (n-p)!, keep p! as it is etc.)

    0 讨论(0)
  • 2020-12-13 03:02

    I recently wrote a piece of code that needed to call for a binary coefficient about 10 million times. So I did a combination lookup-table/calculation approach that's still not too wasteful of memory. You might find it useful (and my code is in the public domain). The code is at

    http://www.etceterology.com/fast-binomial-coefficients

    It's been suggested that I inline the code here. A big honking lookup table seems like a waste, so here's the final function, and a Python script that generates the table:

    extern long long bctable[]; /* See below */
    
    long long binomial(int n, int k) {
        int i;
        long long b;
        assert(n >= 0 && k >= 0);
    
        if (0 == k || n == k) return 1LL;
        if (k > n) return 0LL;
    
        if (k > (n - k)) k = n - k;
        if (1 == k) return (long long)n;
    
        if (n <= 54 && k <= 54) {
            return bctable[(((n - 3) * (n - 3)) >> 2) + (k - 2)];
        }
        /* Last resort: actually calculate */
        b = 1LL;
        for (i = 1; i <= k; ++i) {
            b *= (n - (k - i));
            if (b < 0) return -1LL; /* Overflow */
            b /= i;
        }
        return b;
    }
    

    #!/usr/bin/env python3
    
    import sys
    
    class App(object):
        def __init__(self, max):
            self.table = [[0 for k in range(max + 1)] for n in range(max + 1)]
            self.max = max
    
        def build(self):
            for n in range(self.max + 1):
                for k in range(self.max + 1):
                    if k == 0:      b = 1
                    elif  k > n:    b = 0
                    elif k == n:    b = 1
                    elif k == 1:    b = n
                    elif k > n-k:   b = self.table[n][n-k]
                    else:
                        b = self.table[n-1][k] + self.table[n-1][k-1]
                    self.table[n][k] = b
    
        def output(self, val):
            if val > 2**63: val = -1
            text = " {0}LL,".format(val)
    
            if self.column + len(text) > 76:
                print("\n   ", end = "")
                self.column = 3
            print(text, end = "")
            self.column += len(text)
    
        def dump(self):
            count = 0
            print("long long bctable[] = {", end="");
    
            self.column = 999
            for n in range(self.max + 1):
                for k in range(self.max + 1):
                    if n < 4 or k < 2 or k > n-k:
                        continue
                    self.output(self.table[n][k])
                    count += 1
            print("\n}}; /* {0} Entries */".format(count));
    
        def run(self):
            self.build()
            self.dump()
            return 0
    
    def main(args):
        return App(54).run()
    
    if __name__ == "__main__":
        sys.exit(main(sys.argv))
    
    0 讨论(0)
  • 2020-12-13 03:04

    If you need to compute them for all n, Ribtoks's answer is probably the best. For a single n, you're better off doing like this:

    C[0] = 1
    for (int k = 0; k < n; ++ k)
        C[k+1] = (C[k] * (n-k)) / (k+1)
    

    The division is exact, if done after the multiplication.

    And beware of overflowing with C[k] * (n-k) : use large enough integers.

    0 讨论(0)
  • 2020-12-13 03:05

    I was looking for the same thing and couldn't find it, so wrote one myself that seems optimal for any Binomial Coeffcient for which the endresult fits into a Long.

    // Calculate Binomial Coefficient 
    // Jeroen B.P. Vuurens
    public static long binomialCoefficient(int n, int k) {
        // take the lowest possible k to reduce computing using: n over k = n over (n-k)
        k = java.lang.Math.min( k, n - k );
    
        // holds the high number: fi. (1000 over 990) holds 991..1000
        long highnumber[] = new long[k];
        for (int i = 0; i < k; i++)
            highnumber[i] = n - i; // the high number first order is important
        // holds the dividers: fi. (1000 over 990) holds 2..10
        int dividers[] = new int[k - 1];
        for (int i = 0; i < k - 1; i++)
            dividers[i] = k - i;
    
        // for every dividers there is always exists a highnumber that can be divided by 
        // this, the number of highnumbers being a sequence that equals the number of 
        // dividers. Thus, the only trick needed is to divide in reverse order, so 
        // divide the highest divider first trying it on the highest highnumber first. 
        // That way you do not need to do any tricks with primes.
        for (int divider: dividers) {
           boolean eliminated = false;
           for (int i = 0; i < k; i++) {
              if (highnumber[i] % divider == 0) {
                 highnumber[i] /= divider;
                 eliminated = true;
                 break;
              }
           }
           if(!eliminated) throw new Error(n+","+k+" divider="+divider);
        }
    
    
        // multiply remainder of highnumbers
        long result = 1;
        for (long high : highnumber)
           result *= high;
        return result;
    }
    
    0 讨论(0)
  • 2020-12-13 03:08
    nCp = n! / ( p! (n-p)! ) =
          ( n * (n-1) * (n-2) * ... * (n - p) * (n - p - 1) * ... * 1 ) /
          ( p * (p-1) * ... * 1     * (n - p) * (n - p - 1) * ... * 1 )
    

    If we prune the same terms of the numerator and the denominator, we are left with minimal multiplication required. We can write a function in C to perform 2p multiplications and 1 division to get nCp:

    int binom ( int p, int n ) {
        if ( p == 0 ) return 1;
        int num = n;
        int den = p;
        while ( p > 1 ) {
            p--;
            num *= n - p;
            den *= p;
        }
        return num / den;
    }
    
    0 讨论(0)
  • 2020-12-13 03:09

    You cau use dynamic programming in order to generate binomial coefficients

    You can create an array and than use O(N^2) loop to fill it

    C[n, k] = C[n-1, k-1] + C[n-1, k];
    

    where

    C[1, 1] = C[n, n] = 1
    

    After that in your program you can get the C(n, k) value just looking at your 2D array at [n, k] indices

    UPDATE smth like that

    for (int k = 1; k <= K; k++) C[0][k] = 0;
    for (int n = 0; n <= N; n++) C[n][0] = 1;
    
    for (int n = 1; n <= N; n++)
       for (int k = 1; k <= K; k++)
          C[n][k] = C[n-1][k-1] + C[n-1][k];
    

    where the N, K - maximum values of your n, k

    0 讨论(0)
提交回复
热议问题