我想通过游戏的不同活动分发玩家。
在比赛期间,球队可以进行固定数量的轮次
主要限制因素是:每个团队必须每个游戏,但每个团队 不必须与对抗>所有团队。
我曾在网上搜索过答案,但它们都与锦标赛有关,这与我没有相同的限制。
您现在可以跳过下两节。
我是一个巨大游戏组织的一部分,我有责任在游戏过程中通过不同的活动分配玩家。当我意识到这不是一个容易解决的问题时,我试图编写一个Python脚本来完成这项工作。然后,我意识到我做错了,因为我的脚本以确定的方式工作。人们告诉我,我应该用解算器(Constraint Programming)来做。但是我在这个领域没有任何想法。它试图使用包python-constraint来解决我的问题,但我没有成功定义好的约束。
我也在网上搜索过这个问题,但我发现的大多数答案都与锦标赛有关,这些竞赛没有和我一样的限制。事实上,锦标赛的主要目标是确保每支球队都能与其他球队对抗。在这里,主要的限制是确保每个团队都参与每项活动 我在math.stackexchange上找到了a similar thread但是约束条件并不相同,并且没有正确的答案。
由于共有36支球队进行了22场比赛,因此每轮比赛都会有空 由于有22项活动和22轮,一个团队不能多次开展活动。
| Round 1 | Round 2 | Round n
------ | -------------- | -------------- | --------------
Game 1 | Team1 vs Team2 | Team3 vs Team5 | Team? vs Team?
Game 2 | Team3 vs Team4 | Team1 vs Team6 | Team? vs Team?
Game n | Team? vs Team? | Team? vs Team? | Team? vs Team?
from constraint import *
problem = Problem()
problem.addVariable("Activity", [i for i in range(1, 22+1)])
problem.addVariables(["Team1", "Team2"], [i for i in range(1, 36+1)])
problem.addVariable("Round", [i for i in range(1, 22+1)])
problem.addConstraint(lambda a, b: a != b, ("Team1", "Team2"))
???
我正在寻找能够:
的人它可以是任何语言或任何工具。我以Python为例。
无论如何,任何帮助都会非常感激。先感谢您!
答案 0 :(得分:0)
你的问题根本不是微不足道的,但我会尝试接近它。:)我会尝试在约束可满足性方面定义问题。我将依赖Boolean satisfiability并使用Tseitin transformation对您的任务进行编码。
首先,让我们介绍一组布尔变量,这些变量对应于团队在一轮中玩的游戏:
game0_team0_round0,g0_t0_r1,...,gk_tm_rn
我们将约束编码为布尔公式。如果存在满足所有给定约束的计划,则返回 True 。上面定义的变量的一组值表示一个时间表:如果变量等于 True ,则相应的团队在相应的回合中玩游戏。我们介绍了很多约束。我们同意,如果约束公式返回 True ,则违反此约束。
第一个限制条件禁止团队在一轮中玩几个游戏:
for round in rounds:
for team in teams:
games = get_games_from_team_and_round (round, team)
constr = greater_then_equal (games, 2)
add_must_be_false_constr (constr)
'greater_then_equal'构建伪布尔约束,确保团队/回合中只有一个游戏 True 。您可以阅读here如何实现它。 'add_must_be_false_constr'确保不会违反约束。
接下来,让我们来介绍决斗约束:在一轮中,必须有两支球队参加比赛。与此同时,他们必须玩同样的游戏:
for round in rounds:
for game in games:
teams = get_teams_from_round (round)
constr = greater_then_equal (teams, 3)
add_must_be_false_constr (constr)
最后,每支球队都必须参加每场比赛:
for round in rounds:
for team in teams:
games = get_games_from_team_and_round (round, team)
constr = boolean_or (games)
add_must_be_false_constr (constr)
我们确保每轮至少玩一场比赛。
您可以将结果公式放入解算器(例如Glucose),它会找到满足所有给定约束的随机计划。
现在棘手的部分 - 最小化。我不确定如何正确地做到这一点,但是,例如,如果一对团队在给定轮次中玩游戏,你可以引入一个 True 的变量。接下来,您将引入'greater_then_equal'约束来限制这些变量的 True 值。最后,您可以使用assumptions以最少的重复次数找到最佳时间表。
请注意,使用这种方法可能需要花费大量时间来解决任务:以正确的方式实现所有内容并非易事,因此这就是为什么我不提供代码,而只是给你一个非常通用的描述如何做到这一点。解决问题可能需要几个小时,但这取决于问题本身和约束。其他问题 - 它可能消耗大量内存。我有类似的东西解决非常大(10 ^ 4变量,10 ^ 6约束)问题的实践经验。祝你好运!
答案 1 :(得分:0)
以下是MiniZinc中的解决方案。以下是基本想法:
1)处理哪些球队将参加比赛的决策变量是活动大小数量的3维数组x
x轮次数x 2并包含在特定活动中玩的两支球队。由于可能存在“空”游戏,即当没有团队玩域时的一轮/活动是0 ......多个团队,其中“团队”0表示空游戏。我们还添加了一个约束条件:如果一个游戏的一个团队是团队0,那么另一个团队也必须是团队0:(x[a,r,1] = 0 <-> x[a,r,2] = 0)
其中<->
表示暗示。
2)对于所有团队必须参与所有活动和所有轮次的约束,只需要两个(全局)约束:global_cardinality
计算团队参与活动的次数和轮次分别。
3)问题的目标是尽量减少团队之间“双重”游戏的数量。为此,使用二维矩阵y
:它包含两个团队相互比赛的匹配数。模型目标z
计算双重播放的次数,然后我们将其最小化。然而,似乎根本不需要任何双重播放,因此y
的域只是0..1,即两队之间的比赛一次或根本不相遇。由于域为1,因此实际上约束模型不允许任何双重播放。注意:我不知道这是否适用于团队数量x活动x轮次的所有不同值,但它适用于我测试的不同实例,包括问题中的32 x 22 x 22实例。
include "globals.mzn";
int: num_teams = 36;
int: num_activities = 22;
int: num_rounds = 22;
% decision variables
% x[a,r,1..2] = t1..t2: team t1 and team t2 plays in activity a, round r
% 0 mean an empty game
array[1..num_activities, 1..num_rounds, 1..2] of var 0..num_teams: x;
% number of plays against the other teams
array[1..num_teams, 1..num_teams] of var 0..1: y;
% number of plays where a team meet another team more than once
var 0..(num_teams*(num_teams-1)) div 2: z; % = sum([y[t1,t2] > 1 | t1,t2 in 1..num_teams where t1 < t2]);
% minimize the number of "double" matches between the teams
solve :: int_search(array1d(x), most_constrained, indomain_min, complete) minimize z;
constraint
% the number of times the teams play against each other
forall(t1 in 1..num_teams) (
y[t1,t1] = 0 % a team do not play against itself
/\
forall(t2 in 1..num_teams where t1 < t2) (
y[t1,t2] = sum([x[a,r,1] = t1 /\ x[a,r,2] = t2 | a in 1..num_activities, r in 1..num_rounds])
/\
y[t2,t1] = y[t1,t2] % symmetric matrix
)
)
/\
z = sum([y[t1,t2] > 1 | t1,t2 in 1..num_teams where t1 < t2])
% /\ z = 0
;
constraint
% a team must participate in all activities
forall(a in 1..num_activities) (
global_cardinality([x[a,r,i] | r in 1..num_rounds, i in 1..2],1..num_teams,[1 | r in 1..num_teams]) :: domain
)
/\ % a team must participate in all rounds
forall(r in 1..num_rounds) (
global_cardinality([x[a,r,i] | a in 1..num_activities, i in 1..2],1..num_teams,[1 | r in 1..num_teams]) :: domain
)
/\ % null games "team" 0 vs "team" 0
forall(a in 1..num_activities,r in 1..num_rounds) (
(x[a,r,1] = 0 <-> x[a,r,2] = 0)
)
;
output
[ "z:\(z)\n" ] ++
[
if r = 1 then "\n" else "" endif ++
"\(a)-\(r): \(x[a,r,1]) vs \(x[a,r,2])\n"
| a in 1..num_activities, r in 1..num_rounds
]
++
[ % the play matrix
if t2 = 1 then "\n" else "" endif ++
show_int(2,y[t1,t2]) ++ " "
| t1,t2 in 1..num_teams
]
++ [ "\n\nz:\(z)\n" ];
Gecode解算器在8s内解决了问题(创建FlatZinc模型的时间大约需要一分钟)。对于较小的问题18队x 11活动x 11轮,需要4s(包括展平到FlatZinc)。
模型也在这里:http://hakank.org/minizinc/schedule_team_associations_for_a_game.mzn。它包括我测试过的一些注释内容,例如:不同的对称性破坏约束,但这个相当缩小的模型似乎是最快的。解决方案中活动和回合的团队顺序似乎是随机的。如果这是一个问题,一些对称性破坏约束将有所帮助(但可能需要更长的时间来解决)。
解决方案太长而无法在此处显示,但会保存在http://hakank.org/minizinc/schedule_team_associations_for_a_game.txt
中此外,目前该模型是一个最小化问题,只会显示一个解决方案。如果需要更多/更多解决方案,只需要进行两项更改:
1)将solve ... minimize z
更改为solve satisfy
。
2)取消注释第z = 0
行。