ループ回数の求め方:全探索・組合せ・重複組合せの3パターンを解説

アルゴリズムでは、ループが「何回まわるか」を把握することがしばしば重要になります。本記事では、ループ構造の典型的な3つのパターンについて、それぞれのループ回数の計算の仕方を図を使ってわかりやすく解説します。
ループの回数を求める
以下、3つのパターンについてループ回数を求めてみます。
i0 = i1 = i2 = … の時
1つ目は、全てのループが0~N-1
回の場合の例です。それぞれのループはN回ずつループするので、ループ回数はN*N*N...
になります。
ループの深さを$k$と置くと、合計ループ回数は以下のように表すことができます。
$$
\prod_{i=1}^k N
$$
以下はk=3の例です。
N = 10
cnt = 0
for i0 in range(N):
for i1 in range(N):
for i2 in range(N):
cnt+=1
print(cnt)
$N^3 = 1000$で、プログラムの出力は1000
と一致します。
i0 < i1 < i2 < …. の時
i0が0
の時、i1が1~
となるような場合になります。
k=3のときを例にすると以下のようなプログラムになります。
N = 10
cnt = 0
for i0 in range(N):
for i1 in range(i0+1, N):
for i2 in range(i1+1, N):
cnt+=1
print(cnt)
この場合の考え方ですが、0<=i0<i1<i2<N
となるi0, i1, i2
の組合せを考えるのと同じになるので、「n個からk個を選択する」場合の数と一致します。つまり、$_nC_k$となります。
$$
_nC_k
$$
図で見ると以下のようなイメージになります。図では、i0=0, i1=2, i2=6
の例です。条件i0<i1<i2
からi0,i1,i2
は同じ値になりません。また、i0
はi1, i2
より小さい値となるので選んだk個に小さい値からi0, i1, ...
と割り当てたのと一致します。

実際に、上記のプログラムを実行した結果は120で、$_{10}C_3 = 120$と一致します
i0 ≦ i1 ≦ i2 ≦ …の時
先ほどの場合と異なるのはi0=i1
などの「同値」があることです。
同じ値を許すこのループのループ回数は、重複組合せの数と一致します。
$$
_nH_k = _{n+k-1}C_k
$$
N = 10
cnt = 0
for i0 in range(N):
for i1 in range(i0, N):
for i2 in range(i1, N):
cnt+=1
print(cnt)
これは、図で考えた方がわかりやすいです。下図はi0=1,i1=2,i2=5
の例です。重複組み合わせは、区切りで示したが方がわかりやすいので赤線で示しています。

実際にプログラムを実行すると220
で、$_{10+3-1}C_3=220$と計算結果と一致します。
重複組合せについては以下の記事も参考にしてください。

ループ回数を求める問題はAtCoderで過去に出題されたことがあります。
ABC201 D問題
https://atcoder.jp/contests/abc021/tasks/abc021_d
この問題の解答は以下になります(mod演算があるので少し複雑ですがnHkを計算しているだけです。
n = int(input())
k = int(input())
N = n + k -1
mod = 10**9 + 7
# nCk = n! / (k! * (n-k)!)
# 分子を計算
n0 = 1
for i in range(1, N+1):
n0 *= i
n0 %= mod
# 分母を計算
n1 = 1
for i in range(1, k+1):
n1 *= i
n1 %= mod
for i in range(1, N-k+1):
n1 *= i
n1 %= mod
# 1/n1を計算(逆元)
in1 = pow(n1, mod-2, mod)
print(n0 * in1 % mod)
まとめ
ループ回数について解説しました。計算量の見積もりでは$O(N^k)$となりますが、実際のループ回数が知りたい場合は覚えて置くと便利です。