web-dev-qa-db-ja.com

一意の値を持つ順列

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の出力が大きすぎるため、これは不可能です。

申し訳ありませんが、実際の問題を説明する必要がありました。

67
xyz-123
_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にあるよりも各要素を繰り返すことができないことです。

50
Luka Rahne

新しい質問が重複としてマークされ、著者がこの質問を参照する場合があるため、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]]
30
Bill Bell

これは、ソート済みのイテラブルの順列は、前の順列の複製でない限りソート順であるという実装の詳細に依存しています。

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')
21

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を引いたもの)
    • 残りの要素の順列を(再帰的に)繰り返します:(1、2、3)、(1、3、2)、(2、1、3)、(2、3、1)、(3、1、 2)、(3、2、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を引いたもの)
    • 残りの要素の順列を繰り返します:(1、1、3)、(1、3、1)、(3、1、1)
    • sub_permutationに対して、first_elementを挿入します:(2、1、1、3)、(-2、1、3、1) 、(2、3、1、1)...と結果が得られます。
  • 最後に、first_element = 3で同じことを行います。
11
MiniQuark

あなたはセットを使って試すことができます:

>>> list(itertools.permutations(set([1,1,2,2])))
[(1, 2), (2, 1)]

削除された重複を設定する呼び出し

11
Paul Rubel

これは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]]
8
Little Roys

単純なアプローチは、順列のセットを取ることです。

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は、桁違いに高速です。


詳細

ソースから、再帰アルゴリズム(受け入れられた答えに見られるように)を使用して個別の順列を計算し、それによって無駄な計算を回避します。詳細については、 ソースコード を参照してください。

5
pylang

Itertools.combinations()を探しているように聞こえます docs.python.org

list(itertools.combinations([1, 1, 1],3))
[(1, 1, 1)]
3
Fredrik Pihl

自分で何かを探しているときにこの質問にぶつかった!

私がやったことは次のとおりです。

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)])

基本的に、セットを作成して追加し続けます。あまりにも多くのメモリを消費するリストなどを作成するよりも良い方法です。次の人が外を見るのに役立つことを願っています:-)関数の「更新」セットをコメントアウトして、違いを確認してください。

2
Ashish Datta

これが問題の再帰的な解決策です。

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))
2
prafi

_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']
_
1
blhsing

どう?

np.unique(itertools.permutations([1, 1, 1]))

問題は、順列がNumpy配列の行であるため、より多くのメモリを使用することですが、以前と同様に循環させることができます

perms = np.unique(itertools.permutations([1, 1, 1]))
for p in perms:
    print p
0
Andre Manoel

私自身の問題に取り組んでいる間、先日これに出くわしました。 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')]
0
CCC

私が見たこの問題の最良の解決策は、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

0
bmorgan

この場合、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)と比較すると、簡単に機能することがわかります。

0
mnmldani