コメントで、ここのコードは不透明であると述べています。しかし、それはおそらくcombinations
あなたが目指している種類の関数を実装するための最良の方法であり、理解する価値があるので、それを詳細に説明しようと思います。
基本的な考え方は、シーケンスと選択するアイテムの数が与えられた場合、各組み合わせを、与えられたシーケンスへのインデックスのシーケンスとして表すことができるということです。たとえば、リスト['a', 'b', 'c', 'd', 'e']
があり、そのリストから2つの値のすべての組み合わせを生成するとします。
私たちの最初の組み合わせは次のようになります...
['a', 'b', 'c', 'd', 'e']
^ ^
...そしてインデックスのリストで表されます[0, 1]
。次の組み合わせは次のようになります。
['a', 'b', 'c', 'd', 'e']
^ ^
そして、インデックスのリストで表されます[0, 2]
。
2番目のキャレットが最後に到達するまで、最初のキャレットを所定の位置に保ちながら、2番目のキャレットを前方に動かし続けます。次に、最初のキャレットをインデックス1
に移動し、2番目のキャレットをインデックスに戻すことでプロセスを「リセット」します2
。
['a', 'b', 'c', 'd', 'e']
^ ^
次に、このプロセスを繰り返し、2番目のキャレットを最後まで前方に移動してから、最初のキャレットを1つ前方に移動し、2番目をリセットします。
次に、インデックスのリストを操作して、これを行う方法を理解する必要があります。これは非常に簡単であることがわかります。最終的な組み合わせは次のようになります。
['a', 'b', 'c', 'd', 'e']
^ ^
そして、これのインデックス表現はになります[3, 4]
。これらはインデックスの可能な最大値であり、に等しくなりますi + n - r
。ここi
で、はリスト内の位置、n
は値の数(5
この場合)、r
は選択肢の数(2
この場合)です。したがって、特定のインデックスがこの値に達するとすぐに、それ以上になることはなく、「リセット」する必要があります。
それを念頭に置いて、コードの段階的な分析を次に示します。
def combinations(iterable, r):
pool = tuple(iterable)
n = len(pool)
まず、上記の例に基づいて入力すると、上記pool
の文字のリストがタプルに変換されn
、プール内のアイテムの数になります。
if r > n:
return
n
アイテムリストからアイテムを置き換えることなく選択することはできないn
ので、その場合は単に戻ります。
indices = range(r)
これで、最初の組み合わせ()に初期化されたインデックスができました[0, 1]
。だから私たちはそれを生み出します:
yield tuple(pool[i] for i in indices)
次に、無限ループを使用して残りの組み合わせを生成します。
while True:
ループ内では、最初にインデックスのリストをさかのぼって、まだ最大値に達していないインデックスを検索します。上記の式(i + n - r
)を使用して、特定のインデックスの最大値を決定します。最大値に達していないインデックスが見つかった場合は、ループから抜け出します。
for i in reversed(range(r)):
if indices[i] != i + n - r:
break
見つからない場合は、すべてのインデックスが最大値になっていることを意味するため、反復処理が完了します。(これはあまり知られていないfor-else
構造を使用します。else
ブロックは、for
ループが正常に終了した場合にのみ実行されます。)
else:
return
これで、インデックスi
をインクリメントする必要があることがわかりました。
indices[i] += 1
さらに、後のインデックスi
はすべて最大値になっているため、リセットする必要があります。
for j in range(i+1, r):
indices[j] = indices[j-1] + 1
これで次のインデックスのセットがあるので、別の組み合わせを生成します。
yield tuple(pool[i] for i in indices)
このアプローチにはいくつかのバリエーションがあります。別の方法では、インデックスを逆方向に進む代わりに、前に進み、そのインデックスと次のインデックスの間に「ギャップ」がある最初のインデックスをインクリメントし、下位のインデックスをリセットします。
最後に、これを再帰的に定義することもできますが、実用的には、再帰的な定義はおそらくそれほど効率的ではありません。