我正在寻找一种方法来列举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名成员,手动编组是不可行的。是否有一种简单的方法可以获得包含所有可能的群星座的列表/数据帧?
答案 0 :(得分:4)
这看起来很有效:
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个条目,根据公式检出
基本上,我正在做的只是抓住第一个元素与其他元素的组合,然后将余数传递给递归 - 这确保没有重复的组。
答案 1 :(得分:4)
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)
答案 2 :(得分:1)
如果要将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=", " ) ) )
答案 3 :(得分:1)
我想出了:
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