微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将一组分为两组,使平均值的差异最小?

如何解决如何将一组分为两组,使平均值的差异最小?

据我所知,它与 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 是大小为 vi 子集的总和

Sv的总和,nv的长度

最小化的错误是

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。