Руководитель направления систем бизнес-аналитики BIA Technologies Станислав Воронин рассказывает, как математическая модель решает кому, когда и с кем играть в следующем сезоне, и размышляет о дивном новом будущем спорта.
Математическая оптимизация расписаний
«Ну кто додумался поставить в сетку выездную встречу с «Ювентусом» на этот вторник?! Ребята только отыграли сложнейший матч на домашнем поле!» — восклицаете вы в гневе. И напрасно: расписание составил не человек, а компьютер, и сделал он это на основе сложнейшей математической модели. Как? Сейчас расскажу.
Разумеется, речь пойдёт не о любительских соревнованиях между студенческими командами, а о составлении расписаний для высших спортивных лиг (Российская премьер-лига, итальянская Серия А, НБА, КХЛ и т.д.). Сетка для матчей такого уровня должна учитывать столько ограничений, что составить расписание вручную просто невозможно, поэтому лиги поручают это ответственное дело компьютеру.
Помимо специфических правил в разных типах турниров и видах спорта, существует ряд универсальных требований, единый для всех. Вот ключевые вводные:
1. Предпочтительно, чтобы матчи проводились по выходным или в будни по вечерам (так их увидит больше зрителей); самые зрелищные — в телевизионный прайм-тайм.
2. Нежелательно, чтобы в одно и то же время параллельно проходило несколько популярных матчей самых титулованных команд (болельщикам придётся переключаться между телеканалами).
3. Матчи следует равномерно распределять по сезону; между играми у команды должен быть перерыв не менее 48 часов (это время на отдых и подготовку спортсменов).
4. Домашние и выездные матчи должны чередоваться; нельзя проводить все домашние игры скопом в начале сезона (так как играть на родном стадионе традиционно легче, это создаст ложное ощущение превосходства).
5. Необходимо учитывать часовые пояса, джетлаги и время на переезд спортсменов из одного города/страны в другую.
6. Домашняя и выездная игры между двумя соперниками должны быть разнесены как можно дальше в расписании.
7. Необходимо учитывать уровни команд: нельзя, чтобы команда играла несколько матчей подряд с лидерами дивизиона — череда сложных игр вымотает спортсменов и поставит их в невыгодное положение перед соперниками.
8. Желательно не сводить явных фаворитов в первые две недели сезона (это сведёт на нет всю интригу).
9. Нужно учитывать занятость игроков в европейских чемпионатах: команды не любят играть с сильными соперниками прямо перед встречами еврокубков.
10. Сетка должна учитывать доступность стадионов под другие мероприятия.
Как видно по этому списку, составление расписания — это сложная эквилибристика, поиск идеального баланса между возможностями спортсменов, желаниями зрителей и максимальной финансовой выгодой для клубов и телеканалов. Правильно составленное расписание, которое учитывает все вышеуказанные условия, приносит лигам огромный доход от рекламы, трансляций и продажи билетов, поэтому создание соответствующей математической модели для планирования матчей стоит баснословных денег.
При изменении любого из этих правил или добавлении нового необходимо рассчитать, как изменится вся система в целом. Это сродни игре в шахматы: прежде чем передвинуть фигуру, нужно оценить, как изменится расклад на доске и просчитать последствия на несколько ходов вперёд. В итоге проще предоставить решение компьютеру, и математическая оптимизация сама сделает всю работу.
За календарь Российской футбольной премьер-лиги с 2019 года отвечает алгоритм, разработанный НИУ ВШЭ. Если вам интересны детали, в интервью изданию «Спорт-Экспресс» сотрудники лаборатории исследований спорта рассказали о своей методологии и российской специфике. Так, например, нежелательно проводить пять матчей в одном туре в Москве, поскольку это создаст сложности для МВД. Кроме того, в большинстве регионов страны зимой не поиграешь в футбол на открытом поле.
Искусственный интеллект vs. магия спорта
Сейчас всё больше решений в мире спорта принимается искусственным интеллектом на основе большого количества данных. И речь не только о расписании матчей. Камеры и датчики фиксируют каждое движение спортсмена, компьютер анализирует информацию и выдаёт рекомендации. Есть мнение, что в будущем вообще отпадёт необходимость в тренерском штабе: принимать решения о стартовом составе, сажать игроков на скамейку запасных, программировать режим тренировок, составлять рацион и давать тактические советы перед каждой игрой будет искусственный интеллект.
Во многих случаях компьютер действительно может заметить неочевидные взаимосвязи и предложить оптимальное решение. Вот интересный пример: защитник «Арсенала» Эктор Бельерин в начале сезона 2021/2022 перешёл на правах аренды в испанский клуб «Реал Бетис». Чтобы выбрать идеальный для себя вариант (по слухам, среди них были такие тяжеловесы как «Барселона», «Интер» и «ПСЖ») Бельерин обратился за помощью в консалтинговую фирму Analytics FC. Эксперты компании создали математическую модель специально под футболиста.
Алгоритм проанализировал множество факторов и подсказал, что текущая схема 4-3-2-1, которую предпочитает тренер «Реал Бетис», лучше всего сочетается со стилем игры Бельерина. Кроме того, данные показали, что команда нуждается в крайнем защитнике после трансфера Эмерсона, что точно не позволит Бельерину скучать на скамейке запасных. Наконец, игра в испанском клубе должна увеличить шансы футболиста на попадание в национальную сборную Испании к ближайшему Чемпионату мира. Эксперты говорят, что это первый случай в истории, когда игрок принял решение о трансфере при помощи аналитики.
Но есть и обратная сторона медали. Действительно ли мы готовы отдать на откуп ИИ принятие абсолютно всех решений? Дал бы он шанс, скажем, юному Лионелю Месси с его небольшим ростом и проблемным здоровьем, или сходу посчитал бы его бесперспективным форвардом? А хороший тренер — это просто «оптимизатор», который сравнивает статистику сутками напролёт, или харизматичный тактик, психолог и родитель в одном лице? Может ли условного Теда Лассо полностью заменить компьютер?
В 2018 году вышел документальный фильм «В поисках величия», который задавался примерно такими вопросами. Авторы поговорили с такими легендами спорта как Пеле, Уэйн Гретцки и Джерри Райс, и пришли к выводу, что искусственный интеллект лишил бы нас многих «неочевидных» звёзд. Один из примеров, приводимых в фильме, — чемпион мира по боксу Рокки Марчиано. Он был ниже большинства соперников и не обладал большим размахом рук. Но эти «недостатки» помогли ему найти свой собственный подход: он наклонялся ниже и бил в упор на короткой дистанции. Марчиано не проиграл ни одного поединка в своей карьере и считается одним из лучших боксёров в истории.
Тем не менее сейчас спорт движется в направлении всё большей автоматизации в принятии решений. Насколько это рационально — покажет время. Но мне бы всё-таки хотелось, чтобы часть решений осталась за людьми. В конце концов, мы любим спорт за человеческие моменты, красивую игру, командный дух, эпизоды невероятной удачи и «руку Бога». Если оставить выбор за компьютером, спорт перестанет быть искусством. А вы что думаете?
Этот пост написан пользователем Sports.ru, начать писать может каждый болельщик (сделать это можно здесь).
Что еще можно будет почерпнуть из деления команд на две
восьмерки? Два сорта, хорошие и недостаточно хорошие. Так вот, при жеребьевке
турнира, можно и даже нужно, в каждом туре иметь главный матч, в котором будут
играть две, из какого то числа лучших команд турнира. Чтобы не было в одном
туре двух матчей уровня «Динамо» — «Спартак» и «Локомотив» — ЦСКА.
Теперь займемся определением, количества этих, элитных
клубов. В одном круге пятнадцать туров, так вот, между двумя лучшими командами
в круге – один матч;
Между тремя – три матча;
Между четырьмя – шесть матчей;
Между пятью – десять матчей;
Между шестью – пятнадцать матчей.
Таким образом, если выделить шесть лучших клубов, то можно
их матчи между собой развести по турам. Остальные же матчи доверить жребию, ну
или кому-то более компетентному.
Что это даст?
Такое распределение позволит Лиге иметь высококачественный
продукт (футбольные матчи), регулярно в течениичемпионата, продукт который
можно будет показывать и зарубежному зрителю.
Клубы среднего уровня будут иметь стимул для попадания в
заветную шестерку, а клубы из нее – стимул остаться в ней. В свою очередь,
появление седьмого, восьмого и далее серьезных клубов в Лиге, вряд-ли пойдет ей
во вред.
Определение участников «большой шестерки».
Сейчас оптимально ее состав выглядит так: ЦСКА, «Зенит»,
«Спартак», «Рубин», «Динамо» и «Локомотив». Пожалуй единственным претендентом
на место в ней является «Анжи», но для этого должен быть и претендент на вылет
из шестерки. В случае, если необходимо выделить не только один матч из тура, а
например два, то кроме «Большой шестерки», можно выделить малую тройку. Матчи
представителей тройки, с первымми шестью командами, сформируют – вторые матчи
тура, это будут по шесть игр, с участием двух из трех клубов плюс игры третьей
команды, с тремя клубами из первой шестерки.
При этом, потребитель может считать первым матчем тура не
«Динамо» — «Локомотив», а «Зенит» — «Крылья Советов», но это уже право
потребителей, в то время как обязанность организатора равномерное распределение
матчей п отурам в зависимости от интереса зрителей.
Если бы это было на самом деле, тогда возникла бы
необходимость, поделить клубы на несколько групп:
Первая тройка, вторая тройка, третья тройка в формате два
плюс один.
Или так – «большая тройка», «большая шестерка», «большая
восьмерка», «большая девятка».
Перва тройка (шесть первых матчей + три вторых): ЦСКА,
«Зенит», «Спартак».
Вторая тройка (шесть + два): «Динамо», «Локомотив», «Рубин».
Третья тройка:
Два клуба (шесть вторых матчей тура): «Анжи» и «Кубань».
Один клуб (три вторых матча тура) – «Краснодар» или
«Ростов».
Попадание в ту или иную группу, можно определить по
популярности клубов, его турнирным успехам, участию в Еврокубках и рейтенгу
трансляций его матчей, да хоть голосованием членов Премьер-лиги. Это ведь не
для раскола ее участников, а для привлечения новых зрителей и поднятия интереса
к турниру.
Можно еще методом грубой силы. Случайно выбирая пары и проверяя, что они еще не играли.
Т.е. цикл по турам. Выбираем пару. Упорядочиваем ее по номеру команд. Смотрим (в хэше), если их нет там, то добавляем в хэш, в расписание и уменьшаем счетчик игр в туре (его начальное значение известно). Как счетчик обнулится переходим к новому туру.
Выбор пар наверное можно оптимизировать, чтобы поменьше вычислять случайные числа.
UPDATE
Другой вариант просто посчитать по турам. Я сделал на C. Судя по результатам работы алгоритм не оптимален для количества команд не равным степени 2-х. Но он работает
/*
avp 2012
Таблица чемпионата (один круг)
n команд (аргумент командной строки)
Составление расписания по турам
Количество туров вычисляется в программе
В stdout выводится список туров с парами команд
и квалратная таблица номеров туров для команд
*/
#include <stdio.h>
#include <stdlib.h>
struct cmd { // данные каждой команды
int ic, // номер команды (с нуля)
ng; // количество уже проведенных игр (init 0)
};
/*
Сортировать команды по возрастанию проведенных игр
(у кого меньше в начало списка)
n - размер списка команд
*/
void
sort (struct cmd a[], // список команд
int n)
{
int i, j;
for (i = 1; i < n; i++) {
struct cmd tt = a[i];
for (j = i; j > 0; j--) {
if (a[j].ng >= a[j-1].ng)
break;
a[j] = a[j-1];
}
if (j < i)
a[j] = tt;
}
}
main (int ac, char *av[])
{
int n = 10; // количество команд в чемпионате
if (ac > 1)
if ((n = atoi(av[1])) < 2)
n = 10;
int game[n][n]; // в каком туре играют j,k (туры с 1, номера команд с 0)
int tot = (n*(n-1))/2; // общее количество игр в круге
int cal[tot][n]; // с кем в туре i играет команда j (все индексы с 0)
int i,j,k, jc, kc, // разные индексы
ng, // количество уже проведенных игр в круге
nt; // количество туров (вычисляетя в ходе составления расписания)
// инициализация таблицы игр в круге
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
game[i][j] = 0; // здесь будет в каком туре играют команды i,j
}
game[i][i] = -1;
}
// инициализация таблицы по турам (количество туров с большим запасом)
for (i = 0; i < tot; i++)
for (j = 0; j < n; j++)
cal[i][j] = 0; /* здесь будет с какой командой (номер с 1)
в туре i (индекс с 0) играет команда j (индекс с 0)
*/
struct cmd a[n]; // список команд
for (i = 0; i < n; i++) {
a[i].ic = i;
a[i].ng = 0;
}
// собственно вычисления
for (i = ng = 0; ng < tot; i++) { // увеличиваем туры (i) пока не сыграли
// все матчи круга (ng)
sort(a,n); // это для равномерности (?) по турам
for (j = 0; j < n; j++) { // перебираем команды из списка
jc = a[j].ic;
if (cal[i][jc])
continue; // уже играла в этом туре
for (k = 0; k < n; k++) { // перебираем команды из списка
kc = a[k].ic;
if (kc == jc)
continue; // сама с собой
if (cal[i][kc] || game[jc][kc])
continue; // kc уже играла в туре или играла раньше с jc
/*
kc и jc еще не играли. Пусть сыграют в этом туре
увеличим количество проведенных игр и заполним таблицы
*/
ng++;
a[jc].ng++;
a[kc].ng++;
cal[i][kc] = jc+1;
cal[i][jc] = kc+1;
game[jc][kc] = i+1;
game[kc][jc] = i+1;
break;
}
}
}
nt = i; // это сколько туров получилось.
// печать игр по турам
printf ("ntours %dn",nt);
for (i = 0; i < nt; i++) {
printf ("tour %dn",i+1);
int t[n];
for (j = 0; j < n; j++)
t[j] = 0;
for (j = 0; j < n; j++) {
if (k = cal[i][j]) {
if (!t[k-1]) {
// printf ("%2d:%2d ",j+1,k);
printf (" %c : %c ",'A'+j,'A'+k-1);
t[j] = t[k-1] = 1;
}
}
}
printf ("n");
}
// печать квадратной таблицы туров для команд
printf ("ntours tablen");
printf (" ");
for (i = 0; i < n; i++)
// printf ("%2d ",i+1);
printf (" %c ",'A'+i);
printf ("n");
for (i = 0; i < n; i++) {
// printf (" %2d ",i+1);
printf (" %c ",'A'+i);
for (j = 0; j < n; j++) {
printf ("%2d ",game[i][j]);
}
printf ("n");
}
printf ("n");
}
Уважаемый @namak, Вы уж извините меня за программу на Си. Я понимаю, что Вы спрашивали PHP с SQL, но Си я владею значительно лучше, а алгоритм надеюсь будет понятен.
Алгоритм (операторы) собственно находится между комментариями
// собственно вычисления
и
// печать игр по турам
UPDATE 2
Функция перемешивающая команды (вместо sort())
void
shuffle (struct cmd a[], int n)
{
int i, j;
static int first = 1;
if (first) {
first = 0;
srand(time(0));
}
for (i = 0; i < n; i++) {
struct cmd tt;
j = rand()%n;
tt = a[j];
a[j] = a[i];
a[i] = tt;
}
}
Алгоритм составления расписания матчей
27.03.2015, 12:50. Показов 2942. Ответов 5
Добрый день!
Озадачился созданием расписания матчей (в частности по футболу) и вот что у меня получилось.
Берем допустим 4 команды и делаем один круг (ибо второй круг это тоже самое, только меняем местами команды)
Попытаюсь описать словами специально не используя какой-то определенный язык программирования
Есть 4 команды teams = [a, b, c, d]
Вычисляем кол-во туров и делаем массив (кол-во команд — 1) tours = [[1], [2], [3]]
Далее система такая, я вырезаю первую команду и создаю через цикл пары с оставшимися и заношу в туры если этих команд нет еще
headteam = a
a — b заходим в 1 тур, ни а ни b там нет, записываем tours = [[1, a, b], [2], [3]] все успешно, делаем break
a — c заходим в 1 тур, там есть a, идем дальше, во втором туре нет ни а ни с, записываем и break, у нас получается tours = [[1, a, b], [2, a, c], [3]]
a — d в первом и втором туре есть а, значит идем в третий и записываем, итого tours = [[1, a, b], [2, a, c], [3, a, d]]
Отлично, перезапускаем цикл и вырезаем первую команду, теперь это b и создаем пары с оставшимися командами
b — c, в первом туре есть b, во втором туре есть c, в третьем туре нет ни b, ни с, записываем
[[1, a, b], [2, a, c], [3, a, d, b, c]]
b — d как видим подходит для второго тура [[1, a, b], [2, a, c, b, d], [3, a, d, b, c]]
Перезапускаем цикл, вырезаем первую команду, это c
с — d проходит в первый тур [[1, a, b, c, d], [2, a, c, b, d], [3, a, d, b, c]]
Далее все у нас завершается, так как по циклу, который я описал в самом начале команд у нас становится меньше 1, а точнее 0 и программа завершает работу, все красиво и верно.
Надеюсь ход мыслей моих понятен, а так же сразу скажу, что не претендую на гениальность, думал как реализовать и придумал такой способ, но он работает только если кол-во команд 4, 8, 16, 32, 64 и т.д, если же команд 10 или 20 или 14 и т.д, пары перекрываются и расписание составляется неверно, найти способ как составлять расписание для произвольного количества команд пока не придумал, может кто-то сможет подсказать, хотя бы на словах?
0
Управление чемпионатом
Расписание — Результаты — Ранжирование
Создайте расписание чемпионата одним кликом.
Создайте учётную запись, чтобы управлять своим чемпионатом, записывать результаты, обновлять ранжирование…
Разделяйте варианты запятыми или разрывами строк. Также можно копировать / вставлять из таблицы.
Игры на своём и чужом поле
Создать календарь с двойными встречами (игры на своём и чужом поле)
Недостаточно участников для начала жеребьёвки!Слишком много участников, чтобы начать жеребьёвку!Проверьте количество победителей!