这是subset sum problem,它是一个已知的 NP 完全问题,因此没有已知的有效(多项式)解决方案。
但是,如果您只处理相对较小的整数 - 有一个使用Dynamic Programming 的伪多项式时间解决方案。
这个想法是建立一个矩阵自下而上遵循下一个递归公式:
D(x,i) = false x<0
D(0,i) = true
D(x,0) = false x != 0
D(x,i) = D(x,i-1) OR D(x-arr[i],i-1)
这个想法是模仿一个详尽的搜索 - 在每一点你“猜测”是否选择了元素。
要获得实际的子集,您需要追溯您的矩阵。您从 D(SUM,n) 进行迭代,(假设值为 true) - 您执行以下操作(在矩阵已填满之后):
if D(x-arr[i-1],i-1) == true:
add arr[i] to the set
modify x <- x - arr[i-1]
modify i <- i-1
else // that means D(x,i-1) must be true
just modify i <- i-1
如果D(x-arr[i-1],i-1) == true 和D(x,i-1) == true 都随机选择要采取的行动方案,则每次都获得一个随机子集。
Python 代码(如果你不知道 python 把它读成伪代码,很容易理解)。
arr = [1,2,4,5]
n = len(arr)
SUM = 6
#pre processing:
D = [[True] * (n+1)]
for x in range(1,SUM+1):
D.append([False]*(n+1))
#DP solution to populate D:
for x in range(1,SUM+1):
for i in range(1,n+1):
D[x][i] = D[x][i-1]
if x >= arr[i-1]:
D[x][i] = D[x][i] or D[x-arr[i-1]][i-1]
print D
#get a random solution:
if D[SUM][n] == False:
print 'no solution'
else:
sol = []
x = SUM
i = n
while x != 0:
possibleVals = []
if D[x][i-1] == True:
possibleVals.append(x)
if x >= arr[i-1] and D[x-arr[i-1]][i-1] == True:
possibleVals.append(x-arr[i-1])
#by here possibleVals contains 1/2 solutions, depending on how many choices we have.
#chose randomly one of them
from random import randint
r = possibleVals[randint(0,len(possibleVals)-1)]
#if decided to add element:
if r != x:
sol.append(x-r)
#modify i and x accordingly
x = r
i = i-1
print sol
附言
上面给你随机选择,但不是排列的均匀分布。
要实现均匀分布,您需要计算可能选择的数量来构建每个数字。
公式为:
D(x,i) = 0 x<0
D(0,i) = 1
D(x,0) = 0 x != 0
D(x,i) = D(x,i-1) + D(x-arr[i],i-1)
并且在生成排列时,您执行相同的逻辑,但您决定添加元素i 概率为D(x-arr[i],i-1) / D(x,i)