![](/img/trans.png)
[英]Coding Competitions: How to store large numbers and find its all combination modulus P
[英]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 并且该逆是唯一的这一事实。 此外,我们可以很快找到逆。 另一个问题在这里有一个关于如何做到这一点的答案,我在下面复制了它。
这很方便,因为:
稍微修改其他代码,并概括我们有以下问题:
#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;
}
如果n
和r
的值不太高,它将适用于编程比赛要求的大多数情况
时间复杂度:- 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.