我试图解决涉及模数为素数的大因子的问题,并在另一个解决方案中找到以下算法:
long long factMod (long long n, long long p)
{
long long ans = 1;
while (n > 1)
{
long long cur = 1;
for (long long i = 1; i < p; i++)
{
cur = (cur * i) % p;
}
ans = (ans * modPow(cur, n/p, p)) % p;
for (long long i = 1; i <= n % p; i++)
{
ans = (ans * i) % p;
}
n /= p;
}
return (ans % p);
}
long long nChooseK(long long n, long long k, long long p)
{
int num_degree = get_degree(n, p) - get_degree(n - k, p);
int den_degree = get_degree(k, p);
if (num_degree > den_degree) { return 0; }
long long nFact = factMod(n, p);
long long kFact = factMod(k, p);
long long nMinusKFact = factMod(n-k, p);
long long ans = (((nFact * modPow(kFact, p - 2, p)) % p) * modPow(nMinusKFact, p - 2, p))%p;
return ans;
}
我知道数论的基础知识,但似乎无法弄清楚它是如何工作的。
nChooseK函数似乎使用组合[n!/(n-k)!k!]的定义与使用费马小定理计算的模逆,来代替除法。但是,根据其中一个答案,factMod函数实际上并不计算阶乘。如果是这种情况,nChooseK功能如何工作?
答案 0 :(得分:1)
是的,是的! ≡0mod p当且仅当n≥p,但factMod
不计算n! mod p - 它计算n!/ p k mod p其中k是n!的素数因式分解中p的指数,或许为目的计算二项式系数。循环的迭代i(从0开始计数)计算那些因子1 ... n的贡献,其素因子化包括p i 。语句n /= p;
产生p的倍数的子问题。
函数get_degree(n, p)
可能在n!的素数因子分解中返回p的指数。如果get_degree(n, p) == get_degree(k, p) + get_degree(n - k, p)
,则分子和分母中的p因子完全取消,我们可以使用factMod
来考虑其他因素。否则,组合的数量可以被p整除,所以我们返回0。
自(p-1)以来! ≡-1 mod Wilson's theorem,第一个内部循环是多余的。