[英]Random combinations without duplicates in Python
假設您有一個大小為n
的可迭代t
。 您想從t
中繪制l
個r
元素的隨機組合。 您要求l
組合不同。 到目前為止,我的看法如下(受 iter 工具食譜的啟發):
def random_combinations(iterable,r,size):
n=len(tuple(iterable))
combinations=[None]*size
f=mt.factorial # Factorial function
nCr=f(n)//(f(r)*f(n-r)) # nCr
iteration_limit=10*nCr # Limit of iterations 10 times nCr
repeated_combinations=0 # Counter of repeated combinations
i=0 # Storage index
combinations[i]=tuple(sorted(rn.sample(xrange(n),r))) # First combination
i+=1 # Advance the counting
while i < size: # Loop for subsequent samples
indices=tuple(sorted(rn.sample(xrange(n),r)))
test=[ combinations[j] for j in range(i) ]
test.append(indices)
test=len(list(set(test)))
if test == i+1: # Test of duplicity
repeated_combinations=0
combinations[i]=indices
i+=1
else:
repeated_combinations+=1
if repeated_combinations == iteration_limit: # Test for iteration limit
break
return combinations
有沒有另一種更有效的方法來做到這一點? 我問這個是因為我將從巨大的可迭代對象(超過 100 個元素)中繪制幾種組合。
在選擇了最有幫助的答案后,我確認該解決方案的問題在於迭代過濾未選擇的組合。 然而,這激發了我尋找一種更快的方法來過濾它們。 我最終以下列方式使用集合
import itertools as it
import math as mt
import random as rn
def random_combinations(iterable,r,l):
"""
Calculates random combinations from an iterable and returns a light-weight
iterator.
Parameters
----------
iterable : sequence, list, iterator or ndarray
Iterable from which draw the combinations.
r : int
Size of the combinations.
l : int
Number of drawn combinations.
Returns
-------
combinations : iterator or tuples
Random combinations of the elements of the iterable. Iterator object.
"""
pool=tuple(iterable)
n=len(pool)
n_combinations=nCr(n,r) # nCr
if l > n_combinations: # Constrain l to be lesser or equal to nCr
l=n_combinations
combinations=set() # Set storage that discards repeated combinations
while len(combinations) < l:
combinations.add(tuple(sorted(rn.sample(zrange(n),r))))
def filtro(combi): # Index combinations to actual values of the iterable
return tuple(pool[index] for index in combi)
combinations=it.imap(filtro,combinations) # Light-weight iterator
return combinations
該集合會自動處理重復的組合。
與其生成所有組合,然后選擇其中一個(其增長速度將比n
快得多),不如執行以下操作:
r
項目的樣本(偽代碼實現如下)。l
個樣本以這種方式存儲。所指的偽代碼如下。 另請參見 L. Devroye 的非均勻隨機變量生成,p。 620。
METHOD RandomRItemsInOrder(t, r)
n = size(t)
// Special case if r is 1
if r==1: return [t[RNDINTEXC(n)]]
i = 0
kk = r
ret = NewList()
while i < n and size(ret) < r
u = RNDINTEXC(n - i)
if u <= kk
AddItem(ret, t[i])
kk = kk - 1
end
i = i + 1
end
return ret
END METHOD
除了上面的偽代碼,您還可以通過水庫采樣生成隨機樣本,但是維護樣本的規范順序並非易事。
您可以在C(n,r)序列中執行 select l
隨機索引,並返回與這些選定隨機索引相對應的組合。
import itertools
import random
import math
def random_combinations(iterable, r, l):
copy1, copy2 = itertools.tee(iterable)
num_combos = math.comb(sum(1 for _ in copy1), r)
rand_indices = set(random.sample(range(num_combos), l))
combos = itertools.combinations(copy2, r)
selected_combos = (x[1] for x in enumerate(combos) if x[0] in rand_indices)
return list(itertools.islice(selected_combos, l))
為了避免遍歷組合,我們需要一種跳過組合的機制。 我不確定 Python 的標准庫中是否存在這種機制。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.