簡體   English   中英

有限制地將 n 分割為 k 個部分

[英]Partitions of n into k parts with restrictions

我需要一個算法,將數字n划分為k部分,並附加限制,即分區的每個元素必須介於ab之間。 理想情況下,滿足限制的所有可能分區的可能性應該相等。 如果分區具有不同順序的相同元素,則它們被認為是相同的。

例如,對於n=10 , k=3 , a=2 , b=4一個只有{4,4,2}{4,3,3}作為可能的結果。

是否有針對此類問題的標准算法? 可以假設至少有一個滿足限制的分區始終存在。

您可以將其實現為遞歸算法。 基本上,遞歸是這樣的:

  • 如果k == 1a <= n <= b ,則唯一的分區是[n] ,否則沒有
  • 否則,將ab的所有元素xnx , k-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。

這是一個使用條件概率的采樣算法。

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()

如果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]

盡管我不確定在這種情況下所有分區是否具有完全相同的概率。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM