比赛配对algorhitm

时间:2017-11-20 13:21:36

标签: javascript algorithm math graph

我需要根据以下规则在锦标赛玩家中找到理想的配对:

  • 得分相等或类似的球员应该匹配
  • 两名球员在锦标赛中只能有一场相互比赛
  • 所有玩家必须在一轮中匹配

它基本上是一个简化的瑞士锦标赛系统。

我有以下排名:

[{
  "playerIndex": 0,
  "points": 0,
  "opponents": [3, 2, 4]
}, {
  "playerIndex": 1,
  "points": 3,
  "opponents": [4, 5, 2]
}, {
  "playerIndex": 2,
  "points": 3,
  "opponents": [5, 0, 1]
}, {
  "playerIndex": 3,
  "points": 4,
  "opponents": [0, 4, 5]
}, {
  "playerIndex": 4,
  "points": 6,
  "opponents": [1, 3, 0]
}, {
  "playerIndex": 5,
  "points": 2,
  "opponents": [2, 1, 3]
}]

阅读为:第一个数组项目是玩家编号(索引)0,已经玩过玩家编号(索引)3,2和4并获得0分,每个项目为6个玩家中的一个。

我需要为第四场比赛挑选三场比赛。遵循规则,没有两个玩家可以不止一次地进行相互比赛,我选择以下比赛:

[ [ 0, 1 ], [ 0, 5 ], [ 1, 3 ], [ 2, 3 ], [ 2, 4 ], [ 4, 5 ] ]

这六个可能的比赛中的每一个都有两个球员之间的分数如下:

[3, 2, 1, 1, 2, 4]

第四轮的理想配对让每位球员在一轮比赛中获得最低分差的比赛是:

[ [0, 5], [1, 3], [2, 4] ]

有没有办法实时找到这些理想的配对?不可能尝试所有可能的组合,因为锦标赛中可能有超过100人,计算将永远。

我被建议使用https://en.wikipedia.org/wiki/Edmonds%27_algorithmhttps://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm(两者都在JS中提供:https://www.npmjs.com/package/edmonds-blossomhttps://github.com/sfentress/edmunds-karp)。但我不确定如何阅读结果。

有人可以帮忙吗?

1 个答案:

答案 0 :(得分:0)

编辑:如果有太多可能的解决方案,匈牙利的alghoritm会失败。在我的情况下,在第一轮比赛之后,有很多球员得分相同。

Edmond Blossoms alghoritm的表现要好得多(通过NPM发现这个JS实现https://github.com/mattkrick/EdmondsBlossom)。

无法理解如何使用它。主要区别在于您需要使用成对进给它,并且对于应该优先选择的对,对之间的点差异更大。所以我对以前玩过的对使用零差异。

我的最终(希望)解决方案:

  var maxDiff = (roundIndex + 1) * this.config.pointsWin

  var possiblePairs = []
  availablePlayers.forEach(player => {
    availablePlayers.forEach(opponent => {
      if (
        player.playerIndex !== opponent.playerIndex // &&
        // player.opponents.indexOf(opponent.playerIndex) === -1
      ) {
        var match = [player.playerIndex, opponent.playerIndex]
        match.sort(function(a, b) {
          return a - b;
        })
        if (player.opponents.indexOf(opponent.playerIndex) === -1) {
          match.push(maxDiff - Math.abs(player.points - opponent.points))
        }
        else {
          match.push(0)
        }
        if (this.searchForArray(possiblePairs, match) === -1) {
          possiblePairs.push(match)
        }
      }
    })
  })

  var rawPairing = edmondsBlossom(possiblePairs)
  rawPairing.forEach((match, index) => {
    if (match !== -1 && match < index) {
      round.matches.push({
        home: match,
        home_score: '',
        away: index,
        away_score: '',
        referee: -1
      })
    }
  })

首先,我计算玩家之间的最大可能分数差异(期望有人可以获得零分,而其他人则全部获得)。然后在玩家之间创建所有可能的组合,并使用MAX POSSIBLE POINTS - PLAYERS POINTS DIFFERENCE或ZERO为之前匹配的玩家标记它们。

然后将数组提供给EdmondsBlossom函数,该函数返回整数数组...     [6,8,14,5,15,3,0,10,1,12,7,13,9,11,2,4] ...阅读如下:球员指数0应该与球员6,球员1对8,球员2对14,球员3对5 ...球员5对3(重复)配对。有时在输出中有-1值,只是跳过。

这是我的解决方案(已删除):

感谢@ VedPrakash的评论,我找到了解决我问题的匈牙利算法。幸运的是,NPM上还有一个JS实现https://github.com/addaleax/munkres-js

Munkers功能需要一个矩阵作为输入。在我的情况下,我的矩阵的交叉点上的玩家点差异(见下文)。已经相互比赛的对具有更高的价值,无法实现(在我的情况下为9)。

输入矩阵:

[ [ 9, 4, 9, 9, 9, 3 ],
  [ 4, 9, 9, 2, 9, 9 ],
  [ 9, 9, 9, 2, 4, 9 ],
  [ 9, 2, 2, 9, 9, 9 ],
  [ 9, 9, 4, 9, 9, 5 ],
  [ 3, 9, 9, 9, 5, 9 ] ]

输出:

[ [ 0, 5 ], [ 1, 3 ], [ 2, 4 ], [ 3, 1 ], [ 4, 2 ], [ 5, 0 ] ]

要注意的最后一件事是过滤Munkers输出(包含重复项 - 两对0vs1和1vs0),所以我只需通过比较第一和第二索引来过滤它们。

我的实施:

  var maxDiff = (roundIndex + 1) * this.config.pointsWin

  // prepare matrix
  var matrix = [];
  for (var i = 0; i < availablePlayers.length; i++) {
    matrix[i] = new Array(availablePlayers.length);
    matrix[i].fill(0)
  }

  // fill matrix with players points diff
  for (var y = 0; y < availablePlayers.length; y++) {
    var playerY = availablePlayers[y]

    for (var x = 0; x < availablePlayers.length; x++) {
      var playerX = availablePlayers[x]

      if (x === y) {
        matrix[x][y] = maxDiff
      }
      else if (playerY.opponents.indexOf(x) !== -1) {
        matrix[x][y] = maxDiff
        matrix[y][x] = maxDiff
      }
      else {
        var value = Math.abs(playerX.points - playerY.points)
        matrix[x][y] = value
        matrix[y][x] = value
      }
    }
  }

  // console.table(matrix)
  // console.table(computeMunkres(matrix))
  // return

  // make pairing
  var rawPairing = computeMunkres(matrix)
  rawPairing.forEach(pairing => {
    if (pairing[0] < pairing[1]) {
      round.matches.push({
        home: pairing[0],
        home_score: '',
        away: pairing[1],
        away_score: '',
        referee: -1
      })
    }
  })