Очевидно, что не очень эффективное решение
import itertools
set([s for s in itertools.permutations(orig) if not any([a == b for a, b in zip(s, orig)])])
Второй метод и первое улучшение - это this perm_unique
:
[s for s in perm_unique(orig) if not any([a == b for a, b in zip(s, orig)])]
Третийметод состоит в том, чтобы использовать этот супер быстрый unique_permutations
алгоритм .
import copy
[copy.copy(s) for s in unique_permutations(orig) if not any([a == b for a, b in zip(s, orig)])]
В моей записной книжке с %%timeit
начальный метод занимает 841 µs
, и мы улучшаемся до 266 µs
, а затем до 137 µs
.
Редактировать
Не могу не задумываться, сделал небольшое редактирование второго метода.Не было времени погрузиться в последний метод.Для объяснения, сначала посмотрите оригинальный пост (ссылка выше).Тогда я только добавил чек and el != elements[depth]
, который заставляет состояние расстройства.С этим мы получаем время работы 50 µs
.
from collections import Counter
def derangement_unique(elements):
list_unique = Counter(elements)
length_list = len(elements) # will become depth in the next function
placeholder = [0]*length_list # will contain the result
return derangement_unique_helper(elements, list_unique, placeholder, length_list-1)
def derangement_unique_helper(elements, list_unique, result_list, depth):
if depth < 0: # arrived at a solution
yield tuple(result_list)
else:
# consider all elements and how many times they should still occur
for el, count in list_unique.items():
# ... still required and not breaking the derangement requirement
if count > 0 and el != elements[depth]:
result_list[depth] = el # assignment element
list_unique[el] -= 1 # substract number needed
# loop for all possible continuations
for g in derangement_unique_helper(elements, list_unique, result_list, depth-1):
yield g
list_unique[el] += 1
list(derangement_unique(orig))