组合 - 为游戏安排团队协会

时间:2017-04-23 17:41:20

标签: python combinatorics schedule constraint-programming

TL;博士

我想通过游戏的不同活动分发玩家
在比赛期间,球队可以进行固定数量的轮次 主要限制因素是:每个团队必须每个游戏,但每个团队 必须与对抗>所有团队

我曾在网上搜索过答案,但它们都与锦标赛有关,这与我没有相同的限制。

您现在可以跳过下两节。

简介

我是一个巨大游戏组织的一部分,我有责任在游戏过程中通过不同的活动分配玩家。当我意识到这不是一个容易解决的问题时,我试图编写一个Python脚本来完成这项工作。然后,我意识到我做错了,因为我的脚本以确定的方式工作。人们告诉我,我应该用解算器(Constraint Programming)来做。但是我在这个领域没有任何想法。它试图使用包python-constraint来解决我的问题,但我没有成功定义好的约束。

我已完成的搜索

我也在网上搜索过这个问题,但我发现的大多数答案都与锦标赛有关,这些竞赛没有和我一样的限制。事实上,锦标赛的主要目标是确保每支球队都能与其他球队对抗。在这里,主要的限制是确保每个团队都参与每项活动 我在math.stackexchange上找到了a similar thread但是约束条件并不相同,并且没有正确的答案。

输入

  • 36支队伍
  • 22项活动。 (有决斗)
  • 22轮

约束

  1. 团队不能对抗自己
  2. 每个团队必须参加每项活动
  3. 在游戏过程中,团队不能做超过 22项活动。 (因为只有22轮)
  4. 最小化团队与他们已经玩过的团队比赛。
  5. 由于共有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?  
    

    Python中的问题模型

    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为例。

    无论如何,任何帮助都会非常感激。先感谢您!

2 个答案:

答案 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行。