モジュラスが 1000000007 の場合、32 ビット整数だけでオーバーフローを回避するのは面倒です。しかし、適切な C 実装は 64 ビット整数を提供するため (適切な C++ 実装も提供します)、その必要はありません。
次に、分母に対処するための 1 つの可能性は、KerrekSB がコメントで述べたように、素数を法とする分母のモジュラー逆数を計算することp = 1000000007
です。拡張ユークリッド アルゴリズム、または同等に、 の連分数展開を使用して剰余逆数を計算できますk/p
。次に、計算で割る代わりにk
、モジュラー逆数を掛けます。
もう 1 つのオプションは、カタロニア語の数値に 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)
、 を法として 4 つの積のそれぞれを減らします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
の と の32 回の反復で同じですp4
。それで
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
および の現在の値に2 16モジュロをp4
掛ける必要があります。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;