Basically I have list of players, and I want to pair them up so that each player will play everyone once. What's the quickest way to find this data?
assuming that players do not appear in the list twice, a double for
loop is very quick:
for (int i=0, i <= playerList.Count - 2, i++)
for (int j=i+1, j <= playerList.Count - 1, j++)
//add a new pairing of player i and j
Such tournament schedule is often called round-robin. In wikipedia, there's also an example of possible scheduling algorithm.
I put together 2 implementations to compare performance with. The very naive version 1 is about 50% slower than the version 2. That's not to say that nothing faster exists.
class Program
{
class Player
{
public string Name { get; set; }
public Player(string name)
{
Name = name;
}
}
class Match
{
public readonly Player Player1;
public readonly Player Player2;
public Match(Player player1, Player player2)
{
Player1 = player1;
Player2 = player2;
}
public override string ToString()
{
return string.Format("{0} vs. {1}", Player1.Name, Player2.Name);
}
}
static readonly List<Player> _players = new List<Player>()
{
new Player("John"),
new Player("Lisa"),
new Player("Matt"),
new Player("Dan"),
new Player("Steve"),
new Player("Sarah"),
new Player("Tim")
};
static void Main(string[] args)
{
const int count = 1000000;
{
var v1 = V1();
var sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
v1 = V1();
}
Console.WriteLine(v1);
Console.WriteLine(sw.Elapsed);
}
{
var v2 = V2();
var sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
v2 = V2();
}
Console.WriteLine(v2);
Console.WriteLine(sw.Elapsed);
}
Console.ReadLine();
}
static List<Match> V1()
{
var challengers = new List<Player>(_players);
var matches = new List<Match>();
foreach (var player in _players)
{
challengers.Remove(player);
foreach (var challenger in challengers)
{
matches.Add(new Match(player, challenger));
}
}
return matches;
}
static List<Match> V2()
{
var matches = new List<Match>();
for (int i = 0; i < _players.Count; i++)
{
for (int j = i + 1; j < _players.Count; j++)
{
matches.Add(new Match(_players[i], _players[j]));
}
}
return matches;
}
}
A simple divide and conquer algorithm:
- If there are only two people: Pair them and return them.
Otherwise:
- Split the group in two groups of equal size.
- Find all pairings in each group using this algorithm recursively.
Join the two lists.
E.g.
[[(a,b)]]
and[[(c,d)]]
becomes[[(a,b),(c,d)]]
.Find pairs across of the two groups, by rotating group two.
E.g.
[[(a,c),(b,d)],[(a,d),(b,c)]]
- Return
(3)
+(4)
This algorithm runs in O(n^2)
time, which is optimal, as it generates (n-1)
rounds of n/2
pairings.
For eight players you would get 7 rounds:
[(a,b), (c,d), (e,f), (g,h)]
[(a,c), (b,d), (e,g), (f,h)]
[(a,d), (b,c), (e,h), (f,g)]
[(a,e), (b,f), (c,g), (e,h)]
[(a,f), (b,g), (c,h), (e,e)]
[(a,g), (b,h), (c,e), (e,f)]
[(a,h), (b,e), (c,f), (e,g)]
© 2022 - 2024 — McMap. All rights reserved.