簡體   English   中英

如何從Python列表中獲得所有唯一的組合及其多重性?

[英]How do I get all unique combinations and their multiplicities from a Python list?

我知道itertools有一種生成組合的方法,如下所述: 從python list中獲取元素的唯一組合 不過,我正在尋找一個迭代器,讓獨特的組合它們的重數。

示例:我有一個表達式僅取決於我從列表L = [2,1,2,2]中選擇2個元素的哪種組合。 我需要對所有組合的結果求和。 我想要的是一個給出(([[1,2],3),([2,2],3))的迭代器。 這樣,我可以僅針對2個唯一組合計算表達式,然后乘以3,而不是針對所有6種組合進行計算,其中許多組合給出相同的結果。

您可以將itertools.combinationscollections.Counter結合使用。

import itertools
import collections  

L =  [2,1,2,2]
c = collections.Counter()
c.update(map(tuple, map(sorted, itertools.combinations(L, 2))))

c.items()然后給出:

>>> c.items()
[((1, 2), 3), ((2, 2), 3)]

為了進行分解, itertools.combinations(L, 2)給出長度為2的所有L的有序組合。然后使用sorted使它們具有可比性,因為collections.Counter將使用哈希和相等性進行計數。 最后,由於list對象不可哈希,因此我們將其轉換為tuple對象。

最后,我的代碼花費了太多時間,無法顯式地計算每種可能的組合,因此我想出了一種方法,僅找到唯一的組合,然后分析計算它們的多重性。 它基於以下思想:調用輸入列表A和每個子集k中的元素數。 首先對列表進行排序,並初始化指向A的前k個元素的k個指針。然后反復嘗試將最右邊的指針向右移動,直到遇到新值為止。 每次移動距離最右邊的另一個指針時,指向右邊的所有指針都將設置為它的鄰居,例如,如果指針1移動到索引6,指針2移動到索引7,依此類推。

任何組合C的多重性都可以通過將二項式系數(N_i,m_i)相乘來找到,其中N_i和m_i分別是元素i在A和C中出現的次數。

下面是蠻力方法的一種實現,以及一種利用唯一性的方法。

該圖將蠻力計數的運行時間與我的方法進行了比較。 當輸入列表包含約20個元素時,計數將變得不可行。 運行時比較

# -*- coding: utf-8 -*-
from __future__ import division

from itertools import combinations
from collections import Counter
from operator import mul
import numpy as np
from scipy.special import binom

def brute(A, k):
    '''This works, but counts every combination.'''
    A_sorted = sorted(A)
    d = {}
    for comb in combinations(A_sorted, k):
        try:
            d[comb] += 1
        except KeyError:
            d[comb] = 1
        #
    return d


def get_unique_unordered_combinations(A, k):
        '''Returns all unique unordered subsets with size k of input array.'''
    # If we're picking zero elements, we can only do it in one way. Duh.
    if k < 0:
        raise ValueError("k must be non-negative")

    if k == 0 or k > len(A):
        yield ()
        return  # Done. There's only one way to select zero elements :)

    # Sorted version of input list
    A = np.array(sorted(A))
    # Indices of currently selected combination
    inds = range(k)
    # Pointer to the index we're currently trying to increment
    lastptr = len(inds) - 1

    # Construct list of indices of next element of A different from current.
    # e.g. [1,1,1,2,2,7] -> [3,3,3,5,5,6] (6 falls off list)
    skipper = [len(A) for a in A]
    prevind = 0
    for i in xrange(1, len(A)):
        if A[i] != A[prevind]:
            for j in xrange(prevind, i):
                skipper[j] = i
            prevind = i
        #

    while True:
        # Yield current combination from current indices
        comb = tuple(A[inds])
        yield comb

        # Try attempt to change indices, starting with rightmost index
        for p in xrange(lastptr, -1 , -1):
            nextind = skipper[inds[p]]
            #print "Trying to increment index %d to %d"  % (inds[p], nextind)
            if nextind + (lastptr - p) >= len(A):
                continue  # No room to move this pointer. Try the next
            #print "great success"
            for i in xrange(lastptr-p+1):
                inds[p+i] = nextind + i
            break
        else:
            # We've exhausted all possibilities, so there are no combs left
            return

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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