通过从每个集合中仅选择一个值来选择 k 个大小的子集
Posted
技术标签:
【中文标题】通过从每个集合中仅选择一个值来选择 k 个大小的子集【英文标题】:select k size subset by picking only one value from each set 【发布时间】:2020-03-13 01:50:14 【问题描述】:给定一个长度为 n 个元素的数组,其中每个元素表示集合大小,确定可以选择 K 大小集合的方式数。
条件:您不能从一组中选择多个元素。如何 解决这个问题(任何程序)
例子:
输入:
n = 4
k = 3
1,2,1,1 Each value represents number of elements in each set
输出:7
Example :
1,2,3,4,5
1,2,4
1,2,5
1,3,4
1,3,5
1,4,5
2,4,5
3,4,5
我尝试过的代码,但它返回 10 个不符合条件的值 我在这里犯了什么错误? 他们只是给出了子集长度而不是实际子集。所以我基于所有子集长度的总和,我正在形成一个新数组
count = 0
def printCombination(arr, n, r):
global count
data = [0]*r
combinationUtil(arr, data, 0,
n - 1, 0, r)
def combinationUtil(arr, data, start,
end, index, r):
global count
if (index == r):
for j in range(r):
print(data[j], end=" ")
print()
count += 1
return
i = start
while(i <= end and end - i + 1 >= r - index):
data[index] = arr[i]
combinationUtil(arr, data, i + 1,
end, index + 1, r)
i += 1
in_val = [1,2,1,1]
arr = list(range(1,sum(in_val)+1)) r = 3
n = len(arr)
printCombination(arr, n, r)
print(count)
我们可以用一些公式用最少的时间解决这个问题,而不是模拟每个子集并遍历。请对此有所了解或给我建议以进一步进行。
【问题讨论】:
给定条件“您不能从一组中选择多个元素”为什么 1,2,3 有效?这不是从集合 2,3 中选择两个值吗? 1,3,4 和 1,4,3 不是同一个集合吗? @DarrylG 错误已更正 不应允许同组 @johnwilson——为计数创建了一个函数(因为你提到只需要方法的数量)。如果您需要实际的套装,请告诉我。 【参考方案1】:假设所有输入集都是不相交的,动态规划方法允许我们在O(n)
时间内计算此类组合的数量,其中n
是集的数量(假设输入集的基数由一个常数限定; 否则,时间复杂度为O(n, max_size)
,其中max_size
是具有最大大小的输入集的基数。
import random # testing
from functools import lru_cache # memoization
xs = [1,2,3,4,5]
k = 3
sizes = [len(x) for x in xs]
# [1, 2, 1, 1]
# dynamic programming approach
def count_combinations(sizes, k=3):
@lru_cache(None)
def f(n, k):
if (n < k) or (k < 0):
# no combination possible
return 0
elif n == k:
# return product sizes[:n]
res = 1
for x in sizes[:n]:
res *= x
return res
else:
# recursive memoized call
# f(n-1, k-1) ways to select k-1 elts from n-1 sets
# times size of n-1'st set (counting from 0)
# plus f(n-1, k) ways to select k elts from n-1 sets
return sizes[n-1] * f(n-1, k-1) + f(n-1, k)
return f(len(sizes), k)
# assert count_combs(sizes, k=k) == count_combinations(sizes, k)
# larger benchmark
n = 25
k = n // 2
xs = [random.randint(0, n) for i in range(n) for _ in range(n)]
sizes = [len(x) for x in xs]
%time count_combs(sizes, k) # O(n choose k), 8.6 s
%timeit count_combinations(sizes, k) # O(n), 112 µs
这避免了明确考虑大小 k 的所有可能组合,将复杂性从 O(n choose k)
降低到 O(n)
。
【讨论】:
我的回答速度令人印象深刻。【参考方案2】:确定可以选择 K 尺寸集的方法数。
from itertools import combinations
from functools import reduce
def count_combs(arr, k):
if k > len(arr):
return 0 # Not possible
elif k == len(arr):
return reduce(lambda a, b: a*b, arr) # multiply values in arr
else:
"""sum of answer to each sub-set of arr of size k
subsets of arr of size k are combinations(arr, k)"""
return sum(count_combs(x, k) for x in combinations(arr, k))
测试
arr = [1, 2, 1, 1]
print(count_combs(arr, 3))
# Outputs 7
arr = [1, 1, 1, 1]
print(count_combs(arr, 3))
# Outputs 4
arr = [1, 1, 1, 2]
print(count_combs(arr, 2))
# Output 9
说明
三种情况
-
k > len(arr): 不可能,所以答案是 0
k == len(arr):它是我们可以从每个数组索引中一次获取一个元素的方法数,它是数组 arr 值的乘积。
k
【讨论】:
以上是关于通过从每个集合中仅选择一个值来选择 k 个大小的子集的主要内容,如果未能解决你的问题,请参考以下文章