【问题标题】:Enumeration of all possible two-member group constellations枚举所有可能的二人组星座
【发布时间】:2012-01-16 21:26:40
【问题描述】:

我正在寻找一种方法来为 n 个成员枚举所有可能的二人组星座。

例如,对于 n = 4 个成员,以下 3 个唯一的组群是可能的(请注意,组内成员的顺序和组顺序都不重要):

((1,2), (3,4))
((1,3), (2,4))
((1,4), (2,3))

例如,对于 n = 6 个成员,15 个独特的星座是可能的:

((1,2), (3,4), (5,6))
((1,2), (5,4), (3,6))
((1,2), (6,4), (5,3))
((1,3), (2,4), (5,6))
((1,3), (2,6), (5,4))
((1,3), (2,5), (4,6))
((1,4), (3,2), (5,6))
((1,4), (3,5), (2,6))
((1,4), (3,6), (5,2))
((1,5), (3,4), (2,6))
((1,5), (3,2), (4,6))
((1,5), (3,6), (2,4))
((1,6), (3,4), (5,2))
((1,6), (3,5), (2,4))
((1,6), (3,2), (5,4))

对于 n 个成员,唯一组的数量可以计算为

choose(n,2)*choose(n-2,2)*...*choose(2,2)/factorial(n/2),

其中choose(n,k) 是二项式系数。

对于 n = 4,我们有

choose(4,2)/factorial(4/2) = 3 

可能的二人组星座。对于 n = 6,它是

choose(6,2)*choose(4,2)/factorial(6/2) = 15. 

对于超过 n = 6 个成员,手动枚举组是不可行的。有没有一种简单的方法来获取包含所有可能的组星座的列表/数据框?

【问题讨论】:

  • 我不知道具体怎么做,但是看看 itertools:docs.python.org/library/itertools.html
  • 我以为我明白了,但现在我意识到我不明白。您的 15 个列表包括 ((3,2), (1,4), (5,6)) 和 ((1,4), (3,2), (5,6)),以及几个我的代码认为等效的其他对。我错过了什么?
  • 是的,看起来 OP 中的列表不正确。尽管如此,仍然有一个满足给定条件的 15 个独特列表;看我的回答。
  • -2 没有发布可以轻松检查的答案。编辑了你的“答案”。
  • @DSM 是的,我在列举 n = 6 示例时犯了一个错误。对不起。但是,正如我在上面所写的那样,手动枚举非常麻烦。这就是我问这个问题的原因。不过还是谢谢你。

标签: python r


【解决方案1】:

这看起来可行:

from itertools import combinations, islice

def cons(nums):
    if len(nums)%2 or len(nums)<2:
        raise ValueError
    if len(nums) == 2:
        yield (nums,)
        return
    for c in islice(combinations(nums, 2), len(nums)-1):
        for sub in cons(tuple(set(nums) - set(c))):
            yield ((c,) + sub)

def constellations(n):
    return cons(range(1, n+1))

for c in constellations(6):
    print c

输出:

((1, 2), (3, 4), (5, 6))
((1, 2), (3, 5), (4, 6))
((1, 2), (3, 6), (4, 5))
((1, 3), (2, 4), (5, 6))
((1, 3), (2, 5), (4, 6))
((1, 3), (2, 6), (4, 5))
((1, 4), (2, 3), (5, 6))
((1, 4), (2, 5), (3, 6))
((1, 4), (2, 6), (3, 5))
((1, 5), (2, 3), (4, 6))
((1, 5), (2, 4), (3, 6))
((1, 5), (2, 6), (3, 4))
((1, 6), (2, 3), (4, 5))
((1, 6), (2, 4), (3, 5))
((1, 6), (2, 5), (3, 4))

为constellations(8) 生成 105 个条目,根据公式签出。
本质上,我所做的只是抓取第一个元素与其他元素的组合,然后将剩余部分传递给递归——这样可以确保没有重复的组。

【讨论】:

  • 非常高效 - 非常感谢。这个解决方案非常有用,因为它允许我们为超过 n = 30 个成员枚举星座。
【解决方案2】:

编写 R 包 partitions 是为了回答像您这样的问题,它(用数学术语)是关于将六个元素的所有可能的 partitions of a set 枚举为三个等价类,每个等价类两个元素。

该包提供了两个函数——setparts() 和listParts()——将枚举所有分区。这些函数的不同之处仅在于它们返回这些结果的格式。

在这里,我展示了listParts() 函数的输出,主要是因为它返回的打印格式更接近您在原始问题中包含的内容:

    library(partitions)
    P <- listParts(c(2,2,2)) 
    N <- sapply(P, print)
    # [1] (1,6)(2,5)(3,4)
    # [1] (1,6)(2,4)(3,5)
    # [1] (1,6)(2,3)(4,5)
    # [1] (1,2)(3,6)(4,5)
    # [1] (1,2)(3,5)(4,6)
    # [1] (1,2)(3,4)(5,6)
    # [1] (1,3)(2,6)(4,5)
    # [1] (1,3)(2,4)(5,6)
    # [1] (1,3)(2,5)(4,6)
    # [1] (1,4)(2,6)(3,5)
    # [1] (1,4)(2,5)(3,6)
    # [1] (1,4)(2,3)(5,6)
    # [1] (1,5)(2,6)(3,4)
    # [1] (1,5)(2,4)(3,6)
    # [1] (1,5)(2,3)(4,6)

