簡體   English   中英

Python 中沒有重復的隨機組合

[英]Random combinations without duplicates in Python

假設您有一個大小為n的可迭代t 您想從t中繪制lr元素的隨機組合。 您要求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快得多),不如執行以下操作:

  • 創建一個空的 hash 表或集。
  • 按順序選擇r項目的樣本(偽代碼實現如下)。
  • 檢查樣本是否已經存在(例如,作為 hash 表中的鍵或集合中的值)。 如果不是,則存儲該樣本(例如,作為 hash 表中的鍵或集合中的值)。
  • 繼續,直到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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM