[英]Python combinations without repetitions
我有一個數字列表,我想從中進行組合。 如果我有清單:
t = [2,2,2,2,4]
c = list(itertools.combinations(t, 4))
結果是:
(2, 2, 2, 2)
(2, 2, 2, 4)
(2, 2, 2, 4)
(2, 2, 2, 4)
(2, 2, 2, 4)
但我想得到:
(2, 2, 2, 2)
(2, 2, 2, 4)
除了制作新列表並通過第一個列表之外,是否可以消除重復項?
由於大金剛指向設置,您可以通過將列表轉換為集合來獲取列表中的唯一值:
t = [2,2,2,2,4]
c = list(itertools.combinations(t, 4))
unq = set(c)
print(unq)
結果將是:
{(2, 2, 2, 4), (2, 2, 2, 2)}
如果要將其用作列表,可以通過執行以下操作將其轉換回來:
result = list(unq)
另一種更干凈、更全面的方法是:
t = [2,2,2,2,4]
c = set(itertools.combinations(t, 4))
我知道這已經晚了,但我想補充一點。
set(itertools.combinations(t, 4))
在大多數情況下會做得很好,但它仍然在內部迭代所有重復的組合,因此計算量可能很大。 如果沒有很多實際的獨特組合,情況尤其如此。
這個只迭代獨特的組合:
from itertools import chain,repeat,count,islice
from collections import Counter
def combinations_without_repetition(r, iterable=None, values=None, counts=None):
if iterable:
values, counts = zip(*Counter(iterable).items())
f = lambda i,c: chain.from_iterable(map(repeat, i, c))
n = len(counts)
indices = list(islice(f(count(),counts), r))
if len(indices) < r:
return
while True:
yield tuple(values[i] for i in indices)
for i,j in zip(reversed(range(r)), f(reversed(range(n)), reversed(counts))):
if indices[i] != j:
break
else:
return
j = indices[i]+1
for i,j in zip(range(i,r), f(count(j), counts[j:])):
indices[i] = j
用法:
>>> t = [2,2,2,2,4]
# elements in t must be hashable
>>> list(combinations_without_repetition(4, iterable=t))
[(2, 2, 2, 2), (2, 2, 2, 4)]
# You can pass values and counts separately. For this usage, values don't need to be hashable
# Say you have ['a','b','b','c','c','c'], then since there is 1 of 'a', 2 of 'b', and 3 of 'c', you can do as follows:
>>> list(combinations_without_repetition(3, values=['a','b','c'], counts=[1,2,3]))
[('a', 'b', 'b'), ('a', 'b', 'c'), ('a', 'c', 'c'), ('b', 'b', 'c'), ('b', 'c', 'c'), ('c', 'c', 'c')]
# combinations_without_repetition() is a generator (and thus an iterator)
# so you can iterate it
>>> for comb in combinations_without_repetition(4, t):
... print(sum(comb))
...
8 # 2+2+2+2
10 # 2+2+2+4
請注意, itertools.combinations()
是用 C 實現的,這意味着在大多數情況下它比我的 python 腳本快得多。 僅當重復組合多於唯一組合時,此代碼才比set(itertools.combinations())
方法更有效。
從技術上講,你得到的實際上並不是重復的,這只是itertools.combinations
工作方式,如果你閱讀鏈接頁面中的描述:
itertools.combinations(iterable, r)
從輸入迭代中返回元素的 r 個長度子序列。
組合按字典排序順序發出。 因此,如果輸入可迭代對象已排序,則組合元組將按排序順序生成。
元素被視為唯一基於它們的位置,而不是它們的值。 因此,如果輸入元素是唯一的,則每個組合中都不會出現重復值。
演示:
>>> import itertools as it
>>> list(it.combinations([1,2,3,4,5], 4))
[(1, 2, 3, 4), (1, 2, 3, 5), (1, 2, 4, 5), (1, 3, 4, 5), (2, 3, 4, 5)]
因此,正如在上一個答案中發布的那樣, set()
將為您提供所需的唯一值:
>>> set(it.combinations(t, 4))
{(2, 2, 2, 4), (2, 2, 2, 2)}
現在可以使用 package more-itertools來完成,從 8.7 版開始,它有一個名為distinct_combinations的 function 來實現這一點。
>>> from itertools import combinations
>>> t = [2,2,2,2,4]
>>> set(combinations(t, 4))
{(2, 2, 2, 2), (2, 2, 2, 4)}
>>> from more_itertools import distinct_combinations
>>> t = [2,2,2,2,4]
>>> list(distinct_combinations(t,4))
(2, 2, 2, 2), (2, 2, 2, 4)]
據我所知,我非常有限的測試性能類似於@hahho編寫的 function
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.