[英]Partitions of n into k parts with restrictions
我需要一個算法,將數字n
划分為k
部分,並附加限制,即分區的每個元素必須介於a
和b
之間。 理想情況下,滿足限制的所有可能分區的可能性應該相等。 如果分區具有不同順序的相同元素,則它們被認為是相同的。
例如,對於n=10
, k=3
, a=2
, b=4
一個只有{4,4,2}
和{4,3,3}
作為可能的結果。
是否有針對此類問題的標准算法? 可以假設至少有一個滿足限制的分區始終存在。
您可以將其實現為遞歸算法。 基本上,遞歸是這樣的:
k == 1
且a <= n <= b
,則唯一的分區是[n]
,否則沒有a
到b
的所有元素x
與nx
, 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()
如果k
和b - 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.