"匹配制作"算法?

时间:2012-10-14 16:05:38

标签: c# algorithm match matching

对于一个项目,我需要计算几个团队之间的匹配计划。

要求:

  • 我是一对团队
  • 所有球队必须与其他球队进行一次比赛(并且只有一次)
  • 所有球队同时比赛

E.g。 有了4个团队(A,B,C和D),我希望能够计算出这个:

第1轮

  • A vs B
  • C vs D

第2轮

  • A vs D
  • B vs C

第3轮

  • A vs C
  • B vs D

问题在于,在第X轮中有一些选择,这使得在第X + 1轮比赛中无法进行任何比赛(球队已经与其他球队比赛过)。

我想我可以使用一些回溯技术,但我正在搜索是否有算法?

这将在c#中实现。

你知道怎么做吗?

4 个答案:

答案 0 :(得分:3)

实际上,答案在评论中提供的link Olivier中。更具体地说,this answer

它确实处理了Round的概念,除了它不是很明显。在该代码中,Tuple<string, string>表示匹配(包含两个团队名称的项目),List<Tuple<string, string>>表示一个回合(匹配集合)。

代码以List<List<Tuple<string, string>>>

的形式返回一组Rounds

我重构了一下代码,以便MatchRound的概念在代码中更明显。

以下是MatchRound类:

public class Match
{
    public string Team1 { get; set; }
    public string Team2 { get; set; }

    public Match(string team1, string team2)
    {
        Team1 = team1;
        Team2 = team2;
    }

    public override string ToString()
    {
        return string.Format("{0} vs {1}", Team1, Team2);
    }
}

public class Round
{
    public List<Match> Matches { get; private set; }

    public Round()
    {
        Matches = new List<Match>();
    }

    public override string ToString()
    {
        return String.Join(Environment.NewLine, Matches) + Environment.NewLine;
    }
}

这是执行魔术的代码(信用转到Nagg):

public static List<Round> ComputeFixtures(List<string> listTeam)
{
    var result = new List<Round>();

    var numberOfRounds = (listTeam.Count - 1);
    var numberOfMatchesInARound = listTeam.Count / 2;

    var teams = new List<string>();

    teams.AddRange(listTeam.Skip(numberOfMatchesInARound).Take(numberOfMatchesInARound));
    teams.AddRange(listTeam.Skip(1).Take(numberOfMatchesInARound - 1).ToArray().Reverse());

    var numberOfTeams = teams.Count;

    for (var roundNumber = 0; roundNumber < numberOfRounds; roundNumber++)
    {
        var round = new Round();
        var teamIdx = roundNumber % numberOfTeams;

        round.Matches.Add(new Match(teams[teamIdx], listTeam[0]));

        for (var idx = 1; idx < numberOfMatchesInARound; idx++)
        {
            var firstTeamIndex = (roundNumber + idx) % numberOfTeams;
            var secondTeamIndex = (roundNumber + numberOfTeams - idx) % numberOfTeams;

            round.Matches.Add(new Match(teams[firstTeamIndex], teams[secondTeamIndex]));
        }

        result.Add(round);
    }

    return result;
}

以下是此代码的一些在线运行示例:

答案 1 :(得分:2)

我认为你采取了错误的方式。 我不是基于上一轮计算每一轮配对,而是先使用简单的双循环进行所有可能的配对,然后轮流随机分配游戏。

由于每个玩家将玩完全相同数量的游戏,因此必须存在此类分发。

答案 2 :(得分:1)

尝试循环播放。这是一个简单的调度算法,用于在进程间共享时隙,但是这个问题让我想起了它。

修改

现在这是Round-robin tournament的实现。如果我们有ODD团队数量,我们必须插入一个虚拟团队,否则就会有一个没有对手的团队。由于轮数是偶数,所以总轮数是(NumberOfTeams-1)。在一开始我们建立了第一轮:

A B C D E F G H

H G F E D C B A

