hoán vị với các giá trị duy nhất


76

itertools.permutations tạo ra trong đó các phần tử của nó được coi là duy nhất dựa trên vị trí của chúng, không dựa trên giá trị của chúng. Vì vậy, về cơ bản tôi muốn tránh các bản sao như thế này:

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

Việc lọc sau đó là không thể vì số lượng hoán vị quá lớn trong trường hợp của tôi.

Có ai biết về một thuật toán phù hợp cho việc này không?

Cảm ơn rât nhiều!

BIÊN TẬP:

Về cơ bản những gì tôi muốn là:

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

điều này không thể thực hiện được vì sortedtạo một danh sách và đầu ra của itertools.product quá lớn.

Xin lỗi, tôi nên mô tả vấn đề thực tế.


Có gì quá lớn? Tổng số hoán vị hoặc hoán vị DUY NHẤT hoặc cả hai?
FogleBird

4
Có một giải pháp hơn là câu trả lời được chấp nhận (một thực hiện của Knuth Thuật toán L) đưa ra thậm chí còn nhanh hơn ở đây
Gerrat

Bạn đang tìm kiếm hoán vị của Multisets . Hãy xem câu trả lời của Bill Bell dưới đây.
Joseph Wood

Bạn đã thử for x in permutation() set.add(x)?
NotAnAmbiTurner

Có thể một tiêu đề tốt hơn cho câu hỏi này sẽ là "hoán vị riêng biệt". Vẫn tốt hơn, "hoán vị riêng biệt của một danh sách với các bản sao".
Don Hatch

Câu trả lời:


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

kết quả:

[(2, 1, 1), (1, 2, 1), (1, 1, 2)]

CHỈNH SỬA (cách thức hoạt động):

Tôi đã viết lại chương trình trên để dài hơn nhưng dễ đọc hơn.

Tôi thường gặp khó khăn khi giải thích cách hoạt động của một thứ gì đó, nhưng hãy để tôi thử. Để hiểu cách này hoạt động, bạn phải hiểu một chương trình tương tự nhưng đơn giản hơn sẽ mang lại tất cả các hoán vị có lặp lại.

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

Chương trình này rõ ràng là đơn giản hơn nhiều: d là viết tắt của độ sâu trong hoán vị_giúp và có hai chức năng. Một hàm là điều kiện dừng của thuật toán đệ quy của chúng ta và hàm kia là cho danh sách kết quả được chuyển xung quanh.

Thay vì trả về từng kết quả, chúng tôi nhường nó. Nếu không có hàm / toán tử, yieldchúng ta sẽ phải đẩy kết quả vào một số hàng đợi tại điểm của điều kiện dừng. Nhưng theo cách này, một khi điều kiện dừng được đáp ứng, kết quả sẽ được truyền qua tất cả các ngăn xếp đến trình gọi. Đó là mục đích của
for g in perm_unique_helper(listunique,result_list,d-1): yield g mỗi kết quả được truyền đến người gọi.

Quay lại chương trình ban đầu: chúng tôi có một danh sách các phần tử duy nhất. Trước khi có thể sử dụng từng phần tử, chúng tôi phải kiểm tra xem còn bao nhiêu phần tử trong số chúng để đẩy lên result_list. Làm việc với chương trình này rất giống với permutations_with_replacement. Sự khác biệt là mỗi phần tử không thể được lặp lại nhiều lần hơn nó trong perm_unique_helper.


3
Tôi đang cố gắng hiểu cách thức hoạt động của nó, nhưng tôi rất bối rối. Bạn có thể vui lòng cung cấp một số loại bình luận?
Nathan

@Nathan Tôi đã chỉnh sửa câu trả lời và mã tinh chỉnh. Hãy đăng thêm câu hỏi mà bạn có.
Luka Rahne

1
Đoạn mã đẹp. Bạn thực hiện lại itertools.Counter, phải không?
Eric Duminil

Tôi không quen thuộc với Itertools Counter. Mã này là một ví dụ và cho mục đích giáo dục, nhưng ít hơn cho sản xuất, do các vấn đề về hiệu suất. Nếu ai đó cần giải pháp tốt hơn, tôi sẽ đề xuất giải pháp lặp / không đệ quy bắt nguồn từ Narayana Pandita và cũng được giải thích bởi Donad Knuth về nghệ thuật lập trình máy tính với khả năng triển khai python tại stackoverflow.com/a/12837695/429982
Luka Rahne

