Как автоматически создать расписание спортивной лиги - PullRequest
7 голосов
/ 24 июня 2009

Начну с того, что я понимаю, что эта тема сложная и что, вероятно, нет простого ответа. Если бы это было легко, тогда бы все это делали. Как говорится ...

Меня попросили создать приложение для управления спортивной лигой. Большинство концепций довольно просты для понимания, за исключением следующего: как составить расписание игры, где нет совпадений (команда играет 2 команды одновременно), где команда в дивизионе играет свои команды дважды, но играет команды из один раз в другие дивизии и следит за тем, чтобы в расписании не было дыр (каждая команда играет каждую неделю)

В данный момент процесс выполняется вручную с использованием электронной таблицы типа розеттского камня, которую я построил для этой цели, но она работает только для того количества команд, для которого оно было разработано. У меня есть вариации для 30 команд, 24 команд и 28 команд. Вместо того, чтобы постоянно пытаться перенастроить мою таблицу перевода, я хотел бы иметь возможность кодифицировать эту логику и вместо этого настроить этот процесс.

Мысли

Ответы [ 4 ]

12 голосов
/ 24 июня 2009

Существует довольно простая система, например, шахматные турниры под названием Round-Robin.

Идея состоит в том, чтобы разделить игроков на две стороны стола. Один из игроков обозначен как «хаб» (из-за отсутствия лучшего слова). Турнир начинается с того, что игроки играют друг против друга. После первого раунда все, кроме центра, передвигаются на один стул вперед, и переключается белый / черный (дома / в гостях в спорте). Весь круговой турнир заканчивается, когда игроки сидят на своих местах. Если вы хотите, чтобы все играли по два раза, просто сделайте то же самое снова.

Статья в Википедии с подробностями реализации.

В вашем особом случае, я бы попробовал сделать круговой прием один раз, включая все команды Затем вы делаете то же самое для каждого дивизиона один раз, и чтобы убедиться, что команды внутри дивизий играют друг с другом один раз дома и один раз в гостях, проверьте, как в первом раунде робин играет команды в этом раунде.

Обратной стороной этого является, конечно, то, что вы будете играть во всех матчах между дивизионами задолго до окончания турнира (поскольку последние n-1 матчей против команд внутри дивизии [n = количество команд в дивизионе ]). Если это проблема, вы можете просто поменять местами спички.

На самом деле я написал простой скрипт на Python, который делает это. Это не заняло много строк кода и дало довольно хорошие результаты. Это создаст расписание, в котором каждая команда играет каждую команду в своем дивизионе дважды и один раз против команд в других дивизиях. Нет никакой проверки, чтобы убедиться, что команды встречаются друг с другом дважды таким образом, что одна и та же команда находится дома. Но этот код должен дать хорошее представление о том, как создать свой собственный код планирования.

#!/usr/bin/python

div1 = ["Lions", "Tigers", "Jaguars", "Cougars"]
div2 = ["Whales", "Sharks", "Piranhas", "Alligators"]
div3 = ["Cubs", "Kittens", "Puppies", "Calfs"]

def create_schedule(list):
    """ Create a schedule for the teams in the list and return it"""
    s = []

    if len(list) % 2 == 1: list = list + ["BYE"]

    for i in range(len(list)-1):

        mid = int(len(list) / 2)
        l1 = list[:mid]
        l2 = list[mid:]
        l2.reverse()    

        # Switch sides after each round
        if(i % 2 == 1):
            s = s + [ zip(l1, l2) ]
        else:
            s = s + [ zip(l2, l1) ]

        list.insert(1, list.pop())

    return s


def main():
    for round in create_schedule(div1):
        for match in round:
            print match[0] + " - " + match[1]
    print
    for round in create_schedule(div2):
        for match in round:
            print match[0] + " - " + match[1]
    print
    for round in create_schedule(div3): 
        for match in round:
            print match[0] + " - " + match[1]
    print
    for round in create_schedule(div1+div2+div3): 
        for match in round:
            print match[0] + " - " + match[1]
        print

if __name__ == "__main__":
    main()
6 голосов
/ 24 июня 2009

Существует два алгоритма, один для нечетных команд, другой для четных команд, чтобы гарантировать, что будет проходить круговой прием.

Я сгенерирую вам графику, если смогу.

ODD # команд

Алгоритм - вращать все команды по часовой стрелке. Если бы у нас было 5 команд:

1 2 --> 3 1 --> 5 3 --> 4 5 --> 2 4
3 4     5 2     4 1     2 3     1 5
5       4       2       1       3   

К этому моменту мы завершили один раунд, в котором все разыгрывают друг друга ... следующий раунд снова будет ..

1 2
3 4
5

ДАЖЕ # команд

Когда у нас четное количество команд, вы выполняете одинаковое вращение, за исключением того, что удерживаете команду № 1 в фиксированном положении и вращаете все другие команды вокруг # 1 по часовой стрелке. Итак, если бы у нас было 4 команды ..

1 2 --> 1 3 --> 1 4 
3 4     4 2     2 3 

Это будет один полный раунд ... следующий матч будет ..

1 2 
3 4 

Программно, есть несколько способов, которыми вы могли бы подойти к этому. Может быть, приведенный выше код делает то же самое, LOL ..

2 голосов
/ 24 июня 2009

вот пример реализации

public interface ITeam
{
   bool PlaysOn(DateTime date);
   bool canPlay(ITeam); //returns true if a game between the teams is legal (played once/twice   
                        //already, same different divisions and other applicable rules
}

IEnumerable<ITeam> teams = null; //replace with initialization
IEnumerable<ITeams> reversed = team.Reverse();
IEnumerable<DateTIme> gameDays = null;//replace with initialization
var count = teams.Count();

foreach(var date in gameDays)
{
   for(int i = 0;i<count;i++)
   {
      var innerTeams = i % 2 == 0 ? teams : reversed;
      var team = (from t in innerTeams
                  where !t.PlaysOn(date)
                  select t).First();  
      var opp = (from t in teams
                 where !t.PlaysOn(date) && t.CanPlay(team)
                 select t).First();
      SetupGame(team,opp);
   }
} //lot of optimazation possible :)

Я только протестировал это на бумаге, но для моей установки это работает. Чередуя начало в начале списка команд и конец списка, я избегаю ситуаций, когда одна и та же команда должна будет играть одну и ту же команду снова и снова (или неоднократно играть в один и тот же день) в моем бумажном тесте представлял каждую возможную встречу в качестве другого противника, но это в основном то, что должен делать метод CanPlay. Надеюсь, это поможет, даже если это не полное решение

2 голосов
/ 24 июня 2009

Я бы просто закодировал эти ограничения в виде логической формулы и использовал бы некоторый SAT-решатель для получения решений (например, MiniSAT для C ++, SAT4J для Java, и вы могли бы даже написать собственный простой решатель). Входные данные для этих решателей стандартизированы DIMACS.

Смотрите также «Кодировка SAT для задачи социального игрока в гольф» и «Планировщик на основе SAT для расписаний турниров» для кодировок SAT схожих проблем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...