AD - 4.01.itertools Editorial /

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_K1の要素を持ちます。

特に、第 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 関数の戻り値であるジェネレータは、引数に渡したオブジェクトの長さを N2 番目の引数として渡した値を 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_replacementcombinations と同様に組み合わせの全列挙を行うための関数です。
次のプログラムは [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 関数の戻り値であるジェネレータは、引数に渡したオブジェクトの長さを N2 番目の引数として渡した値を 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問題はありません。本文中にある練習問題を解いてください。

前のページ | 次のページ


  1. {} _ 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 となります 

  2. {} _ 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 となります 

  3. {} _ 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 となります 

  4. ただし、各ループが O(タプルの長さ) であることを意味しないことに注意してください。ここでは詳細な説明は省略しますが、「ならし計算量で O(タプルの長さ)」が正確な表現となります。