C# 中的循环赛算法

发布于 2024-08-02 14:23:38 字数 1214 浏览 12 评论 0原文

我在实现这个小循环项目时遇到了一些麻烦。我尝试做的是生成游戏预览日历,

然后我想要输出;

第一天: 第 1 队对阵第 2 队; 第 3 队对阵第 4 队; 5队对6队;

第二天 第 1 队对阵第 4 队; 第六队对阵第三队; 第 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++;
            }

        }
    }

编辑:我发现了 java 中的代码示例,但 我无法翻译它...

I am having some trouble to achieve this little round robin project. What i try to do is to generate a preview calendar of games

then I want to output;

day 1:
Team 1 vs Team 2;
Team 3 vs Team 4;
Team 5vs Team 6;

day 2
Team 1 vs Team 4;
Team 6 vs Team 3;
Team 2 vs Team 5;

till the end of the championship;

Here is the code i've got so far but i'm having trouble to let the first team fixed while the rest of the array rotates...:

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: I found a code sample in java but i cant translate it...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

请你别敷衍 2024-08-09 14:23:38

使用模块化算术应该很容易做到这一点:

更新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]);
        }
    }
}

它将打印每天的球队比赛。

让我快速尝试解释该算法是如何工作的:

我注意到,由于我们正在轮换除第一个团队之外的所有团队,如果我们将除第一个团队之外的所有团队放入一个数组中,那么我们应该从该数组使用基于日期的索引偏移量并执行模块化算术以正确环绕。在实践中,我们会将该数组视为在两个方向上无限重复,并且我们会将视图逐渐向右(或向左)滑动。

然而,有一个障碍,那就是我们必须以一种非常特殊的方式命令团队才能使其正常工作。否则,我们将无法获得正确的旋转。因此,我们也需要以一种非常特殊的方式来了解匹配的第二支球队。

准备名单的正确方法如下:

  • 切勿将第一支球队(Team#1)放入名单中。
  • 取出队伍名单的后半部分,将其放在名单的前面。
  • 取出列表的前半部分,将其反转并将它们放入列表中(但不包括 Team#1)。

现在,读取列表的正确方法如下:

  • 每天,将您正在查看的第一个索引增加 1
  • 对于您在该位置看到的第一支球队,请将该球队与 Team#1 匹配。
  • 对于列表中的下一个团队 ((day + idx) % numDays),我们通常会将其与偏移量为团队数量减 1 一半的团队进行匹配(减 1,因为我们处理了我们自己的第一场比赛)。但是,由于列表的后半部分是通过恢复来准备的,因此我们需要在恢复后的列表后半部分中匹配该偏移量。一种更简单的方法是观察这相当于匹配相同的索引,但从列表的末尾开始。给定当前偏移量为(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.

This should be easy enough to do using modular arithmetic:

UPDATE 2: (As promised correct algorithm)

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]);
        }
    }
}

which would print each day's team matches.

Let me quickly try to explain how the algorithm works:

I noticed that since we are rotating all the teams except the first one, if we put all the teams in an array except the first one, then we should just read off the first team from that array using index offset based on the day and doing modular arithmetic to wrap around correctly. In practice we would be treating that array as infinitely repeating in both directions and we would be sliding our view incrementally to right (or to the left).

There is one snag, however, and that is the fact that we have to order the teams in a very particular way for this to work correctly. Otherwise, we do not get the correct rotation. Because of this we need to read of the matching second team in a very peculiar way as well.

