/
Time Limit: 0 msec / Memory Limit: 0 KiB
キーポイント
permutations関数で並べ替えの全列挙を行うことができるcombinations関数で組み合わせの全列挙を行うことができるproduct関数でネストしたループをまとめることができる
itertools とは
itertools は python の標準モジュールの 1 つです。その名前の通り、イテレーション(ループ処理)を行う際に便利な関数を持っています。
この節では itertools モジュール内の関数のうち、競技プログラミングにおいて比較的使う機会がある permutations, combinations, combinations_with_replacement, product の 4 つの関数について紹介します。
permutations
permutations は、並べ替えの全列挙を行うための関数です。
次のプログラムは [1,2,3] というリストに対してその並べ替え全てを出力しています。
コード
import itertools
for p in itertools.permutations([1, 2, 3]):
print(p)
出力
(1, 2, 3) (1, 3, 2) (2, 1, 3) (2, 3, 1) (3, 1, 2) (3, 2, 1)
permutations 関数には第 2 引数として整数を渡すことができます。その場合、第 1 引数のリストの中からその個数だけ取り出して並べたものを得ることができます。
コード
import itertools
for p in itertools.permutations([1, 2, 3, 4], 2):
print(p)
出力
(1, 2) (1, 3) (1, 4) (2, 1) (2, 3) (2, 4) (3, 1) (3, 2) (3, 4) (4, 1) (4, 2) (4, 3)
permutations 関数は、「タプルを返すジェネレータ」を返します。permutations 関数の戻り値であるジェネレータは、引数に渡したオブジェクトの長さを N 、第 2 引数を K として、必ず {}_NP_K 個1の要素を持ちます。
特に、第 1 引数に渡したリストが重複する値を含む場合、同じ並べ替えが複数回現れます。
コード
import itertools
for p in itertools.permutations("bab"):
print(p)
出力
('b', 'a', 'b')
('b', 'b', 'a')
('a', 'b', 'b')
('a', 'b', 'b')
('b', 'b', 'a')
('b', 'a', 'b')
引数には list, tuple, str, range などのイテラブルオブジェクトを渡すことができます。
練習問題
以下は permutations 関数を使って解ける問題です。
combinations
combinations は組み合わせの全列挙を行うための関数です。
次のプログラムは [1,2,3,4] というリストから 2 個の要素を取り出す方法を全て出力しています。
コード
import itertools
for p in itertools.combinations([1, 2, 3, 4], 2):
print(p)
出力
(1, 2) (1, 3) (1, 4) (2, 3) (2, 4) (3, 4)
combinations 関数は、「タプルを返すジェネレータ」を返します。combinations 関数の戻り値であるジェネレータは、引数に渡したオブジェクトの長さを N 、2 番目の引数として渡した値を K として、必ず {} _ NC _ {K} 2個の要素を持ちます。
特に、第 1 引数に渡したリストが重複する値を含む場合、同じ組み合わせが複数回現れます。
コード
import itertools
for p in itertools.combinations("bbaa", 2):
print(p)
出力
('b', 'b')
('b', 'a')
('b', 'a')
('b', 'a')
('b', 'a')
('a', 'a')
引数には list, tuple, str, range などのイテラブルオブジェクトを渡すことができます。
combinations_with_replacement
combinations_with_replacement は combinations と同様に組み合わせの全列挙を行うための関数です。
次のプログラムは [1,2,3,4] というリストから重複を許して 2 個の要素を取り出す方法を全て出力しています。
コード
import itertools
for p in itertools.combinations_with_replacement([1, 2, 3], 2):
print(p)
出力
(1, 1) (1, 2) (1, 3) (2, 2) (2, 3) (3, 3)
combinations_with_replacement 関数は、「タプルを返すジェネレータ」を返します。combinations_with_replacement 関数の戻り値であるジェネレータは、引数に渡したオブジェクトの長さを N 、2 番目の引数として渡した値を K として、必ず {} _ NH _ {K} 個3の要素を持ちます。
特に、第 1 引数に渡したリストが重複する値を含む場合、同じ組み合わせが複数回現れます。
コード
import itertools
for p in itertools.combinations_with_replacement("bab", 2):
print(p)
出力
('b', 'b')
('b', 'a')
('b', 'b')
('a', 'a')
('a', 'b')
('b', 'b')
引数には list, tuple, str, range などのイテラブルオブジェクトを渡すことができます。
計算量について
permutations, combinations, combinations_with_replacement の 3 つの関数について、その戻り値を for 文で全てループするときの時間量は O(タプルの長さ \times ループ数) となります。4
したがって、通常の for 文と同様の計算量だと考えて問題ありません。参考としていくつかの例を挙げておきます。
| 長さ | |
|---|---|
permutations(range(10)) |
3628800 |
combinations(range(26), 13) |
10400600 |
combinations_with_replacement(range(20), 10) |
20030010 |
combinations(range(500), 3) |
20708500 |
combinations_with_replacement(range(500), 3) |
20958500 |
product
product 関数は、入れ子になったループを1つにまとめるのに役立ちます。
例えば次のような例を考えます。
コード
for x in range(2):
for y in range(3):
print(x, y)
出力
0 0 0 1 0 2 1 0 1 1 1 2
この例では for 文が二重になっています。これを product 関数により次のように書き換えることができます。
import itertools
for x, y in itertools.product(range(2), range(3)):
print(x, y)
これにより複雑なDPを行う際などにループのネストが深くなることを避けることができます。
product 関数には 3 つ以上の引数を与えることができるため、三重ループ以上も同様に1つのループで書くことができます。
product 関数にはキーワード引数 repeat があり、同じ要素のみを繰り返し引数として渡す以下のケースで記述をまとめることができます。
# 以下の2つは同じになる itertools.product(range(5), range(5), range(5)) itertools.product(range(5), repeat=3)
これにより、bit DP を itertools.product(range(2),repeat=20) を用いて実装することもできます。
公式ドキュメント
itertools モジュールに含まれる関数のうち、競技プログラミングで使う機会が多いものは以上となりますが、itertools モジュールはここで紹介したもの以外にも様々な関数を含みます。他の関数について知りたい方は公式ドキュメントを参照してください。
問題
本節にはEX問題はありません。本文中にある練習問題を解いてください。
-
{} _ NP _ K は「N 個の要素から K 個を取り出して並べる方法の個数」を表します。その値は N から降順に K 個の整数の積 N\times (N-1)\times (N-2)\times \dots\times(N-(K-1)) となります。例えば {} _ 8P _ 3=8\times 7\times 6=336 となります ↩
-
{} _ NC _ K は「N 個の要素から K 個を取り出す方法の個数」を表します。その値は N から降順に K 個の整数の積 N\times (N-1)\times (N-2)\times \dots\times(N-(K-1)) を 1 から K までの積で割った値となります。例えば {} _ 8C _ 3=\frac{8\times 7\times 6}{1\times 2\times 3}=56 となります ↩
-
{} _ NH _ K は「N 個の要素から重複を許して K 個を取り出す方法の個数」を表します。その値は N から昇順に K 個の整数の積 N\times (N+1)\times (N+2)\times \dots\times(N+(K-1)) を 1 から K までの積で割った値となります。例えば {} _ 8H _ 3=\frac{8\times 9\times 10}{1\times 2\times 3}=120 となります ↩
-
ただし、各ループが O(タプルの長さ) であることを意味しないことに注意してください。ここでは詳細な説明は省略しますが、「ならし計算量で O(タプルの長さ)」が正確な表現となります。 ↩