【问题标题】:How do I get all unique combinations and their multiplicities from a Python list?如何从 Python 列表中获取所有唯一组合及其多重性?
【发布时间】:2016-03-03 20:55:24
【问题描述】:

我知道 itertools 有一种生成组合的方法,如下所述:Get unique combinations of elements from a python list。但是,我正在寻找一个迭代器,它可以提供独特的组合它们的多重性。

示例:我有一个表达式,它仅取决于我从列表 L = [2,1,2,2] 中选择的 2 个元素的组合。我需要对所有组合的结果求和。我想要的是一个迭代器,例如(([1,2], 3), ([2,2], 3))。这样,我可以只计算 2 个唯一组合的表达式并乘以 3,而不是计算所有 6 个组合,其中许多组合给出相同的结果。

【问题讨论】:

  • 如果有人能解释为什么这会被否决,那将非常有帮助。这是我在这里的第一个问题,我努力检查现有答案,按照指南的规定提及相关但不充分的答案并写清楚。
  • 如果您已经在 SO 和 interwebz 中搜索了您的答案,但仍未找到任何相关内容,那么您下一步就是尝试自己实现一个迭代器来满足您的需求。如果您提出的问题显示您已搜索但一无所获,并且自己尝试过(并且您的问题显示了您的代码),您可能会得到更好的响应。

标签: python combinatorics


【解决方案1】:

您可以将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 对象。

【讨论】:

  • 不知道为什么人们投反对票。您可以使用一些格式和错字更正,但其他方面看起来还不错。更新了无序的答案,现在我们使用sorted 使它们具有可比性。
  • 谢谢 - 它真的很优雅,我的运行时间至少减少了 10 倍。
【解决方案2】:

最后,我的代码花费了太长时间来明确计算每个可能的组合,所以我想出了一种方法,只找到唯一的组合,然后分析计算它们的多重性。 它基于以下思想:调用输入列表A和每个子集中的元素个数k。首先对列表进行排序,并将 k 指针初始化为 A 的前 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

【讨论】:

    猜你喜欢
    • 2019-10-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-06
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-27
    相关资源
    最近更新 更多