所以,A队 - H队,B队 - G队等

从现在开始,我们将一支球队固定下来,例如A.然后我们将A_Side球队从第二位置转移到右侧。最后一支队伍将进入第2位。(A B C D E F G H将是A H B C D E F G)。请参阅rotate_A_side()递归方法(只是为了好玩)。

B_Sides的一半向左移动。这将使H G F E D - G F E D。

由于球队选择是对称的(A玩H,然后H玩A),B_Side的上半部分是A_Side低部队的反向副本。因此,D C B A将是C B H A)。请参阅rotate_B_side()。

所以,第2轮是:

A H B C D E F G

G F E D C B H A

要进行所有轮次,只需重复上述换档步骤即可。见NextRound()

这是一个实现算法的c#类:

    class Teams
{
    private int[] A_Side;
    private int[] B_Side;
    public int[,] PlayingCounter;
    public int RoundCounter = 1;
    public bool DummyTeam = false;                 // ODD number of teams -> one team will no be able to play.

    public bool NextRoundExists
    {
        get
        {
            return (RoundCounter < B_Side.Length-1);

        }
    }
    public Teams(int NumberOfTeams)
    {
        if (NumberOfTeams % 2 != 0)
        {
            NumberOfTeams++; DummyTeam = true;
        }
        A_Side = new int[NumberOfTeams];
        B_Side = new int[NumberOfTeams];
        PlayingCounter = new int[NumberOfTeams,NumberOfTeams];     // Counting to see if alg is correct
        int x,y;
        for (x=0; x<NumberOfTeams; x++) 
        {
            A_Side[x] = x + 1;                  
            B_Side[NumberOfTeams-x-1]=x+1; 
            for (y=0;y<NumberOfTeams;y++) 
            {
                PlayingCounter[x,y] = 0;
            }

        }

    }

    private void rotate_A_Side(int AtPos)
    {
        if (AtPos == 1)
        {
            int iO = A_Side[A_Side.Length - 1];
            rotate_A_Side(AtPos+1);
            A_Side[1] = iO;
        }
        else 
        {
            if (AtPos < A_Side.Length - 1) { rotate_A_Side(AtPos + 1); }
            A_Side[AtPos] = A_Side[AtPos - 1];
        }
    }
    public void rotate_B_Side()
    {
        int i;
        for (i = 0; i<B_Side.Length/2 ; i++)
        {
            B_Side[i] = B_Side[i + 1];
        }
        for (i = B_Side.Length / 2; i < B_Side.Length; i++)
        {
            B_Side[i] = A_Side[B_Side.Length/2 - (i -B_Side.Length/2 + 1) ];
        }

    }
    public bool NextRound()
    {
        if (NextRoundExists)
        {
            RoundCounter++;         // Next round
            rotate_A_Side(1);       // A side rotation
            rotate_B_Side();        // B side rotation
            LogRound();             // Update counters
            return true;
        }
        else return false;
    }
    public void LogRound()
    {
        for (int x = 0; x < A_Side.Length; x++)
        {
            PlayingCounter[A_Side[x]-1, B_Side[x]-1]++;
            PlayingCounter[B_Side[x]-1, A_Side[x]-1]++;
        }
    }
    public string GetCounters()
    {
        string return_value = "";

        for (int y = 0; y < A_Side.Length; y++)
        {
            for (int x = 0; x < A_Side.Length; x++)
            {
                return_value += String.Format(" {0:D3}", PlayingCounter[y, x]);
            }
            return_value += System.Environment.NewLine;
        }
        return return_value;
    }

    public string GetCurrentRound()
    {
        string Round = "Round #" + RoundCounter.ToString() + " ";
        for (int x = 0; x < B_Side.Length; x++)
        {
            Round += String.Format("Team {0} - Team {1};", A_Side[x], B_Side[x]);
        }
        return Round;
    }

}

从您的代码中,您可以像以下一样使用它:

