以下是问题描述:
让c[n]
成为n
的加泰罗尼亚数字,而p
是一个大素数,例如。1000000007
我需要计算c[n] % p
,其中n
的范围是{1,2,3,...,1000}
我遇到的问题是,在为这么大的整数计算加泰罗尼亚数时,在32位机器上会出现溢出。我熟悉模运算。还
(a.b) % p = ((a % p)(b % p)) % p
这个公式帮助我分别逃脱了分子中的溢出,但我不知道如何处理分母。
答案 0 :(得分:7)
对于1000000007的模数,避免仅使用32位整数溢出是很麻烦的。但是任何体面的C实现都提供64位整数(并且任何体面的C ++实现也都如此),所以这不是必需的。
然后,为了处理这些分母,正如KerrekSB在他的评论中所说的那样,一种可能性是计算以模p = 1000000007
为模的分母的模逆。您可以使用extended Euclidean algorithm计算模逆,或者等效地k/p
的连续分数展开。然后,不是在计算中除以k
,而是乘以其模数逆。
另一个选择是对加泰罗尼亚数字使用Segner的递归关系,这给出了一个没有划分的计算:
C(0) = 1
n
C(n+1) = ∑ C(i)*C(n-i)
0
由于您只需要C(k)
的加泰罗尼亚语数字k <= 1000
,您可以预先计算它们,或者在程序启动时快速计算它们并将它们存储在查找表中。
如果与预期相反,没有64位整数类型可用,您可以通过将因子分为低16位和高16位来计算模块化产品,
a = a1 + (a2 << 16) // 0 <= a1, a2 < (1 << 16)
b = b1 + (b2 << 16) // 0 <= b1, b2 < (1 << 16)
a*b = a1*b1 + (a1*b2 << 16) + (a2*b1 << 16) + (a2*b2 << 32)
要使用a*b (mod m)
计算m <= (1 << 31)
,请将m
模拟的四种产品中的每一种减少,
p1 = (a1*b1) % m;
p2 = (a1*b2) % m;
p3 = (a2*b1) % m;
p4 = (a2*b2) % m;
和合并轮班的最简单方法是
for(i = 0; i < 16; ++i) {
p2 *= 2;
if (p2 >= m) p2 -= m;
}
p3
的相同内容以及p4
的32次迭代。然后
s = p1+p2;
if (s >= m) s -= m;
s += p3;
if (s >= m) s -= m;
s += p4;
if (s >= m) s -= m;
return s;
这种方式不是很快,但是对于这里需要的少数乘法,它足够快。应通过减少班次数来获得小幅加速;首先计算(p4 << 16) % m
,
for(i = 0; i < 16; ++i) {
p4 *= 2;
if (p4 >= m) p4 -= m;
}
然后所有p2
,p3
和p4
的当前值都需要乘以2 16 modulo m
,
p4 += p3;
if (p4 >= m) p4 -= m;
p4 += p2;
if (p4 >= m) p4 -= m;
for(i = 0; i < 16; ++i) {
p4 *= 2;
if (p4 >= m) p4 -= m;
}
s = p4+p1;
if (s >= m) s -= m;
return s;
答案 1 :(得分:2)
如果使用动态编程存储结果并填充查找表,则可以在每一步使用MODULO除法。它会处理1000个加泰罗尼亚语的溢出,并且比BigDecimal / BigInteger更快。
public class Catalan {
private static long [] catalan= new long[1001];
private static final int MOD=1000000007;
public static void main(String[] args) {
precalc();
for (int i=1;i<=1000;i++){
System.out.println("Catalan number for "+i+" is: "+catalan[i]);
}
}
private static void precalc(){
for (int i=0;i<=1000;i++){
if (i==0 || i==1){
catalan[i]=1;
}
else{
long sum =0;long left, right;
for (int k=1;k<=i;k++){
left = catalan[k-1] % MOD;
right= catalan[i-k] % MOD;
sum =(sum+ (left * right)%MOD)%MOD;
}
catalan[i]=sum;
}
}
}
}
答案 2 :(得分:1)
如何使用大整数库?试着谷歌搜索...
答案 3 :(得分:0)
#include <stdio.h>
#include <stdlib.h>
/*
C(n) = (2n)!/(n+1)!n!
= (2n)(2n-1)(2n-2)..(n+2)/n!
*/
int p = 1000000007;
int gcd(int x, int y){
while(y!=0){
int wk = x % y;
x = y;
y = wk;
}
return x;
}
int catalanMod(n){
long long c = 1LL;
int i;
int *list,*wk;
//make array [(2n),(2n-1),(2n-2)..(n+2)]
wk = list = (int*)malloc(sizeof(int)*(n-1));
for(i=n+2;i<=2*n;++i){
*wk++ = i;
}
wk=list;
//[(2n),(2n-1),(2n-2)..(n+2)] / [1,2,3,..n]
//E.g C(10)=[13,17,19,4]
for(i=2;i<=n;++i){
int j,k,w;
for(w=i,j=0;j<n-1;++j){
while(1!=(k = gcd(wk[j], w))){
wk[j] /= k;
w /= k;
}
if(w == 1) break;
}
}
wk=list;
//Multiplication and modulo reduce
for(i=0;i<n-1;++i){
if(wk[i]==1)continue;
c = c * wk[i] % p;
}
free(list);
return c;
}
答案 4 :(得分:0)
简单地说,使用属性,(a * b)%mod =(a%mod)*(b%mod)