计算具有给定大小的集合的子集

时间:2011-05-08 17:26:59

标签: algorithm math combinatorics

给定具有n个元素的集合C(允许重复)和具有n
个的分区P. P = {i1,i2,... / i1 + i2 + ... = n} 在大小为i1,i2,......的子集中有多少不同的C分解?

示例:

C = {2 2 2 3}

P = {2 2}
C = {2 2} U {2 3}

P = {1 1 2}
C = {2} U {2} U {2 3}
C = {2} U {3} U {2 2}

P = {1 3}
C = {2} U {2 2 3}
C = {3} U {2 2 2}

我有一个解决方案,但是当C有十几个元素时效率很低 提前谢谢你 菲利普

2 个答案:

答案 0 :(得分:1)

分解顺序对你来说无关紧要的事实使得它变得更加困难。也就是说,您正在查看{2 2} U {2 3}{2 3} U {2 2}相同。我的算法仍然比你的算法好,但不是很好。

让我以一个现实复杂的例子开始吧。我们的设置为A B C D E F F F F G G G G。分区将为1 1 1 1 2 2 5

我的第一个简化是用数据结构[[2, 4], [5, 1]]来表示我们关注的信息,意味着2个元素重复4次,5个重复一次。

我的第二个明显的复杂因素是用[[5, 1, 1], [2, 2, 1], [4, 1, 1]表示分区。这种模式可能并不明显。每个条目的格式为[size, count, frequency]。因此,2个大小为2的分区的一个不同实例变为[2, 2, 1]。我们还没有使用频率,但它正在计算相同大小和共性的可区分堆。

现在我们要按如下方式递归。我们将采用最常见的元素,并找到使用它的所有方法。因此,在我们的例子中,我们采用4号堆中的一个,发现我们可以按如下方式划分它,按字典顺序重新排列每个剩余的分区策略:

  1. [4]离开[[1, 1, 1], [2, 2, 1], [1, 4, 1]] = [[2, 2, 1], [1, 4, 1], [1, 1, 1]]
  2. [3, [1, 0], 0]离开[[2, 1, 1], [1, 1, 1], [2, 1, 1], [1, 4, 1]] = [[2, 1, 2], [1, 4, 1], [1, 1, 1]。 (请注意,我们现在正在使用频率。)
  3. [3, 0, 1]离开[[2, 1, 1], [2, 2, 1], [0, 1, 1], [1, 3, 1]] = [[2, 2, 1], [2, 1, 1], [1, 3, 1]]
  4. [2, [2, 0], 0]离开[[3, 1, 1], [0, 1, 1], [2, 1, 1], [1, 4, 1]] = [[3, 1, 1], [2, 1, 1], [1, 4, 1]]
  5. [2, [1, 1], 0]离开[[3, 1, 1], [1, 2, 1], [1, 4, 1]] = [[3, 1, 1], [1, 4, 1], [1, 2, 1]]
  6. [2, [1, 0], [1]]离开[[3, 1, 1], [1, 1, 1], [2, 1, 1], [0, 1, 1], [1, 3, 1]] = [[3, 1, 1], [2, 1, 1], [1, 4, 1], [1, 1, 1]]
  7. [2, 0, [1, 1]]离开`[[3,1,1],[2,2,1],[0,2,1],[1,2,1]] = [[3,1, 1],[2,2,1],[1,2,1]] 1
  8. [1, [2, 1]]离开[[4, 1, 1], [0, 1, 1], [1, 1, 1], [1, 4, 1]] = [[4, 1, 1], [1, 4, 1], [1, 1, 1]]
  9. [1, [2, 0], [1]]离开[[4, 1, 1], [0, 1, 1], [2, 1, 1], [0, 1, 1], [1, 3, 1]] = [[4, 1, 1], [2, 1, 1], [1, 3, 1]]
  10. [1, [1, 0], [1, 1]]离开[[4, 1, 1], [1, 1, 1], [2, 1, 1], [0, 2, 1], [1, 2, 1]] = [[4, 1, 1], [2, 1, 1], [1, 2, 1], [1, 1, 1]]
  11. [1, 0, [1, 1, 1]]离开[[4, 1, 1], [2, 2, 1], [0, 3, 1], [1, 1, 1]] = [[4, 1, 1], [2, 2, 1], [1, 1, 1]]
  12. [0, [2, 2]]离开[[5, 1, 1], [0, 2, 1], [1, 4, 1]] = [[5, 1, 1], [1, 4, 1]]
  13. [0, [2, 1], [1]]离开[[5, 1, 1], [0, 1, 1], [1, 1, 1], [0, 1, 1], [1, 3, 1]] = [[5, 1, 1], [1, 3, 1], [1, 1, 1]]
  14. [0, [2, 0], [1, 1]]离开[[5, 1, 1], [0, 2, 1], [2, 1, 1], [0, 2, 1], [1, 2, 1]] = [[5, 1, 1], [2, 1, 1], [1, 2, 1]]
  15. [0, [1, 1], [1, 1]]离开[[5, 1, 1], [1, 2, 1], [0, 2, 1], [1, 2, 1]] = [[5, 1, 1,], [1, 2, 2]]
  16. [0, [1, 0], [1, 1, 1]]离开[[5, 1, 1], [1, 1, 1], [2, 1, 1], [0, 3, 1], [1, 1, 1]] = [[5, 1, 1], [2, 1, 1], [1, 1, 2]]
  17. [0, 0, [1, 1, 1, 1]]离开[[5, 1, 1], [2, 2, 1], [0, 4, 1]] = [[5, 1, 1], [2, 2, 1]]
  18. 现在可以递归地解决每个子问题。这可能感觉我们正在构建所有这些,但我们不是,因为我们memoize递归步骤。事实证明,前两组8人可以通过很多方式结束相同的5次剩余。通过记忆,我们不需要反复重新计算这些解决方案。

    那就是说,我们会做得更好。 12个元素的组不应该成为问题。但我们并没有更好地 。如果它开始在30个左右的元素组中分解,并且有一组有趣的分区,我不会感到惊讶。 (我没有对它进行编码。它可能在30处可以正常,在50处可以分解。我不知道它会在哪里发生故障。但是考虑到你在一些分区上进行迭代,在一些相当小的点上它< em>将分解。)

答案 1 :(得分:0)

所有分区都可以分两个阶段找到。

首先:从P创建新的有序分区n,P_S={P_i1, P_i2, ..., P_ip},总结相同的i。

P = {1, 1, 1, 1, 2, 2, 5}
P_S = (4, 4, 5)

针对{C_i1, C_i2, ..., C_ip}制作C分区P_S。请注意,C_ix是多集的,如C。它按照最终分区的大小将C划分为多个集合。

第二:针对每个{C_i1, C_i2, ..., C_ip}和每个ix, x={1,2,...,p}查找C_ixt的分区数(ix'sP的数量)设置ix个元素。拨打此号码N(C_ix,ix,t)

分区总数为:

sum by all {C_i1, C_i2, ..., C_ip} ( product N(C_ix,ix,t) ix={1,2,...,p} )

第一部分可以递归地完成,非常简单。第二是更复杂。使用M元素将多集n划分为k部分与使用M中的元素查找所有部分排序列表相同。部分订单列表的类型为:

a_1_1, a_1_2, ..., a_1_k, a_2_1, a_2_2, ..., a_2_k, ....

a_i_x <= a_i_y x < y(a_x_1, a_x_2, ..., a_x_k) lexicographic <= (a_y_1, a_y_2, ..., a_y_k) x < y的位置。使用这两个条件,可以递归地从N(C_ix,ix,t)创建所有分区。

对于某些情况,N(C_ix,ix,t)很容易计算。将|C_ix|定义为多集C_ix中不同元素的数量。

if t = 1 than 1
if |C_ix| = 1 than 1
if |C_ix| = 2 than (let m=minimal number of occurrences of elements in C_ix) floor(m/2) + 1
 in general if |C_ix| = 2 than partition of m in numbers <= t.