Tôi tái tạo này với itertools.Counter, nhưng có vẻ như mã của bạn là nhanh hơn :)
Roelant

46

Bởi vì câu hỏi đôi khi mới được đánh dấu là trùng lặp và các tác giả của họ được gọi cho câu hỏi này có thể là quan trọng kể rằng sympy có một iterator cho mục đích này.

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

8
Đây là câu trả lời duy nhất xác định rõ ràng những gì OP thực sự đang tìm kiếm (tức là các hoán vị của Multisets ).
Joseph Wood

25

Điều này dựa vào chi tiết triển khai rằng bất kỳ hoán vị nào của một biến lặp được sắp xếp đều theo thứ tự được sắp xếp trừ khi chúng là bản sao của các hoán vị trước đó.

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

cho

('a', 'a')
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'b')
('b', 'c')
('c', 'a')
('c', 'b')
('c', 'c')

hoạt động hoàn toàn tốt nhưng chậm hơn so với giải pháp được chấp nhận. Cảm ơn bạn!
xyz-123,

Điều này không đúng trong các phiên bản Python mới hơn. Ví dụ, trong Python 3.7.1, list(itertools.permutations([1,2,2], 3))trả về [(1, 2, 2), (1, 2, 2), (2, 1, 2), (2, 2, 1), (2, 1, 2), (2, 2, 1)].
Kirk Strauser

@KirkStrauser: Bạn đúng. Tuyên bố "bất kỳ hoán vị nào của một tệp lặp được sắp xếp đều theo thứ tự được sắp xếp" thậm chí không đúng với các phiên bản Python cũ hơn. Tôi đã thử nghiệm các phiên bản Python trở lại 2.7 và thấy kết quả của bạn chính xác. Điều thú vị là nó không làm mất hiệu lực của thuật toán. Nó tạo ra các hoán vị sao cho chỉ có các hoán vị tối đa tại bất kỳ điểm nào là ban đầu.
Steven Rumbalski

@KirkStrauser: Tôi phải khen ngợi. Bạn không chính xác. Tôi đã chỉnh sửa câu trả lời của mình và đọc kỹ hơn những gì tôi đã viết. Tuyên bố của tôi có một định nghĩa làm cho nó đúng: "bất kỳ hoán vị nào của một tệp có thể lặp lại được sắp xếp đều theo thứ tự được sắp xếp trừ khi chúng là bản sao của các hoán vị trước ."
Steven Rumbalski

15

Gần như nhanh bằng câu trả lời của Luka Rahne, nhưng ngắn hơn và đơn giản hơn, IMHO.

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

Nó hoạt động đệ quy bằng cách thiết lập phần tử đầu tiên (lặp qua tất cả các phần tử duy nhất) và lặp qua các hoán vị cho tất cả các phần tử còn lại.

Hãy đi qua unique_permutations(1,2,3,1) để xem nó hoạt động như thế nào:

  • unique_elements là 1,2,3
  • Hãy lặp lại chúng: first_elementbắt đầu bằng 1.
    • remaining_elements là [2,3,1] (tức là 1,2,3,1 trừ đi 1 đầu tiên)
    • Chúng ta lặp (đệ quy) qua các hoán vị của các phần tử còn lại: (1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)
    • Đối với mỗi sub_permutation, chúng tôi chèn first_element: ( 1 , 1,2,3), ( 1 , 1,3,2), ... và thu được kết quả.
  • Bây giờ chúng ta lặp lại thành first_element= 2 và làm tương tự như trên.
    • remaining_elements là [1,3,1] (tức là 1,2,3,1 trừ đi 2 đầu tiên)
    • Chúng tôi lặp qua các hoán vị của các phần tử còn lại: (1, 1, 3), (1, 3, 1), (3, 1, 1)
    • Với mỗi sub_permutation, chúng ta chèn first_element: ( 2 , 1, 1, 3), ( 2 , 1, 3, 1), ( 2 , 3, 1, 1) ... và thu được kết quả.
  • Cuối cùng, chúng ta làm tương tự với first_element= 3.

13

Bạn có thể thử sử dụng bộ:

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

Lời kêu gọi đặt các bản sao đã xóa


9
Anh ấy có thể cần danh sách (set (itertools.permutations ([1,1,2,2])))
Luka Rahne

