计算以下表达式值的最快算法和代码实现是什么?
N! /(q!) r
我的代码
public static double timesbyf(int n,int q,int qt,int qp1,int qp1t)
{
int totaltimes=qt+qp1t;
double ans=1.0d;
for(int i=1;i<=totaltimes;i++)
{
if(i<=qt)
{
for(int j=q;j>0;j--)
{
ans=ans*((double)n/(double)j);
n--;
}
}
else
{
for(int j=qp1;j>0;j--)
{
ans=ans*((double)n/(double)j);
n--;
}
}
}
while(n>0)
{
ans=(ans*n)%3046201;
n--;
}
return ans;
}
即n!
除以q!
r
次。
我给出n≤3×10 6 且q <1。 n,并保证(q!) r 将干净地划分n!。
答案 0 :(得分:5)
由于n的上限较低,因此可以通过将[1,3×10 6 ]范围内的所有数字分解来开始。有很多方法可以合理有效地完成这项工作。一种方法是使用Sieve of Eratosthenes或相关的筛子找到小于3×10 6 的所有素数,然后使用DP算法:标记1只有自己作为一个素数因子分解,然后对于每个数字2,3,4,5,6,...,3×10 6 ,尝试按顺序将这些数字除以质数,直到找到一个干净地分开,留下r的剩余部分。那么这个数的素因子化将是r的素数因子化,乘以你分出的素数。
一旦对所有这些数字进行素数分解,就可以有效地计算n的素数因子分解!使用数字1,2,3,...,n的素数因子分解。为此,您可以在数字1,2,3,...,n的因子分解中将相应素数的所有指数相加。您可以类似地计算q!的素数因子分解,然后通过乘以q的素因子分解中的所有指数来得到(q!) r 的素数因子分解!由r。
一旦你有这些主要的因素分解,你就可以计算n! /(q!) r 通过简单地对n的素因子分解中的所有指数进行成对减法!通过(q!) r 的素因子分解中的相应指数。然后你可以恢复n的值! /(q!) r 将所有这些数字相乘。
如果您需要一个确切的值,那么您可能会花费更多的工作将所有因素相乘而不是实际找到这些因素。如果你只需要一个大模数的模数值,那么这个方法将非常有效并且会给你一个确切的答案,只要你通过将所有因子相乘的那个大的素数进行修正。
希望这有帮助!
答案 1 :(得分:1)
我认为计算两个非常大的数字是一个非常糟糕的主意,并希望商出来是明智的。
首先采用自然日志:
ln(n!/(q!)^r) = ln(n!) - r*ln(q!)
您可以使用gammaln()
作为两个函数值,简化,然后使用exp()
获得您想要的结果:
value = exp(gammln(n+1) -r*gammln(q+1))
Numerical Recipes有一个关于如何实现gammln().
答案 2 :(得分:1)
如果您确实需要标准类型中的完整结果(不是以某些数字为模的结果),我想提出另一种解决方案,例如long。你可以跳过一些计算知道它们会溢出。
第一个案例: n很小,所以q很小(所以假设它干净地划分n!),计算很容易, 例如使用@templatetypedef answer。
第二个案例
n很大,q相对于n很小:结果是无穷大。
要确定q是否相对于n较小,请使用Stirling Formula。所以如果结果:
exp((n*ln(n)-n)/(q*ln(q)-q)^r))
双重计算时Infinity
,无需实际计算结果。特别是假设你想要一个长的确切结果。
第三种情况: n很大,q也很大。 @templatetypedef也适用。
显然,如果你不需要确切的结果,当n和q足够大时,你可以简单地使用斯特林近似。更准确地说,使用Ramanujan's version。