我试图将具有不同技能点(从100-3000不等)的n名球员投入到球队中,以便每支球队的整体技能尽可能接近其他球队。
我首先按照技能点的降序对球员进行排序,并将顶级球员放入每支球队。现在技能点最低(迭代和计算总和)的球队将获得最佳球员。
例如。
对于2支球队,结果将是:
在另一种方法中,每支球队都有一名顶级球员和一名来自底部的球员。
因此,第一种方法更好,但对于不同的数据集,有时方法2是最佳的,有时方法1是最佳的。
有没有具体的算法来做到这一点?我试图阅读微软的TrueSkill算法,但这太复杂了。
答案 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