The correct way to prepare your list is as follows:

  • Never put the first team (Team#1) in the list.
  • Take the last half of the team list and put them in the front of the list.
  • Take the first half of the list, reverse it and put them in the list (but not Team#1).

Now, the correct way to read off the list is as follow:

  • For each day, increment the first index you are looking at by 1.
  • For the first team that you see at that location, match that team with Team#1.
  • For the next team in the list ((day + idx) % numDays), we would normally match it with the team that is offset by half the number of teams minus 1 (minus 1 because we dealt with the first match ourselves). However, since the second half of our list was prepared by reverting, we need to match that offset in the reverted second half of the list. A simpler way to do is to observe that in this is equivalent to matching the same index but from the end of the list. Given the current day offset that is (day + (numDays - idx)) % numDays.

UPDATE 3: I was not happy that my solution involved such convoluted selection, matching, reversing of array elements. After I got thinking about what my solution involved I realized that I was too hung up about keep the order of the teams as given. However, that is not a requirement and one can get a different but equally valid schedule by not caring about the initial ordering. All that matters is the selection algorithm I describe in the second part of my explanation.

Thus you can simplify the following lines:

teams.AddRange(ListTeam.Skip(halfSize).Take(halfSize));
teams.AddRange(ListTeam.Skip(1).Take(halfSize -1).ToArray().Reverse());

to:

teams.AddRange(ListTeam); // Copy all the elements.
teams.RemoveAt(0); // To exclude the first team.
冷弦 2024-08-09 14:23:38

听起来您想安排一场循环赛。 wp 文章包含该算法。

我不知道你在哪里尝试旋转阵列。排列将类似于: 1 -> 2-> 3-> 4 ...-> n/2-1-> n-1-> n-2-> n-3-> ...-> n/2-> 1(0 保持固定)。您可以通过 2 个循环(顶行和底行)来完成此操作。

It sounds like you want to schedule a round-robin tournament. The wp article contains the algorithm.

I don't see where you are even trying to rotate the array. The permutation will look something like: 1 -> 2 -> 3 -> 4 ... -> n/2 - 1 -> n - 1 -> n - 2 -> n - 3 -> ... -> n/2 -> 1 (and 0 stays fixed). You can do that in 2 loops (top row and bottom row).

梓梦 2024-08-09 14:23:38

我在计算双循环计划的已回答代码块中做了一些改进,

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 made some improvements in the answered code block that calculates double round-robin schedule

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]);
                    }
                }
            }
        }
    }

If u want u can create 2 methods and pass and integer(Day) like i did in the 2 commented lines, to separate the code.

If you have any questions or suggestions feel free to reply.

白首有我共你 2024-08-09 14:23:38

根据 @paracycle 的回答,我提供了一个更好的代码,并进行了以下更改:

  • 我的方法基于泛型类型 T
  • 我的代码不会更改原始的 teams列表实例
  • 提高代码质量
public static IEnumerable<(int Day, T First, T Second)> ListMatches<T>(IList<T> teams)
{
    var matches = new List<(int, T, T)>();
    if (teams == null || teams.Count < 2)
    {
        return matches;
    }

    var restTeams = new List<T>(teams.Skip(1));
    var teamsCount = teams.Count;
    if (teams.Count % 2 != 0)
    {
        restTeams.Add(default);
        teamsCount++;
    }

    for (var day = 0; day < teamsCount - 1; day++)
    {
        if (restTeams[day % restTeams.Count]?.Equals(default) == false)
        {
            matches.Add((day, teams[0], restTeams[day % restTeams.Count]));
        }

        for (var index = 1; index < teamsCount / 2; index++)
        {
            var firstTeam = restTeams[(day + index) % restTeams.Count];
            var secondTeam = restTeams[(day + restTeams.Count - index) % restTeams.Count];
            if (firstTeam?.Equals(default) == false && secondTeam?.Equals(default) == false)
            {
                matches.Add((day, firstTeam, secondTeam));
            }
        }
    }

    return matches;
}

测试代码:

foreach (var match in ListMatches(new List<string> { "T1", "T2", "T3", "T4", "T5", "T6" }))
{
    Console.WriteLine($"{match.Day} => {match.First}-{match.Second}");
}

6 个团队的输出:

0 => T1-T2
0 => T3-T6
0 => T4-T5
1 => T1-T3
1 => T4-T2
1 => T5-T6
2 => T1-T4
2 => T5-T3
2 => T6-T2
3 => T1-T5
3 => T6-T4
3 => T2-T3
4 => T1-T6
4 => T2-T5
4 => T3-T4

5 个团队的输出:

0 => T1-T2
0 => T4-T5
1 => T1-T3
1 => T4-T2
2 => T1-T4
2 => T5-T3
3 => T1-T5
3 => T2-T3
4 => T2-T5
4 => T3-T4

Based on the @paracycle's answer, I am providing a better code with the following changes:

  • My method is based on the generic type T
  • My code doesn't change the original teams list innstance
  • Improved code quality
