n分为k部分有限制

时间:2017-02-01 14:41:11

标签: algorithm partitioning

我需要一种算法,将n数字的分区生成k部分,并增加限制,即分区的每个元素必须介于ab之间。理想情况下,满足限制的所有可能分区应该同样可能。如果分区具有不同顺序的相同元素,则认为分区相同。

例如,n=10k=3a=2b=4只有{4,4,2}{4,3,3}作为可能的结果。

是否存在针对此类问题的标准算法?可以假设至少有一个满足限制的分区总是存在。

3 个答案:

答案 0 :(得分:2)

您可以将其实现为递归算法。基本上,重现是这样的:

  • 如果k == 1a <= n <= b,那么唯一的分区是[n],否则没有
  • 否则,将xa中的所有元素bn-xk-1
  • 的所有分区合并
  • 为了防止重复,也将下限a替换为x

这里有一些Python(又名可执行的伪代码):

def partitions(n, k, a, b):
    if k == 1 and a <= n <= b:
        yield [n]
    elif n > 0 and k > 0:
        for x in range(a, b+1):
            for p in partitions(n-x, k-1, x, b):
                yield [x] + p

print(list(partitions(10, 3, 2, 4)))
# [[2, 4, 4], [3, 3, 4]]

这可以通过分别检查(k-1)*a(k-1)*b的剩余元素的下限和上限,并相应地限制x的范围来进一步改进:

        min_x = max(a, n - (k-1) * b)
        max_x = min(b, n - (k-1) * a)
        for x in range(min_x, max_x+1):

对于具有3,157个解决方案的partitions(110, 12, 3, 12),这会将递归调用的数量从638,679减少到24,135。

答案 1 :(得分:2)

这是一个使用条件概率的采样算法。

import collections
import random

countmemo = {}


def count(n, k, a, b):
    assert n >= 0
    assert k >= 0
    assert a >= 0
    assert b >= 0
    if k == 0:
        return 1 if n == 0 else 0
    key = (n, k, a, b)
    if key not in countmemo:
        countmemo[key] = sum(
            count(n - c, k - 1, a, c) for c in range(a, min(n, b) + 1))
    return countmemo[key]


def sample(n, k, a, b):
    partition = []
    x = random.randrange(count(n, k, a, b))
    while k > 0:
        for c in range(a, min(n, b) + 1):
            y = count(n - c, k - 1, a, c)
            if x < y:
                partition.append(c)
                n -= c
                k -= 1
                b = c
                break
            x -= y
        else:
            assert False
    return partition


def test():
    print(collections.Counter(
        tuple(sample(20, 6, 2, 5)) for i in range(10000)))


if __name__ == '__main__':
    test()

答案 2 :(得分:1)

如果kb - a不是太大,您可以尝试随机深度优先搜索:

import random

def restricted_partition_rec(n, k, min, max):
    if k <= 0 or n < min:
        return []
    ps = list(range(min, max + 1))
    random.shuffle(ps)
    for p in ps:
        if p > n:
            continue
        elif p < n:
            subp = restricted_partition(n - p, k - 1, min, max)
            if subp:
                return [p] + subp
        elif k == 1:
            return [p]
    return []

def restricted_partition(n, k, min, max):
    return sorted(restricted_partition_rec(n, k, min, max), reverse=True)

print(restricted_partition(10, 3, 2, 4))
>>>
[4, 4, 2]

虽然我不确定在这种情况下所有分区是否具有完全相同的概率。