我々はthis answerから基本的な再帰アルゴリズムを使用して生成し、不要なパーティションを除外することなく、特定の長さのパーティションを生成するためにそれを変更することができます。
def sorted_k_partitions(seq, k):
"""Returns a list of all unique k-partitions of `seq`.
Each partition is a list of parts, and each part is a tuple.
The parts in each individual partition will be sorted in shortlex
order (i.e., by length first, then lexicographically).
The overall list of partitions will then be sorted by the length
of their first part, the length of their second part, ...,
the length of their last part, and then lexicographically.
"""
n = len(seq)
groups = [] # a list of lists, currently empty
def generate_partitions(i):
if i >= n:
yield list(map(tuple, groups))
else:
if n - i > k - len(groups):
for group in groups:
group.append(seq[i])
yield from generate_partitions(i + 1)
group.pop()
if len(groups) < k:
groups.append([seq[i]])
yield from generate_partitions(i + 1)
groups.pop()
result = generate_partitions(0)
# Sort the parts in each partition in shortlex order
result = [sorted(ps, key = lambda p: (len(p), p)) for ps in result]
# Sort partitions by the length of each part, then lexicographically.
result = sorted(result, key = lambda ps: (*map(len, ps), ps))
return result
ここではかなり多くのことが説明されています。
まず、我々は同じaforementioned recursive algorithmの手続き、ボトムアップ(?teminology)実装を開始します。
def partitions(seq):
"""-> a list of all unique partitions of `seq` in no particular order.
Each partition is a list of parts, and each part is a tuple.
"""
n = len(seq)
groups = [] # a list of lists, currently empty
def generate_partitions(i):
if i >= n:
yield list(map(tuple, groups))
else:
for group in groups
group.append(seq[i])
yield from generate_partitions(i + 1)
group.pop()
groups.append([seq[i]])
yield from generate_partitions(i + 1)
groups.pop()
if n > 0:
return list(generate_partitions(0))
else:
return [[()]]
メインアルゴリズムは、ネストされたgenerate_partitions
機能です。基本的に、それはシーケンスを通って歩き、各アイテムについて、それは:1)そのアイテムをワーキングセット内の現在のグループ(a.k.aパーツ)のそれぞれに入れて再帰する。 2)アイテムをそれ自身の新しいグループに入れます。
シーケンス(i == n
)の最後に到達すると、私たちが構築している作業セットの(深い)コピーが得られます。
ここで、特定の長さのパーティションを取得するには、はとなります。これは、探しているものに対して結果をフィルタリングまたはグループ化するだけですが、このアプローチでは多くの不必要な作業私たちがちょうどある長さの区画を望むならば、それはk
です。上記の関数で、パーティションの長さ(基すなわち#)がたびに増加する
注
# this adds a new group (or part) to the partition
groups.append([seq[i]])
yield from generate_partitions(i + 1)
groups.pop()
は...実行されます。したがって、我々は単純にそうように、そのブロックの上にガードを置くことによって、パーティションのサイズを制限:新しいパラメータとpartitions
機能にちょうどその行を追加する
def partitions(seq, k):
...
def generate_partitions(i):
...
# only add a new group if the total number would not exceed k
if len(groups) < k:
groups.append([seq[i]])
yield from generate_partitions(i + 1)
groups.pop()
今だけの長さのパーティションを生成することが発生しますまでk
。これはほとんど私たちが望むものです。問題は、for
ループが長さがで、k
より小さいパーティションを生成することがあることです。
これらの再帰的ブランチを削除するには、作業セットを合計でk
個のグループに拡張するのに十分な残りの要素があることが確認できるときには、for
ループを実行する必要があります。残りの要素またはまだグループに配置されていない要素の数はn - i
(またはlen(seq) - i
)です。 k - len(groups)
は、有効なkパーティションを作成するために追加する必要がある新しいグループの数です。 n - i <= k - len(groups)
の場合は、はには現在のグループの1つを追加してアイテムを捨てることはできません。はに新しいグループを作成する必要があります。
だから我々は単に別のガードを追加し、他の再帰的なブランチにこの時間:
def generate_partitions(i):
...
# only add to current groups if the number of remaining items
# exceeds the number of required new groups.
if n - i > k - len(groups):
for group in groups:
group.append(seq[i])
yield from generate_partitions(i + 1)
group.pop()
# only add a new group if the total number would not exceed k
if len(groups) < k:
groups.append([seq[i]])
yield from generate_partitions(i + 1)
groups.pop()
そして、それとは、あなたが取り組んでK-パーティション発電機を持っています。再帰呼び出しの一部をさらに崩壊させる可能性があります(たとえば、残りのアイテムが3つあり、さらに3つのグループが必要な場合は、各アイテムをそれぞれのグループに分割する必要があることをすでに知っています)。すべてのパーティションを生成する基本的なアルゴリズムのわずかな変更として機能します。
残っているのは、結果を並べ替えることだけです。残念ながら、望ましい順序でパーティションを直接生成する方法(よりスマートな犬のためのエクササイズ)を考え出すのではなく、私は騙されてポストジェネレーションをソートしました。
def sorted_k_partitions(seq, k):
...
result = generate_partitions(0)
# Sort the parts in each partition in shortlex order
result = [sorted(ps, key = lambda p: (len(p), p)) for ps in result]
# Sort partitions by the length of each part, then lexicographically.
result = sorted(result, key = lambda ps: (*map(len, ps), ps))
return result
キー機能を除いて、やや自明です。最初の1:
key = lambda p: (len(p), p)
は、(Pythonでは、デフォルトで辞書順順序付けられている、)配列自体によって、長さのシーケンスをソートすると言います。 p
は「部品」の略です。これは、パーティション内のパーツ/グループをソートするために使用されます。このキーは、たとえば(4,)
が(1, 2, 3)
に先行するので、[(1, 2, 3), (4,)]
が[(4,), (1, 2, 3)]
とソートされることを意味します。
key = lambda ps: (*map(len, ps), ps)
# or for Python versions <3.5: lambda ps: tuple(map(len, ps)) + (ps,)
ここps
は、複数の "部" の略です。これは、各要素の長さ(シーケンス自体でなければならない)でシーケンスをソートし、次にシーケンス自体によって(辞書的に)並べ替えると言います。これは、パーティションをお互いに並べ替えるために使用されます。たとえば、[(4,), (1, 2, 3)]
が[(1, 2), (3, 4)]
に先行するようにします。
次
seq = [1, 2, 3, 4]
for k in 1, 2, 3, 4:
for groups in sorted_k_partitions(seq, k):
print(k, groups)
は生成します。
1 [(1, 2, 3, 4)]
2 [(1,), (2, 3, 4)]
2 [(2,), (1, 3, 4)]
2 [(3,), (1, 2, 4)]
2 [(4,), (1, 2, 3)]
2 [(1, 2), (3, 4)]
2 [(1, 3), (2, 4)]
2 [(1, 4), (2, 3)]
3 [(1,), (2,), (3, 4)]
3 [(1,), (3,), (2, 4)]
3 [(1,), (4,), (2, 3)]
3 [(2,), (3,), (1, 4)]
3 [(2,), (4,), (1, 3)]
3 [(3,), (4,), (1, 2)]
4 [(1,), (2,), (3,), (4,)]
をですから、リスト番号の順列をしたいが、数字は除外することができますか? – heather
あなたの希望する結果を定義することによって、あなたが良いスタートを持つように見えます。今度は、プロセス/アルゴリズムを単語/擬似コードで書き留めてロジックをハッシュアウトし、コードに変換しようとします。物事がうまくいかない場合は、戦略的なポイントでprintステートメントを使用して、変数で何が起きているのかを確認してください。 – wwii
基本的には、[パーティション](https://en.wikipedia.org/wiki/Partition_of_a_set) _n_の各値に対して[短くする順序](https://en.wikipedia.org/wiki/Shortlex_order)で重複してソートされています。基本的なパーティションアルゴリズムについては、[here](http://stackoverflow.com/a/30134039)を参照してください。私は、結果をフィルタリングおよび/またはソートすることが、まず最初に正しい順序で生成するよりも簡単になるだろうと考えています。 –