algorithm – 具有给定约束的分区数

考虑一组13名丹麦人,11名日本人和8名波兰人.众所周知,将这组人分组的不同方式的数量是13 11 8 = 32:贝尔数(设置分区的数量).但是,我们被要求在给定约束下找到可能的集合分区的数量.问题如下:

如果没有由至少两个仅包含单一国籍的人组成的小组,则称集合分区是好的.这个套装有多少个好的分区? (一组可能只包括一个人.)

蛮力方法需要经过大约10 ^ 26个分区并检查哪些分区是好的.这似乎是不可行的,特别是如果团体较大或一个人介绍其他国籍.有一种聪明的方式吗?

编辑:作为旁注.一个非常好的解决方案可能没有希望.一个备受尊敬的组合学专家answered一个相关的问题,我认为,基本上说相关问题,因此这个问题也很难准确解决.

最佳答案 这是使用动态编程的解决方案.

它从空集开始,然后一次添加一个元素并计算所有有效分区.

状态空间很大,但请注意,为了能够计算下一步,我们只需要了解以下内容:

>对于每个国籍,它包含的数量仅包含该国籍的一个成员. (例如:{a})
>它包含混合元素的集合数量. (例如:{a,b,c})

对于这些配置中的每一个,我只存储总计数.例:

[0, 1, 2, 2] -> 3
{a}{b}{c}{mixed} 
   e.g.: 3 partitions that look like: {b}, {c}, {c}, {a,c}, {b,c}

这是python中的代码:

import collections
from operator import mul
from fractions import Fraction

def nCk(n,k):
  return int( reduce(mul, (Fraction(n-i, i+1) for i in range(k)), 1) )

def good_partitions(l):
    n = len(l)
    i = 0
    prev = collections.defaultdict(int)
    while l:
        #any more from this kind?
        if l[0] == 0:
            l.pop(0)
            i += 1
            continue
        l[0] -= 1
        curr = collections.defaultdict(int)

        for solution,total in prev.iteritems():
            for idx,item in enumerate(solution):
                my_solution = list(solution)
                if idx == i:
                    # add element as a new set
                    my_solution[i] += 1
                    curr[tuple(my_solution)] += total
                elif my_solution[idx]:
                    if idx != n:
                        # add to a set consisting of one element
                        # or merge into multiple sets that consist of one element
                        cnt = my_solution[idx]
                        c = cnt
                        while c > 0:
                            my_solution = list(solution)
                            my_solution[n] += 1
                            my_solution[idx] -= c
                            curr[tuple(my_solution)] += total * nCk(cnt, c)
                            c -= 1
                    else:
                        # add to a mixed set
                        cnt = my_solution[idx]
                        curr[tuple(my_solution)] += total * cnt

        if not prev:
            # one set with one element
            lone = [0] * (n+1)
            lone[i] = 1
            curr[tuple(lone)] = 1

        prev = curr
    return sum(prev.values())

print good_partitions([1, 1, 1, 1])      # 15
print good_partitions([1, 1, 1, 1, 1])   # 52
print good_partitions([2, 1])            # 4
print good_partitions([13, 11, 8])       # 29811734589499214658370837

它为测试用例生成正确的值.我还针对蛮力解决方案(对于小值)测试它,并且它产生相同的结果.

点赞