Teams Rounds = new Teams(22);
if (Rounds.DummyTeam) { 
       // Anything to do if nober of teams is odd?
}
Rounds.LogRound();    // DEBUG - you can check number of matches ;-)
while (Rounds.NextRoundExists)     // While we have next round...
 {
   Rounds.NextRound();             // ... generate the next 
                                   //     round (team assignment)
   // Your can tack using: Rounds.GetCurrentRound()
 }
// If you want to see the number of matches, call Rounds.GetCounters();

6小组给了我以下输出:

第1轮A-F; B-E; C-D; D-C; E-B; F A ;

第二轮A-E; F-D; B-C; C-B; D-F; E-A;

第3轮A-D; E-C; F-B; B-F; C-E; D-A;

第4轮A-C; D B ; E-F; F-E; B-D; C-A;

第#轮第5轮A-B; C-F; D-E; E-D; F-C; B-A;

我用A等替换了Team 1

应该改进rotate_B_Side(),这是一种快速的方法。

答案 3 :(得分:0)

我快速使用,使用简单的方法

  1. 生成所有可能的游戏
  2. 根据团队可用性将游戏分配到轮次
  3. 这导致游戏的分布似乎有些“僵硬”。

    schedule_tournament(new List<string> { "A", "B", "C" });
    schedule_tournament(new List<string> { "A", "B", "C", "D", });
    schedule_tournament(new List<string> { "A", "B", "C", "D", "E" });
    schedule_tournament(new List<string> { "A", "B", "C", "D", "E", "F" });
    schedule_tournament(new List<string> { "A", "B", "C", "D", "E", "F", "G" });
    ...
    
    private void schedule_tournament(List<string> teams)
    {            
        List<string> games = new List<string>();
        List<string> rounds = new List<string>();
    
        // get all possible games
        for (int i = 0; i < teams.Count; i++)
        {
            for (int j = i + 1; j < teams.Count; j++)
            {
                string game_name = string.Format("{0}{1}", teams[i], teams[j]);
                if (!games.Contains(game_name)) games.Add(game_name);
            }
        }
    
        // allocate games to rounds
        for (int i = 0; i < games.Count; i++)
        {
            bool allocated = false;
            for (int j = 0; j < rounds.Count; j++)
            {
                string team_1 = games[i].Substring(0, 1);
                string team_2 = games[i].Substring(1, 1);
                if (!rounds[j].Contains(team_1) && !rounds[j].Contains(team_2))
                {
                    rounds[j] += " - " + games[i];
                    allocated = true;
                    break;
                }
            }
            if (!allocated)
            {
                rounds.Add(games[i]);
            }
        }
        Console.WriteLine("{0} teams, play {1} games in {2} rounds", teams.Count, games.Count, rounds.Count);
        for (int i = 0; i < rounds.Count; i++) Console.WriteLine("Round {0}: {1}", i + 1, rounds[i]);
    }
    

    输出是:

    3 teams, play 3 games in 3 rounds
    Round 1: AB
    Round 2: AC
    Round 3: BC
    4 teams, play 6 games in 3 rounds
    Round 1: AB - CD
    Round 2: AC - BD
    Round 3: AD - BC
    5 teams, play 10 games in 7 rounds
    Round 1: AB - CD
    Round 2: AC - BD
    Round 3: AD - BC
    Round 4: AE
    Round 5: BE
    Round 6: CE
    Round 7: DE
    6 teams, play 15 games in 7 rounds
    Round 1: AB - CD - EF
    Round 2: AC - BD
    Round 3: AD - BC
    Round 4: AE - BF
    Round 5: AF - BE
    Round 6: CE - DF
    Round 7: CF - DE
    7 teams, play 21 games in 7 rounds
    Round 1: AB - CD - EF
    Round 2: AC - BD - EG
    Round 3: AD - BC - FG
    Round 4: AE - BF - CG
    Round 5: AF - BE - DG
    Round 6: AG - CE - DF
    Round 7: BG - CF - DE