我刚看到这个问题而不知道如何解决它。你可以请我提供算法,C ++代码或想法吗?
这是一个非常简单的问题。给定N和K的值,您需要告诉我们二项式系数C(N,K)的值。您可以放心,K <= N且N的最大值是1,000,000,000,000,000。由于该值可能非常大,因此您需要以1009计算结果模数。 输入
输入的第一行包含测试用例T的数量,最多为1000.下一个T行中的每一行由两个空格分隔的整数N和K组成,其中0 <= K <= N且1&lt; ; = N <= 1,000,000,000,000,000。 输出
对于每个测试用例,在新行上打印,二项式系数C(N,K)的模数为1009。 实施例
输入:
3
3 1
5 2
10 3输出:
3
10个
120个
答案 0 :(得分:27)
请注意,1009是素数。
现在您可以使用Lucas' Theorem。
哪个州:
Let p be a prime.
If n = a1a2...ar when written in base p and
if k = b1b2...br when written in base p
(pad with zeroes if required)
Then
(n choose k) modulo p = (a1 choose b1) * (a2 choose b2) * ... * (ar choose br) modulo p.
i.e. remainder of n choose k when divided by p is same as the remainder of
the product (a1 choose b1) * .... * (ar choose br) when divided by p.
Note: if bi > ai then ai choose bi is 0.
因此,您的问题减少到找到格式为1009的最多log N / log 1009数字(基数1009中N的位数)的形式a选择b其中a&lt; = 1009且b&lt; = 1009。
即使N接近10 ^ 15,这也应该更容易。
注意:
对于N = 10 ^ 15,N选择N / 2大于 这是2 ^(100000000000000)的方式 超出无条件的长期。
此外,该算法建议 卢卡斯定理是O(log N)
exponentially
比尝试更快 计算二项式系数 直接(即使你做了一个mod 1009 照顾溢出问题。)
以下是我曾经写过的Binomial的一些代码,你需要做的就是修改它来做模数1009的操作(可能有bug,不一定推荐编码风格):
class Binomial
{
public:
Binomial(int Max)
{
max = Max+1;
table = new unsigned int * [max]();
for (int i=0; i < max; i++)
{
table[i] = new unsigned int[max]();
for (int j = 0; j < max; j++)
{
table[i][j] = 0;
}
}
}
~Binomial()
{
for (int i =0; i < max; i++)
{
delete table[i];
}
delete table;
}
unsigned int Choose(unsigned int n, unsigned int k);
private:
bool Contains(unsigned int n, unsigned int k);
int max;
unsigned int **table;
};
unsigned int Binomial::Choose(unsigned int n, unsigned int k)
{
if (n < k) return 0;
if (k == 0 || n==1 ) return 1;
if (n==2 && k==1) return 2;
if (n==2 && k==2) return 1;
if (n==k) return 1;
if (Contains(n,k))
{
return table[n][k];
}
table[n][k] = Choose(n-1,k) + Choose(n-1,k-1);
return table[n][k];
}
bool Binomial::Contains(unsigned int n, unsigned int k)
{
if (table[n][k] == 0)
{
return false;
}
return true;
}
答案 1 :(得分:8)
二项式系数是一个因子除以另外两个因子,尽管底部的k!
项以明显的方式取消。
观察如果1009(包括它的倍数)在分子中出现的次数多于分母,则答案mod 1009为0.在分母中出现的次数不能超过分子(因为二项式系数)是整数),因此你必须做任何事情的唯一情况是它们在两者中出现的次数相同。不要忘记将(1009)^ 2的倍数计算为两个,依此类推。
在那之后,我认为你只是在清理小案件(意味着少量的数值乘以/除),虽然我不确定没有几个测试。在正面1009是素数,因此算术模1009在一个场中发生,这意味着在从顶部和底部输出1009的倍数之后,你可以按任何顺序完成乘法和除法mod 1009的其余部分。 / p>
如果还有非小的情况,它们仍然会将长连续的整数相乘。知道1008! (mod 1009)
可以简化这一过程。它是-1(如果您愿意,则为1008),因为1 ... 1008是p-1
上的素数字段的p
非零元素。因此,它们由1,-1和(p-3)/2
对乘法反转组成。
因此,例如考虑C((1009 ^ 3),200)的情况。
想象一下,1009的数量是相等的(不知道它们是否相同,因为我没有编写公式来查明),所以这是一个需要工作的情况。
在顶部我们有201 ... 1008,我们必须在预先计算的表中计算或查找,然后是1009,然后是1010 ...... 2017,2018,2019 ... 3026,3027等......范围都是-1,所以我们只需要知道有多少这样的范围。
那留下1009,2018,3027,一旦我们用1009从底部取消它们将只是1,2,3,...... 1008,1010,......,加上一些1009 ^ 2的倍数,我们再次取消并留下连续整数来增加。
我们可以做一些与底部非常相似的东西来计算产品mod 1009的“1 ... 1009 ^ 3 - 200,其中1009的所有功率被分开”。这使我们在一个主要领域中分裂。 IIRC原则上很棘手,但1009是一个足够小的数字,我们可以管理其中的1000个(测试用例数量的上限)。
当然,当k = 200时,存在巨大的重叠,可以更直接地取消。这就是我所说的小案例和非小案例:我把它当作一个非小案例,事实上我们可以通过计算((1009^3-199) * ... * 1009^3) / 200!
<来勉强“蛮力”这个案例/ p>
答案 2 :(得分:5)
我认为你不想计算C(n,k)然后减去mod 1009.最大的一个,C(1e15,5e14)将需要像1e16位~1000TB
此外,执行snakiles循环回答1e15次似乎可能需要一段时间。 您可以使用的是,如果
n = n0 + n1 * p + n2 * p ^ 2 ... + nd * p ^ d
m = m0 + m1 * p + m2 * p ^ 2 ... + md * p ^ d
(其中0 <= mi,ni&lt; p)
然后 C(n,m)= C(n0,m0)* C(n1,m1)* ... * C(nd,nd)mod p
请参阅,例如http://www.cecm.sfu.ca/organics/papers/granville/paper/binomial/html/binomial.html
一种方法是使用pascal的三角形来构建所有C(m,n)的表,其中0 <= m <= n <= 1009。
答案 3 :(得分:2)
用于计算nCk的psudo代码:
result = 1
for i=1 to min{K,N-K}:
result *= N-i+1
result /= i
return result
时间复杂度:O(min {K,N-K})
循环变为from i=1 to min{K,N-K}
而不是from i=1 to K
,这是正常的,因为
C(k,n) = C(k, n-k)
如果你使用GammaLn函数,你可以更有效地计算这个东西。
nCk = exp(GammaLn(n+1)-GammaLn(k+1)-GammaLn(n-k+1))
GammaLn函数是Gamma function的自然对数。我知道有一种有效的算法来计算GammaLn函数,但该算法根本不是微不足道的。
答案 4 :(得分:-1)
以下代码显示了如何获取给定大小“n”的所有二项式系数。您可以轻松地将其修改为在给定的k处停止以确定nCk。它在计算上非常高效,编码简单,适用于非常大的n和k。
binomial_coefficient = 1
output(binomial_coefficient)
col = 0
n = 5
do while col < n
binomial_coefficient = binomial_coefficient * (n + 1 - (col + 1)) / (col + 1)
output(binomial_coefficient)
col = col + 1
loop
二项式系数的输出因此是:
1
1 * (5 + 1 - (0 + 1)) / (0 + 1) = 5
5 * (5 + 1 - (1 + 1)) / (1 + 1) = 15
15 * (5 + 1 - (2 + 1)) / (2 + 1) = 15
15 * (5 + 1 - (3 + 1)) / (3 + 1) = 5
5 * (5 + 1 - (4 + 1)) / (4 + 1) = 1
我曾经在维基百科上找到过这个公式,但出于某种原因它不再存在:(