プログラミング
記事内に商品プロモーションを含む場合があります

パスカルの三角形を使った組み合わせ数の計算方法|Python

Aru

競技プログラミングでは、「組み合わせ数(コンビネーション, nCr)をMで割った余りを計算」する問題がたまに出題されます。多くの場合Mは素数なことが多いですが、素数でない場合は厄介です。ここでは、パスカルの三角形を使ってnCrを計算する方法を紹介します。

nCrをMで割ったあまりの計算

Mが素数の場合

Mが素数の場合は、逆元(1/M)が簡単に計算できるので、

$$
_nC_r = \frac{n!}{r!(n-r)!}
$$

の計算をそのまますることができます。具体的には、$n!$, $r!$, $(n-r)!$を計算して、$1/r!$, $1/(n-r)!$をフェルマーの小定理を利用して計算し、掛け合わせればOKです。

あわせて読みたい
PythonでMOD付きnCk(組み合わせ数)を求める方法
PythonでMOD付きnCk(組み合わせ数)を求める方法

Mが素数でない場合

Mが素数でない場合はフェルマーの小定理を利用した逆元の計算ができないため、面倒になります。この場合は、他のアプローチが必要になります。

ここでは、パスカルの三角形を使った解法を解説します。

パスカルの三角形

パスカルの三角形って?

パスカルの三角形は、二項展開における二項係数を三角形に並べたものです。具体的には以下のようなものになります。

パスカルの三角形(6段)

これは、以下のように$_nC_r$に対応します。

また、パスカルの三角形は、上から左右の値を足した結果をくりかえした構造になっていて、頂点から順番に計算することが可能です。

配列に格納する

多少無駄になりますが、これをNxNの配列Cに格納することを考えます。左詰で考えると以下のようになります。

index012345
01
111
2121
31331
414641
515101051

このように配置するとC[n][r]が$_nC_r$の値を格納するので扱いやすいです。

どのくらいのサイズまでOKか?

仮にN=1,000とします。するとN^2 = 1,000,000となります。64bit(8byte)の整数だと、これは8Mbyteに相当します。計算回数は10^6です。AtCoderのABCの制約をみるとメモリが1024MBまでOKなのでまだまだ行けそうです。

N=10,000とすると、N^2=100,000,000となりメモリはギリギリ大丈夫そうですが、計算回数が10^8となり少し厳しくなります。

実際に使えるのはN<10,000以下だと考えた方が良いかと思います。

パスカルの三角形を使ってnCrを計算するPythonクラス

nCrクラス

class nCr :
    def __init__(self, n, mod) :
        self.c = [[0 for _ in range(n)] for _ in range(n)]
        self.c[0][0] = 1
        for i in range(n-1) :
            for j in range(i+1) :
                self.c[i+1][j] += self.c[i][j]
                self.c[i+1][j] %= mod
                self.c[i+1][j+1] += self.c[i][j]
                self.c[i+1][j+1] %= mod

    def nCr(self, n, r) :
        return self.c[n][r]

使い方

クラスは以下のように使います。
今回作成したクラスでは、[0, n)までのテーブルを作成するので、$_5C_i$を計算したい場合は、nCr(6, mod)とすることに注意してください。

n = 5
mod = 100
c = nCr(n+1, mod)
for i in range(n+1) :
    for j in range(i+1) :
        print(f"{i}C{j} = ", end="")
        print(c.nCr(i, j), end=" ")
    print()

結果は以下のようになります

実行結果
0C0 = 1 
1C0 = 1 1C1 = 1 
2C0 = 1 2C1 = 2 2C2 = 1 
3C0 = 1 3C1 = 3 3C2 = 3 3C3 = 1 
4C0 = 1 4C1 = 4 4C2 = 6 4C3 = 4 4C4 = 1 
5C0 = 1 5C1 = 5 5C2 = 10 5C3 = 10 5C4 = 5 5C5 = 1 

ちなみにmod=10とすると以下のように10以上の部分が正しく変化します。

実行結果
0C0 = 1 
1C0 = 1 1C1 = 1 
2C0 = 1 2C1 = 2 2C2 = 1 
3C0 = 1 3C1 = 3 3C2 = 3 3C3 = 1 
4C0 = 1 4C1 = 4 4C2 = 6 4C3 = 4 4C4 = 1 
5C0 = 1 5C1 = 5 5C2 = 0 5C3 = 0 5C4 = 5 5C5 = 1 

まとめ

パスカルの三角形をつかって、組み合わせの数を計算する方法について解説しました。ちなみに、$_nC_r = \frac{n!}{r!(n-r)!}$の計算を愚直に行うとオーバーフローするような場合にも使えます。

パスカルの三角形が使えるのはnCrnが小さい場合です(nが数千程度まで)。問題の制約を見て使えるかどうかの判断をつける必要があります。

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
ある/Aru
ある/Aru
IT&機械学習エンジニア/ファイナンシャルプランナー(CFP®)
専門分野は並列処理・画像処理・機械学習・ディープラーニング。プログラミング言語はC, C++, Go, Pythonを中心として色々利用。現在は、Kaggle, 競プロなどをしながら悠々自適に活動中
記事URLをコピーしました