2
Hoặc list(itertools.permutations({1,1,2,2}))trong Python 3+ hoặc Python 2.7, do sự tồn tại của các ký tự được đặt. Mặc dù nếu anh ta không sử dụng các giá trị theo nghĩa đen, set()thì dù sao anh ta cũng sẽ sử dụng . Và @ralu: hãy xem lại câu hỏi, lọc sau đó sẽ rất tốn kém.
JAB

32
set (permutations (somelist))! = hoán vị (set (somelist))
Luka Rahne

1
vấn đề với điều này là tôi cần đầu ra có độ dài của đầu vào. Ví dụ: list(itertools.permutations([1, 1, 0, 'x']))nhưng wihtout các bản sao trong đó các bản sao được hoán đổi cho nhau.
xyz-123

2
@JAB: hm, quá trình này mất nhiều thời gian cho hơn 12 giá trị ... những gì tôi thực sự muốn là một cái gì đó giống như itertools.product((0, 1, 'x'), repeat=X)nhưng tôi cần xử lý các giá trị với vài 'x đầu tiên (sắp xếp không phù hợp vì nó đang tạo danh sách và sử dụng nhiều bộ nhớ).
xyz-123

9

Đây là giải pháp của tôi với 10 dòng:

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

--- Kết quả ----

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

Tôi thích giải pháp này
jef

Tôi rất vui vì bạn thích phương pháp này
Ít Roys

Xin chào @LittleRoys. Tôi đã sử dụng một phiên bản mã của bạn được sửa đổi một chút để làm PR trongmore-itertools . Bạn có ổn với điều đó không?
jferard

1
Tôi tò mò, liệu lớp có thêm bất kỳ giá trị nào không? Tại sao đây không chỉ là một chức năng?
Don Hatch

9

Một cách tiếp cận đơn giản có thể là lấy tập hợp các hoán vị:

list(set(it.permutations([1, 1, 1])))
# [(1, 1, 1)]

Tuy nhiên, kỹ thuật này tính toán một cách lãng phí các hoán vị lặp lại và loại bỏ chúng. Một cách tiếp cận hiệu quả hơn sẽ là more_itertools.distinct_permutations, một công cụ của bên thứ ba .

import itertools as it

import more_itertools as mit


list(mit.distinct_permutations([1, 1, 1]))
# [(1, 1, 1)]

Hiệu suất

Sử dụng công cụ có thể lặp lại lớn hơn, chúng tôi sẽ so sánh hiệu suất giữa kỹ thuật ngây thơ và kỹ thuật của bên thứ ba.

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

Chúng tôi thấy more_itertools.distinct_permutationslà một thứ tự cường độ nhanh hơn.


Chi tiết

Từ nguồn, một thuật toán đệ quy (như đã thấy trong câu trả lời được chấp nhận) được sử dụng để tính toán các hoán vị riêng biệt, do đó loại bỏ các phép tính lãng phí. Xem mã nguồn để biết thêm chi tiết.


Đã ủng hộ. list(mit.distinct_permutations([1]*12+[0]*12))cũng nhanh hơn ~ 5,5 lần so với list(multiset_permutations([1]*12+[0]*12))câu trả lời của @Bill Bell.
Darkonaut

3

Đây là một giải pháp đệ quy cho vấn đề.

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

Có vẻ như bạn đang tìm itertools.combaries () docs.python.org

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

8
Không, sự kết hợp sẽ có cùng một vấn đề.
JAB

chỉ cung cấp cho nó theo thứ tự, ví dụ: [1, 2, 3] sẽ tạo ra [1, 2, 3] chứ không phải [3, 2, 1] hoặc [2, 3, 1], v.v.
Jk

1

Đắm mình vào câu hỏi này khi đang tìm kiếm điều gì đó cho bản thân mình!

Đây là những gì tôi đã làm:

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

Về cơ bản, hãy tạo một bộ và tiếp tục thêm vào nó. Tốt hơn là tạo danh sách, vv tốn quá nhiều bộ nhớ .. Hy vọng nó sẽ giúp người tiếp theo tìm ra :-) Hãy bình luận về bộ 'cập nhật' trong hàm để thấy sự khác biệt.


