根据技能点划分球队

时间:2015-10-04 03:55:01

标签: java algorithm

我试图将具有不同技能点(从100-3000不等)的n名球员投入到球队中,以便每支球队的整体技能尽可能接近其他球队。

我首先按照技能点的降序对球员进行排序,并将顶级球员放入每支球队。现在技能点最低(迭代和计算总和)的球队将获得最佳球员。

例如。

  • A 600
  • B 550
  • C 400
  • D 250
  • E 220
  • F 200
  • G 150
  • H 140

对于2支球队,结果将是:

  • A队{600,250,220,150} = 1220
  • B队{550,400,200,140} = 1290

在另一种方法中,每支球队都有一名顶级球员和一名来自底部的球员。

  • A队{600,140,​​400,200} = 1340
  • B队{550,150,250,220} = 1170

因此,第一种方法更好,但对于不同的数据集,有时方法2是最佳的,有时方法1是最佳的。

有没有具体的算法来做到这一点?我试图阅读微软的TrueSkill算法,但这太复杂了。

2 个答案:

答案 0 :(得分:0)

看起来你想要为每个球员组合得分。我会在这里作弊并使用python:

from itertools import combinations

players = [600, 550, 400, 250, 220, 150, 140]

scores = {}

for i in range(1, int(len(players)/2)):
    for c in combinations(players, i):
        scores[c] = abs(sum(c) - sum([p for p in players if p not in c]))

print sorted(scores.items(), key=lambda x: x[1])[0]

打印:((600, 550), 10)

编辑:没有立即认识到这是一个难题。

答案 1 :(得分:0)

与评论中提到的mcdowella一样,如上所述,这个问题是 np-hard 。一种经典的方法是Integer Programming

以下实现使用Julia编程语言和JuMP 库作为建模工具。使用的整数/混合整数编程求解器是cbc,但也可以使用Gurobi的商业解算器(只需要2行代码更改!)。除了Gurobi,所有提到的工具都是开源的!

代码

using JuMP
using Cbc

# PARAMS
N_PLAYERS = 15
N_TEAMS = 3
LOWER_SKILL = 100
UPPER_SKILL = 3000

# RANDOM INSTANCE
SKILL = Int[]
for p = 1:N_PLAYERS
    push!(SKILL, rand(LOWER_SKILL:UPPER_SKILL))
end

# MODEL
m = Model(solver=CbcSolver())
bigM = sum(SKILL)^2  # more tight bound possible

# VARS
@defVar(m, x[1:N_PLAYERS, 1:N_TEAMS], Bin)  # player-team assignment vars
@defVar(m, 0 <= tsum_pos[1:N_TEAMS,1:N_TEAMS] <= bigM)  # abs-linearization: pos-part
@defVar(m, 0 <= tsum_neg[1:N_TEAMS,1:N_TEAMS] <= bigM)  # abs-linearization: neg-part

# CONSTRAINTS 
# each player is assigned to exactly one team 
for p = 1:N_PLAYERS
    @addConstraint(m, sum{x[p,t], t=1:N_TEAMS} == 1)
end

# temporary team sum expresions
team_sums = AffExpr[]
for t = 1:N_TEAMS
    @defExpr(y, SKILL[p] * sum{x[p,t], p=1:N_PLAYERS})
    push!(team_sums, y)
end

# errors <-> splitted abs-vars equality
for t1 = 1:N_TEAMS
    for t2 = 1:N_TEAMS
        if t1 != t2
            @addConstraint(m, (team_sums[t1] - team_sums[t2]) == (tsum_pos[t1,t2] - tsum_neg[t1,t2]))
        end 
    end 
end

# objective
@setObjective(m, Min, sum{tsum_pos[i,j] + tsum_neg[i,j], i=1:N_TEAMS, j=1:N_TEAMS})  # symmetry could be used

# SOLVE
tic()
status = solve(m)
toc()

# OUTPUT
println("Objective is: ", getObjectiveValue(m))
println("Solution: ")
println("Player skills: ", SKILL)
for p = 1:N_PLAYERS 
    for t = 1:N_TEAMS 
        if getValue(x[p,t]) > 0.5
            println("player ", p, " in team ", t)
        end 
    end
end

for t=1:N_TEAMS
    sum_ = 0
    for p=1:N_PLAYERS
        if getValue(x[p,t]) > 0.5
            sum_ += SKILL[p]
        end
    end
    println("team: ", t, " -> ", sum_)
end
println(sum(SKILL))

此建模使用一些linearization-trick来模拟绝对值,这与您在帖子中描述的基于L1规范的错误一样需要!

输出

elapsed time: 9.785739578 seconds
Objective is: 28.00000000000063 # REMARK: error is doubled because of symmetries which could be changed
Solution: 
Player skills: [2919,1859,1183,1128,495,1436,2215,2045,651,540,2924,2367,1176,334,1300]
player 1 in team 3
player 2 in team 1
player 3 in team 3
player 4 in team 1
player 5 in team 3
player 6 in team 2
player 7 in team 2
player 8 in team 1
player 9 in team 1
player 10 in team 1
player 11 in team 3
player 12 in team 2
player 13 in team 2
player 14 in team 2
player 15 in team 1
team: 1 -> 7523
team: 2 -> 7528
team: 3 -> 7521
22572