Алгоритм турнира Round Robin в C#
У меня есть некоторые проблемы, чтобы достичь этого маленького раунд Робин. То, что я пытаюсь сделать, это создать предварительный календарь игр
тогда я хочу вывести;
день 1: Команда 1 против команды 2; Команда 3 команда против 4; Команда 5ВС 6;
день 2 Команда 1 против команды 4; Команда 6 против команды 3; Команда 2 против команды 5;
до конца чемпионата;
вот код, который у меня есть до сих пор, но у меня возникли проблемы с первой командой а остальное время вращается...:
static void Main(string[] args)
{
string[] ListTeam = new string[] {"Equipe1", "Equipe2", "Equipe3", "Equipe4", "Equipe5", "Equipe6"};
IList<Match> ListMatch = new List<Match>();
it NumberOfDays = (ListTeam.Count()-1);
int y = 2;
for (int i = 1; i <= NumberOfDays; i++)
{
Console.WriteLine("nDay {0} : n",i);
Console.WriteLine(ListTeam[0].ToString() + " VS " + ListTeam[i].ToString());
for (y =ListTeam.Count(); y>0 ; y--)
{
Console.WriteLine(ListTeam[y].ToString() + " VS " + ListTeam[y+1].ToString());
y++;
}
}
}
EDIT: я нашел пример кода на Java, но Я не могу перевести его...
6 ответов
это должно быть достаточно легко сделать с помощью модульной арифметики:
обновление 2: (как и обещал правильный алгоритм)
public void ListMatches(List<string> ListTeam)
{
if (ListTeam.Count % 2 != 0)
{
ListTeam.Add("Bye");
}
int numDays = (numTeams - 1);
int halfSize = numTeams / 2;
List<string> teams = new List<string>();
teams.AddRange(ListTeam.Skip(halfSize).Take(halfSize));
teams.AddRange(ListTeam.Skip(1).Take(halfSize -1).ToArray().Reverse());
int teamsSize = teams.Count;
for (int day = 0; day < numDays; day++)
{
Console.WriteLine("Day {0}", (day + 1));
int teamIdx = day % teamsSize;
Console.WriteLine("{0} vs {1}", teams[teamIdx], ListTeam[0]);
for (int idx = 1; idx < halfSize; idx++)
{
int firstTeam = (day + idx) % teamsSize;
int secondTeam = (day + teamsSize - idx) % teamsSize;
Console.WriteLine("{0} vs {1}", teams[firstTeam], teams[secondTeam]);
}
}
}
который будет печатать матчи команды каждый день.
позвольте мне быстро объяснить, как работает алгоритм:
я заметил, что, поскольку мы вращаем все команды, кроме первой, если мы поместим все команды в массив, кроме первого, то мы должны просто прочитать первую команду из этого массива использование смещения индекса на основе дня и выполнение модульной арифметики для правильной обертки. На практике мы будем рассматривать этот массив как бесконечно повторяющийся в обоих направлениях, и мы будем постепенно сдвигать наш взгляд вправо (или влево).
есть одна загвоздка, однако, и это тот факт, что мы должны приказать командам очень определенным образом, чтобы это работало правильно. Иначе мы не получим правильную ротацию. Из-за этого нам нужно прочитать соответствия вторая команда тоже очень своеобразно.
правильный способ подготовки списка выглядит следующим образом:
- никогда не помещайте первую команду (Команда № 1) в список.
- возьмите последнюю половину списка команды и поместите их в начало списка.
- возьмите первую половину списка, переверните его и поместите их в список (но не Команда № 1).
теперь правильный способ считывания списка таков: следуйте:
- для каждого дня увеличьте первый индекс, который вы смотрите на
1
. - для первой команды, которую вы видите в этом месте, сопоставьте эту команду с командой№1.
- для следующей команды в списке (
(day + idx) % numDays
), мы обычно сопоставляем его с командой, которая компенсируется половиной количества команд минус 1 (минус 1, потому что мы сами справились с первым матчем). Однако, поскольку вторая половина нашего списка была подготовлена путем возврата, нам необходимо сопоставьте это смещение во второй половине списка. Более простой способ сделать это-наблюдать, что в этом эквивалентно сопоставлению того же индекса, но с конца списка. Учитывая текущееday
смещение(day + (numDays - idx)) % numDays
.
обновление 3: Я не был счастлив, что мое решение включало такой запутанный выбор, сопоставление, реверсирование элементов массива. После того, как я подумал о том, что мое решение включало, я понял, что я был слишком зациклен на сохранении порядок команд, как данность. Однако это не требование, и можно получить другой, но одинаково действительный график, не заботясь о первоначальном заказе. Все, что имеет значение, - это алгоритм выбора, который я описываю во второй части моего объяснения.
таким образом, вы можете упростить следующие строки:
teams.AddRange(ListTeam.Skip(halfSize).Take(halfSize));
teams.AddRange(ListTeam.Skip(1).Take(halfSize -1).ToArray().Reverse());
в:
teams.AddRange(ListTeam); // Copy all the elements.
teams.RemoveAt(0); // To exclude the first team.
похоже, вы хотите запланировать круговом турнире. Статья wp содержит алгоритм.
Я не вижу, где вы даже пытаетесь повернуть массив. Перестановка будет выглядеть: 1 -> 2 -> 3 -> 4 ... - > n/2 - 1 -> n-1 - > n-2 - > n-3 ->... -> n / 2 - > 1 (и 0 остается фиксированным). Вы можете сделать это в 2 петли (верхний ряд и нижний ряд).
Я сделал некоторые улучшения в блоке кода с ответом, который вычисляет двойной круговой график
GameEntities db = new GameEntities();
private void btnTeamFixtures_Click(object sender, RoutedEventArgs e)
{
txtResults.Text = null;
var allTeams = db.Team.Select(t => t.TeamName);
int numDays = allTeams.Count() - 1;
int halfsize = allTeams.Count() / 2;
List<string> temp = new List<string>();
List<string> teams = new List<string>();
teams.AddRange(allTeams);
temp.AddRange(allTeams);
teams.RemoveAt(0);
int teamSize = teams.Count;
for (int day = 0; day < numDays * 2; day++)
{
//Calculate1stRound(day);
if (day % 2 == 0)
{
txtResults.Text += String.Format("\n\nDay {0}\n", (day + 1));
int teamIdx = day % teamSize;
txtResults.Text += String.Format("{0} vs {1}\n", teams[teamIdx], temp[0]);
for (int idx = 0; idx < halfsize; idx++)
{
int firstTeam = (day + idx) % teamSize;
int secondTeam = ((day + teamSize) - idx) % teamSize;
if (firstTeam != secondTeam)
{
txtResults.Text += String.Format("{0} vs {1}\n", teams[firstTeam], teams[secondTeam]);
}
}
}
//Calculate2ndRound(day);
if (day % 2 != 0)
{
int teamIdx = day % teamSize;
txtResults.Text += String.Format("\n\nDay {0}\n", (day + 1));
txtResults.Text += String.Format("{0} vs {1}\n", temp[0], teams[teamIdx]);
for (int idx = 0; idx < halfsize; idx++)
{
int firstTeam = (day + idx) % teamSize;
int secondTeam = ((day + teamSize) - idx) % teamSize;
if (firstTeam != secondTeam)
{
txtResults.Text += String.Format("{0} vs {1}\n", teams[secondTeam], teams[firstTeam]);
}
}
}
}
}
Если вы хотите, вы можете создать 2 метода и передать и целое число (День), как я сделал в 2 комментариях, чтобы отделить код.
Если у вас есть какие-либо вопросы или предложения, не стесняйтесь ответить.
Это может быть запутанный метод, но это может быть сведено к задаче теории графов. Создайте вершину графа для каждой команды и создайте ребро между каждой вершиной (так что это полный граф). Тогда для алгоритма:
для каждого дня i = 1 .. n:
- выберите любые две немаркированные вершины, которые непосредственно связаны, и пометьте ребро между ними i. Отметьте обе вершины.
- повторять, пока не будут отмечены все вершины.
- выход помеченные края (т. е. team1 против team2, team3 против team4 и т. д.)
- удалите помеченные ребра из графика и сбросьте все вершины на немаркированные.
Как насчет расчета возможных комбинаций для каждого дня, который вы хотите, то
- сортировать их в каждой паре, т. е. команда с наименьшим числом всегда первая в любой паре.
- сортировка перечисленных пар для каждого дня по первым в каждом пара.
предполагая, что у нас всегда есть четное число команд/игроков (если нечетно, добавьте BYE; для моего случая команды/игроки, ранжированные по их рейтингу, мы хотели бы иметь первую команду/игрока, чтобы играть слабее противника), вот моя реализация.
void CreateSchedule(int n)
{
int[] orig = new int[n];
for(int i=0;i<n; i++){
orig[i] = i + 1;
}
IEnumerable<int> rev = orig.Reverse();
int len = orig.Length;
for (int j = 0; j < len - 1; j++)
{
List<int> tmp = new List<int>();
tmp.Add(orig[0]);
tmp.AddRange(rev.Take(j).Reverse());
if (j < len && len > 1 + j) tmp.AddRange(orig.Skip(1).Take(len - 1 - j));
PrintMe(tmp, j + 1);
}
}
void PrintMe(IEnumerable<int> arr, int round)
{
Console.WriteLine("----------Round {0}----------------", round);
int halfSize = arr.Count() / 2;
IEnumerable<int> A = arr.Take(halfSize);
IEnumerable<int> B = arr.Skip(halfSize).Take(halfSize).Reverse();
var Result = A.Zip(B, (x, y) => $"{x} vs {y}");
Console.WriteLin(Result);
}