C#中的循环赛事算法

时间:2009-08-18 10:27:54

标签: c# algorithm round-robin

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

然后我要输出;

第1天: 第1队与第2队; 第3队与第4队; Team 5vs Team 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++;
            }

        }
    }
编辑:我发现code sample in java but我无法翻译它......

8 个答案:

答案 0 :(得分:11)

这应该很容易使用模运算:

更新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.

答案 1 :(得分:6)

听起来你想安排一个round-robin tournament。 wp文章包含算法。

我甚至没有看到你在哪里试图旋转阵列。排列看起来像:1 - &gt; 2 - &gt; 3 - &gt; 4 ... - &gt; n / 2 - 1 - &gt; n - 1 - &gt; n - 2 - &gt; n - 3 - &gt; ... - &gt; n / 2 - &gt; 1(和0保持固定)。您可以在2个循环(顶行和底行)中执行此操作。

答案 2 :(得分: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个方法并传递和整数(Day),就像我在2个注释行中所做的那样,来分隔代码。

如果您有任何问题或建议,请随时回复。

答案 3 :(得分:1)

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

每天i = 1 .. n:

  • 选择任意两个未标记的直接连接的顶点,并用i标记它们之间的边缘。标记两个顶点。
  • 重复,直到标记所有顶点。
  • 输出标记的边缘(即team1 vs team2,team3 vs team4等)
  • 从图表中删除带标签的边缘,并将所有顶点重置为未标记。

答案 4 :(得分:0)

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

  1. 在每对中对它们进行排序,即 最低数量的团队总是第一位的 任何一对。
  2. 按每个日期中的第一个对每天列出的配对进行排序 对

答案 5 :(得分:0)

假设我们总是有偶数队/球员(如果是奇数,请加上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);
}

答案 6 :(得分:0)

基于@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

答案 7 :(得分:0)

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

欢呼