Các , 4cần được loại bỏ vì vậy nó sẽ làm việc vào những thứ của bất kỳ chiều dài. Ngay cả khi đã cố định, đây không phải là một giải pháp tuyệt vời. Đối với một điều, nó lưu trữ tất cả các mục trong bộ nhớ cùng một lúc, đánh bại một số ưu điểm của máy phát điện. Đối với những người khác, nó vẫn siêu kém hiệu quả về mặt thời gian, trong một số trường hợp cần phải có ngay lập tức. Cố gắng for i in dont_repeat([1]*20+[2]): print i; nó sẽ mất mãi mãi.
Don Hatch

1

Giải pháp tốt nhất cho vấn đề này mà tôi đã thấy là sử dụng "Thuật toán L" của Knuth (như đã được Gerrat lưu ý trước đó trong các nhận xét cho bài đăng gốc):
http://stackoverflow.com/questions/12836385/how-can-i-interleave- or-create-unique-permutations-of-two-stings-without-recurs / 12837695

Một số thời gian:

Sắp xếp [1]*12+[0]*12(2.704.156 hoán vị duy nhất):
Thuật toán L → 2,43 giây
Lời giải của Luke Rahne → 8,56 giây
scipy.multiset_permutations()→ 16,8 giây


1

Bạn có thể tạo một hàm sử dụng collections.Counterđể lấy các mục duy nhất và số lượng của chúng từ chuỗi đã cho và sử dụng itertools.combinationsđể chọn kết hợp các chỉ số cho từng mục duy nhất trong mỗi lệnh gọi đệ quy và ánh xạ các chỉ số trở lại danh sách khi tất cả các chỉ số được chọn:

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')]trả về:

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

1

Để tạo các hoán vị duy nhất của ["A","B","C","D"]tôi, tôi sử dụng như sau:

from itertools import combinations,chain

l = ["A","B","C","D"]
combs = (combinations(l, r) for r in range(1, len(l) + 1))
list_combinations = list(chain.from_iterable(combs))

Tạo ra:

[('A',),
 ('B',),
 ('C',),
 ('D',),
 ('A', 'B'),
 ('A', 'C'),
 ('A', 'D'),
 ('B', 'C'),
 ('B', 'D'),
 ('C', 'D'),
 ('A', 'B', 'C'),
 ('A', 'B', 'D'),
 ('A', 'C', 'D'),
 ('B', 'C', 'D'),
 ('A', 'B', 'C', 'D')]

Lưu ý, các bản sao không được tạo (ví dụ: các mục kết hợp với Dkhông được tạo, vì chúng đã tồn tại).

Ví dụ: Điều này sau đó có thể được sử dụng để tạo các điều khoản có thứ tự cao hơn hoặc thấp hơn cho các mô hình OLS thông qua dữ liệu trong khung dữ liệu Pandas.

import statsmodels.formula.api as smf
import pandas as pd

# create some data
pd_dataframe = pd.Dataframe(somedata)
response_column = "Y"

# generate combinations of column/variable names
l = [col for col in pd_dataframe.columns if col!=response_column]
combs = (combinations(l, r) for r in range(1, len(l) + 1))
list_combinations = list(chain.from_iterable(combs))

# generate OLS input string
formula_base = '{} ~ '.format(response_column)
list_for_ols = [":".join(list(item)) for item in list_combinations]
string_for_ols = formula_base + ' + '.join(list_for_ols)

Tạo ra ...

Y ~ A + B + C + D + A:B + A:C + A:D + B:C + B:D + C:D + A:B:C + A:B:D + A:C:D + B:C:D + A:B:C:D'

Sau đó có thể được chuyển đến hồi quy OLS của bạn

model = smf.ols(string_for_ols, pd_dataframe).fit()
model.summary()

0

Tình cờ gặp chuyện này vào một ngày nọ khi đang giải quyết một vấn đề của riêng tôi. Tôi thích cách tiếp cận của Luka Rahne, nhưng tôi nghĩ rằng việc sử dụng lớp Counter trong thư viện bộ sưu tập có vẻ là một cải tiến khiêm tốn. Đây là mã của tôi:

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

Mã này trả về mỗi hoán vị dưới dạng danh sách. Nếu bạn cấp cho nó một chuỗi, nó sẽ cung cấp cho bạn danh sách các hoán vị trong đó mỗi hoán vị là một danh sách các ký tự. Nếu bạn muốn đầu ra là một danh sách các chuỗi thay thế (ví dụ: nếu bạn là một người tồi tệ và bạn muốn lạm dụng mã của tôi để giúp bạn gian lận trong Scrabble), chỉ cần làm như sau:

