Подтвердить что ты не робот

Перестановки с уникальными значениями

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

Фильтрация впоследствии невозможна, потому что количество перестановок слишком велико в моем случае.

Кто-нибудь знает подходящий алгоритм для этого?

Большое спасибо!

EDIT:

В основном я хочу:

x = itertools.product((0, 1, 'x'), repeat=X)
x = sorted(x, key=functools.partial(count_elements, elem='x'))

что невозможно, потому что sorted создает список, а результат itertools.product слишком велик.

Извините, я должен был описать фактическую проблему.

4b9b3361

Ответ 1

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 и имеет две функции. Одна функция является условием остановки нашего рекурсивного алгоритма, а другая - для списка результатов, который передается.

Вместо того, чтобы возвращать каждый результат, мы даем его. Если бы не было функции/оператора yield мы бы поместили результат в некоторую очередь в точке условия остановки. Но таким образом, как только условие остановки выполнено, результат распространяется по всем стекам до вызывающей стороны. Это цель
for g in perm_unique_helper(listunique,result_list,d-1): yield g поэтому каждый результат распространяется до вызывающей стороны.

Вернуться к исходной программе: у нас есть список уникальных элементов. Прежде чем мы сможем использовать каждый элемент, мы должны проверить, сколько из них все еще доступно для добавления в result_list. Работа с этой программой очень похожа на permutations_with_replacement. Разница в том, что каждый элемент не может повторяться больше раз, чем в perm_unique_helper.

Ответ 2

Потому что иногда новые вопросы помечены как дубликаты, и их авторы ссылаются на этот вопрос, может быть важно отметить, что 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]]

Ответ 3

Это опирается на деталь реализации, что любая перестановка отсортированного итерабельного в отсортированном порядке, если они не являются дубликатами предыдущих перестановок.

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

Ответ 4

Вы можете попробовать использовать set:

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

Вызов для удаления удаленных дубликатов

Ответ 5

Грубо так же быстро, как Лука Ране отвечает, но короче и проще, ИМХО.

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

Он работает рекурсивно, устанавливая первый элемент (итерируя через все уникальные элементы) и повторяя через перестановки для всех остальных элементов.

Пройдите через unique_permutations (1,2,3,1), чтобы увидеть, как он работает:

  • 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.

Ответ 6

Это мое решение с 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 __name__ == '__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]]

Ответ 7

Наивный подход может заключаться в том, чтобы взять множество перестановок:

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 на порядок выше.


подробности

Из источника алгоритм рекурсии (как показано в принятом ответе) используется для вычисления различных перестановок, тем самым устраняя расточительные вычисления. Подробнее см. Исходный код.

Ответ 8

Звучит так, будто вы ищете itertools.combinations() docs.python.org

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

Ответ 9

Ввязался в этот вопрос, ища что-то сам!

Вот что я сделал:

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

В принципе, создайте набор и продолжайте добавлять к нему. Лучше, чем создавать списки и т.д., Которые занимают слишком много памяти. Надеюсь, это поможет следующему человеку, смотрящему:-) Комментируйте набор "обновление" в функции, чтобы увидеть разницу.

Ответ 10

Рекурсивное решение этой проблемы.

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

Ответ 11

Вы можете создать функцию, которая использует 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 я in unique_permutations('moon')] возвращает:

['moon', 'mono', 'mnoo', 'omon', 'omno', 'nmoo', 'oomn', 'onmo', 'nomo', 'oonm', 'onom', 'noom']

Ответ 12

Что насчет

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

Проблема заключается в том, что теперь перестановки представляют собой строки массива Numpy, тем самым используя больше памяти, но вы можете циклически перемещать их по-прежнему

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

Ответ 13

Пришел к этому на днях, работая над собственной проблемой. Мне нравится подход Луки Ране, но я думал, что использование класса 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')]

Ответ 14

Я придумал очень подходящую реализацию, используя itertools.product в этом случае (это реализация, где вы хотите, чтобы все комбинации

unique_perm_list = [''.join(p) for p in itertools.product(['0', '1'], repeat = X) if ''.join(p).count() == somenumber]

это по существу комбинация (n над k) с n = X и somenumber = k itertools.product() итерации от k = 0 до k = X, последующая фильтрация с помощью count гарантирует, что только перестановки с правильным числом из них список. вы можете легко увидеть, что он работает, когда вы вычисляете n над k и сравниваете его с len (unique_perm_list)

Ответ 15

Лучшее решение этой проблемы, которое я видел, использует Кнут "Алгоритм L" (как отмечено ранее Герратом в комментариях к оригинальному сообщению):
fooobar.com/info/111868/...

Некоторые сроки:

Сортировка [1]*12+[0]*12 (2 704 156 уникальных перестановок):
Алгоритм L → 2,43 с
Решение Люка Ране → 8,56 с
scipy.multiset_permutations() → 16,8 с