itertools.permutationsは、その要素が値ではなく位置に基づいて一意として扱われる場所を生成します。だから基本的に私はこのような重複を避けたい:
>>> list(itertools.permutations([1, 1, 1]))
[(1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1)]
私の場合、順列の量が大きすぎるため、その後のフィルタリングは不可能です。
誰かがこれに適したアルゴリズムを知っていますか?
どうもありがとうございました!
編集:
私が基本的に欲しいのは次のとおりです:
x = itertools.product((0, 1, 'x'), repeat=X)
x = sorted(x, key=functools.partial(count_elements, elem='x'))
sorted
はリストを作成し、itertools.productの出力が大きすぎるため、これは不可能です。
申し訳ありませんが、実際の問題を説明する必要がありました。
_class unique_element:
def __init__(self,value,occurrences):
self.value = value
self.occurrences = occurrences
def perm_unique(elements):
eset=set(elements)
listunique = [unique_element(i,elements.count(i)) for i in eset]
u=len(elements)
return perm_unique_helper(listunique,[0]*u,u-1)
def perm_unique_helper(listunique,result_list,d):
if d < 0:
yield Tuple(result_list)
else:
for i in listunique:
if i.occurrences > 0:
result_list[d]=i.value
i.occurrences-=1
for g in perm_unique_helper(listunique,result_list,d-1):
yield g
i.occurrences+=1
a = list(perm_unique([1,1,2]))
print(a)
_
結果:
_[(2, 1, 1), (1, 2, 1), (1, 1, 2)]
_
編集(これの仕組み):
上記のプログラムを書き直して、より長くても読みやすくしました。
私は通常、何かがどのように機能するかを説明するのに苦労しますが、試してみましょう。これがどのように機能するかを理解するには、繰り返しのあるすべての順列を生成する、類似しているがより単純なプログラムを理解する必要があります。
_def permutations_with_replacement(elements,n):
return permutations_helper(elements,[0]*n,n-1)#this is generator
def permutations_helper(elements,result_list,d):
if d<0:
yield Tuple(result_list)
else:
for i in elements:
result_list[d]=i
all_permutations = permutations_helper(elements,result_list,d-1)#this is generator
for g in all_permutations:
yield g
_
このプログラムは明らかにはるかに単純です。dはpermutations_helperの深さを表し、2つの機能があります。 1つの関数は再帰アルゴリズムの停止条件で、もう1つの関数は渡される結果リスト用です。
各結果を返す代わりに、結果を返します。関数/演算子yield
がなかった場合、停止条件のポイントで結果を何らかのキューにプッシュする必要があります。ただし、この方法では、停止条件が満たされると、結果はすべてのスタックを介して呼び出し元に伝播されます。それが目的ですfor g in perm_unique_helper(listunique,result_list,d-1): yield g
ので、各結果は呼び出し元に伝播されます。
元のプログラムに戻ります:一意の要素のリストがあります。各要素を使用する前に、result_listにプッシュできる要素の数を確認する必要があります。このプログラムでの作業は、_permutations_with_replacement
_と非常に似ています。違いは、perm_unique_helperにあるよりも各要素を繰り返すことができないことです。
新しい質問が重複としてマークされ、著者がこの質問を参照する場合があるため、sympyにはこの目的のための反復子があることに注意することが重要です。
>>> from sympy.utilities.iterables import multiset_permutations
>>> list(multiset_permutations([1,1,1]))
[[1, 1, 1]]
>>> list(multiset_permutations([1,1,2]))
[[1, 1, 2], [1, 2, 1], [2, 1, 1]]
これは、ソート済みのイテラブルの順列は、前の順列の複製でない限りソート順であるという実装の詳細に依存しています。
from itertools import permutations
def unique_permutations(iterable, r=None):
previous = Tuple()
for p in permutations(sorted(iterable), r):
if p > previous:
previous = p
yield p
for p in unique_permutations('cabcab', 2):
print p
与える
('a', 'a')
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'b')
('b', 'c')
('c', 'a')
('c', 'b')
('c', 'c')
Luka Rahneの答えとほぼ同じ速さですが、より短くシンプルな私見です。
def unique_permutations(elements):
if len(elements) == 1:
yield (elements[0],)
else:
unique_elements = set(elements)
for first_element in unique_elements:
remaining_elements = list(elements)
remaining_elements.remove(first_element)
for sub_permutation in unique_permutations(remaining_elements):
yield (first_element,) + sub_permutation
>>> list(unique_permutations((1,2,3,1)))
[(1, 1, 2, 3), (1, 1, 3, 2), (1, 2, 1, 3), ... , (3, 1, 2, 1), (3, 2, 1, 1)]
最初の要素を設定(すべての一意の要素を反復)し、残りのすべての要素の順列を反復処理することにより、再帰的に機能します。
(1,2,3,1)のunique_permutations
を見て、どのように機能するかを見てみましょう。
unique_elements
は1,2,3ですfirst_element
は1で始まります。remaining_elements
は[2,3,1]です(つまり、1,2,3,1から最初の1を引いたもの)sub_permutation
に対して、first_element
を挿入します:(1、1,2,3)、(-1、1,3,2) 、...および結果を生成します。first_element
= 2を繰り返し、上記と同じことを行います。remaining_elements
は[1,3,1]です(つまり、1,2,3,1から最初の2を引いたもの)sub_permutation
に対して、first_element
を挿入します:(2、1、1、3)、(-2、1、3、1) 、(2、3、1、1)...と結果が得られます。first_element
= 3で同じことを行います。あなたはセットを使って試すことができます:
>>> list(itertools.permutations(set([1,1,2,2])))
[(1, 2), (2, 1)]
削除された重複を設定する呼び出し
これは10行の私のソリューションです:
class Solution(object):
def permute_unique(self, nums):
perms = [[]]
for n in nums:
new_perm = []
for perm in perms:
for i in range(len(perm) + 1):
new_perm.append(perm[:i] + [n] + perm[i:])
# handle duplication
if i < len(perm) and perm[i] == n: break
perms = new_perm
return perms
if __== '__main__':
s = Solution()
print s.permute_unique([1, 1, 1])
print s.permute_unique([1, 2, 1])
print s.permute_unique([1, 2, 3])
---結果----
[[1, 1, 1]]
[[1, 2, 1], [2, 1, 1], [1, 1, 2]]
[[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]
単純なアプローチは、順列のセットを取ることです。
list(set(it.permutations([1, 1, 1])))
# [(1, 1, 1)]
ただし、この手法は、複製の順列を無駄に計算して破棄します。より効率的なアプローチは more_itertools.distinct_permutations
、 サードパーティツール 。
コード
import itertools as it
import more_itertools as mit
list(mit.distinct_permutations([1, 1, 1]))
# [(1, 1, 1)]
パフォーマンス
より大きな反復可能オブジェクトを使用して、単純な手法とサードパーティの手法のパフォーマンスを比較します。
iterable = [1, 1, 1, 1, 1, 1]
len(list(it.permutations(iterable)))
# 720
%timeit -n 10000 list(set(it.permutations(iterable)))
# 10000 loops, best of 3: 111 µs per loop
%timeit -n 10000 list(mit.distinct_permutations(iterable))
# 10000 loops, best of 3: 16.7 µs per loop
私たちは見る more_itertools.distinct_permutations
は、桁違いに高速です。
詳細
ソースから、再帰アルゴリズム(受け入れられた答えに見られるように)を使用して個別の順列を計算し、それによって無駄な計算を回避します。詳細については、 ソースコード を参照してください。
Itertools.combinations()を探しているように聞こえます docs.python.org
list(itertools.combinations([1, 1, 1],3))
[(1, 1, 1)]
自分で何かを探しているときにこの質問にぶつかった!
私がやったことは次のとおりです。
def dont_repeat(x=[0,1,1,2]): # Pass a list
from itertools import permutations as per
uniq_set = set()
for byt_grp in per(x, 4):
if byt_grp not in uniq_set:
yield byt_grp
uniq_set.update([byt_grp])
print uniq_set
for i in dont_repeat(): print i
(0, 1, 1, 2)
(0, 1, 2, 1)
(0, 2, 1, 1)
(1, 0, 1, 2)
(1, 0, 2, 1)
(1, 1, 0, 2)
(1, 1, 2, 0)
(1, 2, 0, 1)
(1, 2, 1, 0)
(2, 0, 1, 1)
(2, 1, 0, 1)
(2, 1, 1, 0)
set([(0, 1, 1, 2), (1, 0, 1, 2), (2, 1, 0, 1), (1, 2, 0, 1), (0, 1, 2, 1), (0, 2, 1, 1), (1, 1, 2, 0), (1, 2, 1, 0), (2, 1, 1, 0), (1, 0, 2, 1), (2, 0, 1, 1), (1, 1, 0, 2)])
基本的に、セットを作成して追加し続けます。あまりにも多くのメモリを消費するリストなどを作成するよりも良い方法です。次の人が外を見るのに役立つことを願っています:-)関数の「更新」セットをコメントアウトして、違いを確認してください。
これが問題の再帰的な解決策です。
def permutation(num_array):
res=[]
if len(num_array) <= 1:
return [num_array]
for num in set(num_array):
temp_array = num_array.copy()
temp_array.remove(num)
res += [[num] + perm for perm in permutation(temp_array)]
return res
arr=[1,2,2]
print(permutation(arr))
_collections.Counter
_を使用して特定のシーケンスから一意のアイテムとそのカウントを取得し、_itertools.combinations
_を使用して各再帰呼び出しの各一意のアイテムのインデックスの組み合わせを選択し、インデックスをマップし直す関数を作成できますすべてのインデックスが選択されたときのリスト:
_from collections import Counter
from itertools import combinations
def unique_permutations(seq):
def index_permutations(counts, index_pool):
if not counts:
yield {}
return
(item, count), *rest = counts.items()
rest = dict(rest)
for indices in combinations(index_pool, count):
mapping = dict.fromkeys(indices, item)
for others in index_permutations(rest, index_pool.difference(indices)):
yield {**mapping, **others}
indices = set(range(len(seq)))
for mapping in index_permutations(Counter(seq), indices):
yield [mapping[i] for i in indices]
_
[''.join(i) for i in unique_permutations('moon')]
が返すように:
_['moon', 'mono', 'mnoo', 'omon', 'omno', 'nmoo', 'oomn', 'onmo', 'nomo', 'oonm', 'onom', 'noom']
_
どう?
np.unique(itertools.permutations([1, 1, 1]))
問題は、順列がNumpy配列の行であるため、より多くのメモリを使用することですが、以前と同様に循環させることができます
perms = np.unique(itertools.permutations([1, 1, 1]))
for p in perms:
print p
私自身の問題に取り組んでいる間、先日これに出くわしました。 Luka Rahneのアプローチは気に入っていますが、コレクションライブラリでCounterクラスを使用すると、ささやかな改善のように思えました。私のコードは次のとおりです。
def unique_permutations(elements):
"Returns a list of lists; each sublist is a unique permutations of elements."
ctr = collections.Counter(elements)
# Base case with one element: just return the element
if len(ctr.keys())==1 and ctr[ctr.keys()[0]] == 1:
return [[ctr.keys()[0]]]
perms = []
# For each counter key, find the unique permutations of the set with
# one member of that key removed, and append the key to the front of
# each of those permutations.
for k in ctr.keys():
ctr_k = ctr.copy()
ctr_k[k] -= 1
if ctr_k[k]==0:
ctr_k.pop(k)
perms_k = [[k] + p for p in unique_permutations(ctr_k)]
perms.extend(perms_k)
return perms
このコードは、各順列をリストとして返します。文字列を入力すると、それぞれが文字のリストである順列のリストが表示されます。代わりに文字列のリストとして出力したい場合(たとえば、あなたがひどい人で、Scrabbleでごまかすために私のコードを悪用したい場合)、次のようにします。
[''.join(perm) for perm in unique_permutations('abunchofletters')]
私が見たこの問題の最良の解決策は、Knuthの「アルゴリズムL」を使用しています(以前の投稿のGerratによるコメントで述べたように)。
http://stackoverflow.com/questions/12836385/how-can-i-interleave-or-create-unique-permutations-of-two-stings-without-recurs/12837695 =
いくつかのタイミング:
_[1]*12+[0]*12
_(2,704,156のユニークな順列)の並べ替え:
アルゴリズムL→2.43 s
Luke Rahneのソリューション→8.56秒scipy.multiset_permutations()
→16.8 s
この場合、itertools.productを使用して非常に適切な実装を考え出しました(これは、すべての組み合わせが必要な実装です
unique_perm_list = [''.join(p) for p in itertools.product(['0', '1'], repeat = X) if ''.join(p).count() == somenumber]
これは本質的に、n = Xとsomenumber = kの組み合わせ(n上のk)です。itertools.product()は、k = 0からk = Xまで反復します。リスト。 nをkで計算してlen(unique_perm_list)と比較すると、簡単に機能することがわかります。