[''.join(perm) for perm in unique_permutations('abunchofletters')]

0

Tôi đã đưa ra một triển khai rất phù hợp bằng cách sử dụng itertools.product trong trường hợp này (đây là một triển khai mà bạn muốn tất cả các kết hợp

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

về cơ bản đây là sự kết hợp (n trên k) với n = X và somenumber = k itertools.product () lặp lại từ k = 0 đến k = X, việc lọc tiếp theo với số lượng đảm bảo rằng chỉ các hoán vị với đúng số đơn vị được chuyển thành một danh sách. bạn có thể dễ dàng thấy rằng nó hoạt động khi bạn tính n trên k và so sánh nó với len (unique_perm_list)


0

Được điều chỉnh để loại bỏ đệ quy, sử dụng từ điển và numba để có hiệu suất cao nhưng không sử dụng kiểu năng suất / trình tạo để sử dụng bộ nhớ không bị giới hạn:

import numba

@numba.njit
def perm_unique_fast(elements): #memory usage too high for large permutations
    eset = set(elements)
    dictunique = dict()
    for i in eset: dictunique[i] = elements.count(i)
    result_list = numba.typed.List()
    u = len(elements)
    for _ in range(u): result_list.append(0)
    s = numba.typed.List()
    results = numba.typed.List()
    d = u
    while True:
        if d > 0:
            for i in dictunique:
                if dictunique[i] > 0: s.append((i, d - 1))
        i, d = s.pop()
        if d == -1:
            dictunique[i] += 1
            if len(s) == 0: break
            continue
        result_list[d] = i
        if d == 0: results.append(result_list[:])
        dictunique[i] -= 1
        s.append((i, -1))
    return results
import timeit
l = [2, 2, 3, 3, 4, 4, 5, 5, 6, 6]
%timeit list(perm_unique(l))
#377 ms ± 26 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

ltyp = numba.typed.List()
for x in l: ltyp.append(x)
%timeit perm_unique_fast(ltyp)
#293 ms ± 3.37 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

assert list(sorted(perm_unique(l))) == list(sorted([tuple(x) for x in perm_unique_fast(ltyp)]))

Nhanh hơn khoảng 30% nhưng vẫn bị một chút do sao chép và quản lý danh sách.

Ngoài ra, không có numba nhưng vẫn không có đệ quy và sử dụng trình tạo để tránh các vấn đề về bộ nhớ:

def perm_unique_fast_gen(elements):
    eset = set(elements)
    dictunique = dict()
    for i in eset: dictunique[i] = elements.count(i)
    result_list = list() #numba.typed.List()
    u = len(elements)
    for _ in range(u): result_list.append(0)
    s = list()
    d = u
    while True:
        if d > 0:
            for i in dictunique:
                if dictunique[i] > 0: s.append((i, d - 1))
        i, d = s.pop()
        if d == -1:
            dictunique[i] += 1
            if len(s) == 0: break
            continue
        result_list[d] = i
        if d == 0: yield result_list
        dictunique[i] -= 1
        s.append((i, -1))

0

Đây là nỗ lực của tôi mà không cần dùng đến set / dict, như một trình tạo sử dụng đệ quy, nhưng sử dụng chuỗi làm đầu vào. Đầu ra cũng được sắp xếp theo thứ tự tự nhiên:

def perm_helper(head: str, tail: str):
    if len(tail) == 0:
        yield head
    else:
        last_c = None
        for index, c in enumerate(tail):
            if last_c != c:
                last_c = c
                yield from perm_helper(
                    head + c, tail[:index] + tail[index + 1:]
                )


def perm_generator(word):
    yield from perm_helper("", sorted(word))

thí dụ:

from itertools import takewhile
word = "POOL"
list(takewhile(lambda w: w != word, (x for x in perm_generator(word))))
# output
# ['LOOP', 'LOPO', 'LPOO', 'OLOP', 'OLPO', 'OOLP', 'OOPL', 'OPLO', 'OPOL', 'PLOO', 'POLO']

-1

Thế còn

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

Vấn đề là các hoán vị bây giờ là các hàng của một mảng Numpy, do đó sử dụng nhiều bộ nhớ hơn, nhưng bạn có thể chuyển qua chúng như trước

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.