通过从每个集合中仅选择一个值来选择 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 个大小的子集的主要内容,如果未能解决你的问题,请参考以下文章

如何通过从数据库中获取值来制作可靠的下拉列表

无法通过从两个不同子例程传递给新子例程的值来执行计算:Perl

通过从数组中选择来创建排列

如何通过从数据库中检索值来显示文本框中的值?

集合视图 - 如何在每个部分中仅选择一个单元格

6041 I Curse Myself(点双联通加集合合并求前K大) 2017多校第一场