繁体   English   中英

求大数的组合值

[英]To find combination value of large numbers

我想为大整数找到(n 选择 r),并且我还必须找出该数字的模数。

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。 如果有其他方法可以做到这一点,请告诉我。

我还没有评论的声誉,但我想指出,rock321987 的答案非常有效:

它在 C(62, 31) 之前是快速正确的

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

C(67, 33) = 14,226,520,737,620,288,370 (验证正确性和大小)

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

请注意,p = 10000007 是质数,这使我们能够利用所有整数都有一个逆模 p 并且该逆是唯一的这一事实。 此外,我们可以很快找到逆。 另一个问题在这里有一个关于如何做到这一点的答案,我在下面复制了它。

这很方便,因为:

  1. x/y mod p == x*(y inverse) mod p;
  2. xy mod p == (x mod p)(y mod p)

稍微修改其他代码,并概括我们有以下问题:

#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 远小于 (nr) 其中 r <= nr,则仍有改进的余地。 例如,我们可以预先计算所有的倒数 mod p,而不是多次按需计算。

nCr = n! / (r!* (nr)!) {! =阶乘}

现在选择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(r, (n - r));//minimum of r,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 > 60(大约)

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM