找到大数的组合值

时间:2014-06-27 06:00:08

标签: c++ combinations

我想找到(n选择r)大整数,我也必须找出那个数字的mod。

long long int choose(int a,int b)
{
    if (b > a)
        return (-1);
    if(b==0 || a==1 || b==a)
        return(1);
    else
    {
        long long int r = ((choose(a-1,b))%10000007+(choose(a-1,b-  1))%10000007)%10000007;
        return r;
    }
}

我正在使用这段代码,但我得到了TLE。如果还有其他方法,请告诉我。

3 个答案:

答案 0 :(得分:5)

我还没有评论的声誉,但我想指出rock321987的答案效果很好:

  

正确直至并包括C(62,31)

无法处理输出适合uint64_t的所有输入。作为证据,请尝试:

  

C(67,33)= 14,226,520,737,620,288,370 (verify correctness and size)

不幸的是,另一个实现吐出了8,829,174,638,479,413,这是不正确的。还有其他计算nCr的方法不会像这样破坏,但真正的问题是没有尝试利用模数。

请注意,p = 10000007是素数,这允许我们利用所有整数具有逆模p的事实,并且该逆是唯一的。此外,我们可以很快发现逆。另一个问题有关于如何执行此操作的答案here,我已在下面复制过。

这很方便,因为:

  1. x / y mod p == x *(y inverse)mod p;和
  2. xy mod p ==(x mod p)(y mod p)
  3. 稍微修改其他代码,并概括我们遇到以下问题:

    #include <iostream>
    #include <assert.h>
    
    // p MUST be prime and less than 2^63
    uint64_t inverseModp(uint64_t a, uint64_t p) {
        assert(p < (1ull << 63));
        assert(a < p);
        assert(a != 0);
        uint64_t ex = p-2, result = 1;
        while (ex > 0) {
            if (ex % 2 == 1) {
                result = (result*a) % p;
            }
            a = (a*a) % p;
            ex /= 2;
        }
        return result;
    }
    
    // p MUST be prime
    uint32_t nCrModp(uint32_t n, uint32_t r, uint32_t p)
    {
        assert(r <= n);
        if (r > n-r) r = n-r;
        if (r == 0) return 1;
        if(n/p - (n-r)/p > r/p) return 0;
    
        uint64_t result = 1; //intermediary results may overflow 32 bits
    
        for (uint32_t i = n, x = 1; i > r; --i, ++x) {
            if( i % p != 0) {
                result *= i % p;
                result %= p;
            }
            if( x % p != 0) {
                result *= inverseModp(x % p, p);
                result %= p;
            }
        }
        return result;
    }
    
    int main() {
        uint32_t smallPrime = 17;
        uint32_t medNum = 3001;
        uint32_t halfMedNum = medNum >> 1;
        std::cout << nCrModp(medNum, halfMedNum, smallPrime) << std::endl;
    
        uint32_t bigPrime = 4294967291ul; // 2^32-5 is largest prime < 2^32
        uint32_t bigNum = 1ul << 24;
        uint32_t halfBigNum = bigNum >> 1;
        std::cout << nCrModp(bigNum, halfBigNum, bigPrime) << std::endl;
    }
    

    如果您愿意等待,那么应该为任何一组32位输入产生结果。为了证明这一点,我已经包括了24位n和最大32位素数的计算。我的微软电脑需要大约13秒才能计算出来。检查wolfram alpha的答案,但要注意它可能超过那里的“标准计算时间”。

    如果p远小于(n-r),其中r <= n-r,则仍有改进的余地。例如,我们可以预先计算所有逆变量而不是按需执行多次。

答案 1 :(得分:2)

  

nCr = n! /(r!*(n-r)!){! = factorial}

现在以这样的方式选择r or n - r,其中任何一个都是最小的

#include <cstdio>
#include <cmath>

#define MOD 10000007

int main()
{
    int n, r, i, x = 1;
    long long int res = 1;
    scanf("%d%d", &n, &r);
    int mini = fmin(n, (n - r));

    for (i = n;i > mini;i--) {
        res = (res * i) / x;
        x++;
    }
    printf("%lld\n", res % MOD);
    return 0;
}

如果nr的值不太高

,它将适用于编程竞赛所要求的大多数情况

时间复杂度: - O(min(r,n - r))

限制: - 对于像C / C ++等语言,如果

会出现溢出
  

n&gt; 60(约)

因为没有数据类型可以存储最终值..

答案 2 :(得分:0)

nCr的膨胀总是可以减小为整数的乘积。这是通过消除分母中的项来完成的。此方法适用于以下功能。

此函数的时间复杂度为O(n ^ 2 * log(n))。这将在1秒内计算n <= 10000的nCr%m。

#include <numeric>
#include <algorithm>
int M=1e7+7;
int ncr(int n, int r)
{
    r=min(r,n-r);
    int A[r],i,j,B[r];
    iota(A,A+r,n-r+1);  //initializing A starting from n-r+1 to n
    iota(B,B+r,1);      //initializing B starting from 1 to r

    int g;
    for(i=0;i<r;i++)
    for(j=0;j<r;j++)
    {
        if(B[i]==1)
            break;
        g=__gcd(B[i], A[j] );
        A[j]/=g;
        B[i]/=g;
    }
    long long ans=1;
    for(i=0;i<r;i++)
        ans=(ans*A[i])%M;
    return ans;
}