public static IEnumerable<(int Day, T First, T Second)> ListMatches<T>(IList<T> teams)
{
    var matches = new List<(int, T, T)>();
    if (teams == null || teams.Count < 2)
    {
        return matches;
    }

    var restTeams = new List<T>(teams.Skip(1));
    var teamsCount = teams.Count;
    if (teams.Count % 2 != 0)
    {
        restTeams.Add(default);
        teamsCount++;
    }

    for (var day = 0; day < teamsCount - 1; day++)
    {
        if (restTeams[day % restTeams.Count]?.Equals(default) == false)
        {
            matches.Add((day, teams[0], restTeams[day % restTeams.Count]));
        }

        for (var index = 1; index < teamsCount / 2; index++)
        {
            var firstTeam = restTeams[(day + index) % restTeams.Count];
            var secondTeam = restTeams[(day + restTeams.Count - index) % restTeams.Count];
            if (firstTeam?.Equals(default) == false && secondTeam?.Equals(default) == false)
            {
                matches.Add((day, firstTeam, secondTeam));
            }
        }
    }

    return matches;
}

Code to test it:

foreach (var match in ListMatches(new List<string> { "T1", "T2", "T3", "T4", "T5", "T6" }))
{
    Console.WriteLine($"{match.Day} => {match.First}-{match.Second}");
}

Output for 6 teams:

0 => T1-T2
0 => T3-T6
0 => T4-T5
1 => T1-T3
1 => T4-T2
1 => T5-T6
2 => T1-T4
2 => T5-T3
2 => T6-T2
3 => T1-T5
3 => T6-T4
3 => T2-T3
4 => T1-T6
4 => T2-T5
4 => T3-T4

Output for 5 teams:

0 => T1-T2
0 => T4-T5
1 => T1-T3
1 => T4-T2
2 => T1-T4
2 => T5-T3
3 => T1-T5
3 => T2-T3
4 => T2-T5
4 => T3-T4
懒猫 2024-08-09 14:23:38

这可能是一种复杂的方法,但这可以简化为图论问题。为每个团队创建一个图顶点,并在每个顶点之间创建一条边(因此它是一个完整的图)。然后对于算法:

对于每一天 i = 1 .. n :

  • 选取任意两个直接连接的未标记顶点,并用 i 标记它们之间的边。标记两个顶点。
  • 重复此操作,直到所有顶点都被标记。
  • 输出标记的边(即 team1 与 team2、team3 与 team4 等)
  • 从图中删除标记的边并将所有顶点重置为未标记。

It may be a convoluted method, but this can be reduced to a graph theory problem. Create a graph vertex for each team, and create an edge between every vertex (so it is a complete graph). Then for the algorithm:

For each day i = 1 .. n :

  • Pick any two unmarked vertices that are directly connected and label the edge between them with i. Mark both vertices.
  • Repeat until all vertices are marked.
  • Output the labelled edges (i.e. team1 vs team2, team3 vs team4 etc)
  • Delete the labelled edges from the graph and reset all vertices to unmarked.
渡你暖光 2024-08-09 14:23:38

我已在 github 上发布了我的实现 RoundRobinTournamentSchedule
您可以找到相应的nuget包并在您自己的解决方案中使用它

干杯

I've posted my implementation on github RoundRobinTournamentSchedule
You can find corresponding nuget package and use it in your own solution

Cheers

口干舌燥 2024-08-09 14:23:38

如何计算您想要的每一天的可能组合,然后

  1. 在每对中对它们进行排序,即
    人数最少的队伍总是第一
    在任何一对中。
  2. 按每天的第一个对列出的配对进行排序
    一对。

How about calculating the possible combinations for each day that you want then

  1. sort them within each pair i.e.
    lowest number team is always first
    in any pair.
  2. sort the listed pairings for each day by the first in each
    pair.
素衣风尘叹 2024-08-09 14:23:38

假设我们总是有偶数球队/球员(如果是奇数,请添加“再见”;就我而言,球队/球员按评分排名,我们希望头号种子球队/球员首先对阵较弱的对手),这是我的实现。

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);
}

Assuming we always have even number teams/players (if odd, add BYE; for my case the teams/players ranked by their rating, we would like to have the top seeded team/player to play weaker opponent first), Here is my implementation.

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文