【讨论】:

  • 谢谢,这是一个非常优雅的解决方案。不幸的是,对于超过 n = 8 个成员,R 无法枚举所有组星座。
  • 这个包叫partitions吗?我在 CRAN 上看不到 partition,但有 partitions。
  • @phx 你能解释一下超过 n = 8 是什么意思吗?我做了P &lt;- listParts(c(2,2,2,2,2))(即n = 10),它提供了945个分区。
  • @GavinSimpson:看来这很大程度上取决于运行 R 的机器。我家里的电脑给了我一些错误信息,n >= 8。但是,我工作的电脑可以处理 n >= 8。由于 R 中的内存分配问题是众所周知的,我总是使用 Python 代码来处理更大的 ns .特别是,因为它比 R 快得多。——无论如何,R 代码真的很漂亮和优雅;)。
  • @phx 我在 n = 30 时收到一个错误,但话又说回来,我不确定您将如何处理所有 6.190283e+15“星座”,即使您可以生成它们。这与内存分配无关,但由于 R 仅使用 32 位索引,您正在接近向量可以有多长的限制。但是,错误来自函数,而不是 R,因此包作者正在限制它可以使用的向量的大小。
【解决方案3】:

如果您想将 1:n 的所有分区枚举成对,您可以递归地进行。 这是一个 R 解决方案。

f <- function(x) {
  # We can only partition the set into pairs 
  # if it has an even number of elements
  stopifnot( length(x) %% 2 == 0 )
  stopifnot( length(x) > 0 )
  # To avoid double counting, sort the array, 
  # and put the first element in the first pair
  x <- sort(x)
  # The first pair contains the first element 
  # and another element: n - 1 possibilities
  first_pairs <- lapply( x[-1], function(u) c(x[1],u) )
  if( length(x) == 2 ) { return( list( first_pairs ) ) }
  # Progressively build the result, by considering 
  # those pairs one at a time
  result <- list()
  for( first_pair in first_pairs ) {
    y <- setdiff( x, first_pair )
    rest <- f(y)
    # Call the function recursively: 
    # a partition of 1:n that starts with (1,2)
    # is just (1,2) followed by a partition of 3:n.
    result <- append( 
      result, 
      # This is the tricky bit: 
      # correctly use append/c/list to build the list.
      lapply( rest, function (u) { append( list( first_pair ), u ) } )  
    )
  }
  result
}

# The result is a list of lists of 2-element vectors: print it in a more readable way.
result <- f(1:6)
result <- lapply( result, function (u) unlist(lapply( u, function (v) paste( "(", paste(v,collapse=","), ")", sep="" ))))
result <- unlist( lapply( result, function (u) paste( u, collapse=", " ) ) )

【讨论】:

    【解决方案4】:

    我想出了:

    from itertools import combinations
    
    def have_common(a, b):
        """Test if two iterables have a common item."""
        for i in a:
            if i in b:
                return True
        return False
    
    def have_same(iterable):
        """Test if a nested iterable like ((1, 2), (3, 4), (5, 6)) 
        present the same number more then once.
    
        """
        memory = []
        for duo in iterable:
            if have_common(memory, duo):
                return True
            else:
                memory.extend(duo)
        return False
    
    def constellation(num):
        """Loops on all the combinations of 2 combinations and then yields them
        if they don't have numbers in common.
    
        """
        lst = (i for i in combinations(range(1, num+1), 2))
        for cost in combinations(lst, int(num/2)):
            if not have_same(cost):
                yield cost
    

    跑步:

    for i in constellation(6):
        print(i)
    

    我明白了:

    ((1, 2), (3, 4), (5, 6))
    ((1, 2), (3, 5), (4, 6))
    ((1, 2), (3, 6), (4, 5))
    ((1, 3), (2, 4), (5, 6))
    ((1, 3), (2, 5), (4, 6))
    ((1, 3), (2, 6), (4, 5))
    ((1, 4), (2, 3), (5, 6))
    ((1, 4), (2, 5), (3, 6))
    ((1, 4), (2, 6), (3, 5))
    ((1, 5), (2, 3), (4, 6))
    ((1, 5), (2, 4), (3, 6))
    ((1, 5), (2, 6), (3, 4))
    ((1, 6), (2, 3), (4, 5))
    ((1, 6), (2, 4), (3, 5))
    ((1, 6), (2, 5), (3, 4))
    

    性能:这仍然可以通过have_same 和have_common 的更好算法来改进。

    但我还是用timit做了一点时间,我得到了:

    constellation(4): 13.54 usec/pass
    constellation(6): 118.48 usec/pass
    constellation(8): 3222.14 usec/pass
    

    【讨论】:

    • 非常感谢。效果很好。
    猜你喜欢
    • 2021-07-31
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 2017-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多