均匀分组的最佳方法是什么?

时间:2018-08-10 07:07:43

标签: javascript node.js algorithm

我正在尝试将总额平均分配给每个参与人员。

例如,我将用钱。

示例1

人员A有20美元

人员B的收入为40美元

人员C有$ 60

因此,即使要解决所有问题,也要给C人给A人20美元。

示例2

人员A有$ 36

人员B的收入为$ 15

人员C的收入为$ 9

要使这种情况甚至...

人A给人B $ 16,然后人B给人C $ 11,

或人A给予人B $ 5和人C $ 11

示例3

人员A有$ 53

人员B有$ 95

人员C有$ 24

人员D有$ 98

人E有$ 30

每个人需要60美元,我怎么能找出这种方式来进行最少的钱转移呢?

2 个答案:

答案 0 :(得分:1)

一个自然的方法是:最多的人给最少的人;然后重复。

具体来说:您可以计算每个人最后应拥有的V值(这只是每个人开始金额的平均值)。然后,如果拥有最多的人拥有M,拥有最少的人拥有L,则将拥有最多的人的min(M-V,V-L)给予拥有最少的人。此举之后,这两个人中至少有一个拥有正确的数量。现在重复,直到每个人都拥有所需的数量。

移动数量最多为人数。

我怀疑这可能是最佳选择,但您应该自己检查一下。您可以尝试应用https://cs.stackexchange.com/q/59964/755中的方法,以查看是否可以找到反例或证明其正确。

答案 1 :(得分:-1)

让这样的问题得不到解决似乎很可耻,因为这并不难解决-让我们使用您的最后一个示例:

  • 人员A有$ 53
  • 人员B有$ 95
  • 人员C有$ 24
  • 人员D有$ 98
  • 个人E有30美元

为简单起见,我将使用数组。

const totals = [53, 95, 24, 98, 30]
let sum, target, moveCounter = 0

if (totals.length) {
  sum = totals.reduce(function(a, b) { return (a + b) })
  target = sum / totals.length
}

目标只是总数的平均值,在示例中为60美元。

let needs = totals.map(function(a){ return target - a })
// needs = [7, -35, 36, -38, 30]

现在您知道每个needs是什么。

[0]需要获得$ 7, [1]需要下降$ 35, [2]需要获得$ 36, 等

该数组为零和:(7)+(-35)+(36)+(-38)+(30)= 0

您的问题是如何在尽可能少的移动中将每个元素都设为0。

您可以检查是否有任何元素相互抵消,例如[20,-20]。一种有效的检查方法是创建一个Set。

let unmatched = new Set([])
let matches = []
needs.forEach((need) => {
  if (0 !== need) {
    let value = Math.abs(need)
    if (unmatched.has(value)) {
      matches.concat([need, -need])
      unmatched.delete(value)
    } else {
      unmatched.add(value)
    }
  }
})

if (matches.length) {
  matches.forEach((matchElement) => {
    let index = needs.findIndex((needElement) => {
      return matchElement === needElement
    })
    if (index) {
      needs[index] = needs[index] - matchElement
      moveCounter = moveCounter + 1
  })
}

至此,所有轻松的步骤都完成了,您可以对数组进行排序,然后用蛮力将其余的数组进行排序。排序使事情变得容易一些。

var sortedNeeds = needs.sort(function(a, b){ return a < b ? -1 : 1 })
// [-38, -35, 7, 30, 36]

该数组为零和,因此您可以将其分为leftright两部分。可以将其视为平衡方程的左侧和右侧。

const zeroIndex = sortedNeeds.findIndex((value) => { return 0 === value })
// zeroIndex : -1

const positiveIndex = sortedNeeds.findIndex((value) => { return value > 0 })
// positiveIndex : 2

const leftLimit = (zeroIndex > -1) ? zeroIndex : positiveIndex
// leftLimit : 2

let left = sortedNeeds.slice(0, leftLimit)
// left: [-38, -35]

let right = sortedNeeds.slice(positiveIndex)
// right: [7, 30, 36]

下一块是魔术:

moveCounter = moveCounter + (left.length - 1) + right.length
// 4 = 0 + 1 + 3

计算的结果无关紧要,因为零和关系是关键。

  1. D给了B $ 38
  2. B给出了A $ 7
  3. B给C $ 36
  4. B给了E $ 30