问题描述
据我所知,它与 partition problem 相关。
但是我想问一个稍微不同的问题,我不在乎总和,而是在乎平均值。在这种情况下,它需要同时优化 2 个约束(总和和项目数)。这似乎是一个更难的问题,我在网上找不到任何解决方案。
是否有针对此变体的任何解决方案?或者它与分区问题有什么关系?
示例:
input X = [1,1,6]
output based on sum: A = [1,1],B=[6]
output based on average: A = [1],B=[1,6]
解决方法
在某些输入上,针对通常的分区问题修改动态程序将提高速度。我们必须通过它的计数和总和来对每个部分解决方案进行分类,而不仅仅是总和,这会减慢速度。下面的 Python 3(请注意,字典的使用会隐式折叠功能相同的部分解决方案):
def children(ab,x):
a,b = ab
yield a + [x],b
yield a,b + [x]
def proper(ab):
a,b = ab
return a and b
def avg(lst):
return sum(lst) / len(lst)
def abs_diff_avg(ab):
a,b = ab
return abs(avg(a) - avg(b))
def min_abs_diff_avg(lst):
solutions = {(0,0): ([],[])}
for x in lst:
solutions = {
(sum(a),len(a)): (a,b)
for ab in solutions.values()
for (a,b) in children(ab,x)
}
return min(filter(proper,solutions.values()),key=abs_diff_avg)
print(min_abs_diff_avg([1,1,6]))
,
让 S_i
是大小为 v
的 i
子集的总和
令S
为v
的总和,n
为v
的长度
最小化的错误是
err_i = |avg(S_i) - avg(S-S_i)|
err_i = |S_i/i - (S-S_i)/(n-i)|
err_i = |(nS_i - iS)/(i(n-i))|
下面的算法:
for all tuple sizes (1,...,n/2) as i
- for all tuples of size i-1 as t_{i-1}
- generate all possible tuple of size i from t_{i-1} by adjoining one elem from v
- track best tuple in regard of err_i
我发现的唯一切口是:
对于大小为 i
的两个元组具有相同的总和,保留最后一个元素的索引最小的元组
例如给定的元组 A
,B
(其中 X
是从 v
中提取的一些元素)
A: [X,....,X....]
B: [.,X,.....,X..]
保留 A
因为其最右边的元素具有最小索引
(想法是在大小 3 时,A
将提供与 B
相同的候选人以及更多)
function generateTuples (v,tuples) {
const nextTuples = new Map()
for (const [,t] of tuples) {
for (let l = t.l + 1; l < v.length; ++l) {
const s = t.s + v[l]
if (!nextTuples.has(s) || nextTuples.get(s).l > l) {
const nextTuple = { v: t.v.concat(l),s,l }
nextTuples.set(s,nextTuple)
}
}
}
return nextTuples
}
function processV (v) {
const fErr = (() => {
const n = v.length
const S = v.reduce((s,x) => s + x,0)
return ({ s: S_i,v }) => {
const i = v.length
return Math.abs((n * S_i - i * S) / (i * (n - i)))
}
})()
let tuples = new Map([[0,{ v: [],s: 0,l: -1 }]])
let best = null
let err = 9e3
for (let i = 0; i < Math.ceil(v.length / 2); ++i) {
const nextTuples = generateTuples(v,tuples)
for (const [,t] of nextTuples) {
if (fErr(t) <= err) {
best = t
err = fErr(t)
}
}
tuples = nextTuples
}
const s1Indices = new Set(best.v)
return {
sol: v.reduce(([v1,v2],x,i) => {
(s1Indices.has(i) ? v1 : v2).push(x)
return [v1,v2]
},[[],[]]),err
}
}
console.log('best: ',processV([1,6]))
console.log('best: ',2,3,4,5]))
console.log('best: ',5,7,8]))