我想找到(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。如果还有其他方法,请告诉我。
答案 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,我已在下面复制过。
这很方便,因为:
稍微修改其他代码,并概括我们遇到以下问题:
#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;
}
如果n
和r
的值不太高
时间复杂度: - 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;
}