Distribute matches 'tournament friendly' - c#

Say I have four teams, ABCD, I want to create matches so that the teams evenly have to do them:
not desired
A vs B
A vs C
A vs D
B vs C
B vs D
C vs D
desired
A vs B
C vs D
B vs C
A vs D
A vs C
B vs D
Another way of putting it: I want the teams to have a match as few in a row as possible.
Target language is C#, but I can translate easily.
Edit: Quick sidenote, it can be more than 4 teams!

One way to solve this is by the following steps:
Create a collection containing the total number of match combinations.
Create a new collection with the same length as the collection in step 1.
Go through the items in step 1, add the same item in step 2, with the condition that the next item to be added should have the maximum difference between it and the last added item.
Some sample code:
// Just a container to conveniently hold a match between two teams
public class Match
{
public Match(string teamOne, string teamTwo)
{
TeamOne = teamOne;
TeamTwo = teamTwo;
}
public string TeamOne { get; private set; }
public string TeamTwo { get; private set; }
public override string ToString()
{
return String.Format("{0} vs {1}", TeamOne, TeamTwo);
}
}
public class MatchMaking
{
public MatchMaking()
{
Teams = new List<string> { "A", "B", "C", "D", "E" };
}
public IList<string> Teams { get; private set; }
public IList<Match> GetMatches()
{
var unorderedMatches = GetUnorderedMatches();
// The list that we will eventually return
var orderedMatches = new List<Match>();
// Track the most recently added match
Match lastAdded = null;
// Loop through the unordered matches
// Add items to the ordered matches
// Add the one that is most different from the last added match
for (var i = 0; i < unorderedMatches.Count; i++)
{
if (lastAdded == null)
{
lastAdded = unorderedMatches[i];
orderedMatches.Add(unorderedMatches[i]);
unorderedMatches[i] = null;
continue;
}
var remainingMatches = unorderedMatches.Where(m => m != null);
// Get the match which is the most different from the last added match
// We do this by examining all of the unadded matches and getting the maximum difference
Match mostDifferentFromLastAdded = null;
int greatestDifference = 0;
foreach (var match in remainingMatches)
{
var difference = GetDifference(lastAdded, match);
if (difference > greatestDifference)
{
greatestDifference = difference;
mostDifferentFromLastAdded = match;
}
if (difference == 2)
{
break;
}
}
// Add the most different match
var index = unorderedMatches.ToList().IndexOf(mostDifferentFromLastAdded);
lastAdded = unorderedMatches[index];
orderedMatches.Add(unorderedMatches[index]);
unorderedMatches[index] = null;
}
return orderedMatches;
}
// Create a list containing the total match combinations with an arbitrary order
private List<Match> GetUnorderedMatches()
{
var totalNumberOfCombinations = AdditionFactorial(Teams.Count - 1);
var unorderedMatches = new List<Match>(totalNumberOfCombinations);
for (int i = 0; i < Teams.Count; i++)
{
for (int j = 0; j < Teams.Count; j++)
{
if (j <= i) continue;
unorderedMatches.Add(new Match(Teams[i], Teams[j]));
}
}
return unorderedMatches;
}
// Get the difference between two matches
// 0 - no difference, 1 - only one team different, 2 - both teams different
private int GetDifference(Match matchOne, Match matchTwo)
{
var matchOneTeams = new HashSet<string> { matchOne.TeamOne, matchOne.TeamTwo };
var matchTwoTeams = new HashSet<string> { matchTwo.TeamOne, matchTwo.TeamTwo };
var intersection = matchOneTeams.Intersect(matchTwoTeams);
return (intersection.Count() - 2) * -1;
}
// Just a helper to get the total number of match combinations
private int AdditionFactorial(int seed)
{
int result = 0;
for (int i = seed; i > 0; i--)
{
result += i;
}
return result;
}
}
public class Program
{
private static void Main(string[] args)
{
var matchMaking = new MatchMaking();
foreach (var match in matchMaking.GetMatches())
{
Console.WriteLine(match);
}
}
}

I think you may achieve what you need doing as follows. If you've got n number of teams, all the possible matches between teams can be represented with a Kn Complete graph.
The way I would come up with your desired sorting, is taking (removing) edges from that graph, one at a time, always trying to find an edge that matches teams that didn't match immediately before. Further more, I think this approach (with a little variation) can be used to generate the best way to maximize concurrent matches, if each time you take an edge you pick the one with the teams that haven't played in most time.
For simplicity, lets assume that teams are ints in the range of 0 to n-1. The graph can simply be represented with a boolean matrix. To pick the match with the teams that haven't played in most time, you can keep track of the last time each team played. For n teams, you'll have a total of n*(n-1)/2 number of matches.
IEnumerable<Tuple<int,int>> GenerateMatches(int n) {
bool[,] pendingMatches = new bool[n,n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++)
pendingMatches[i,j] = true;
}
int[] lastPlayed = new int[n];
int totalMatches = n*(n-1)/2;
for (int m = 1; m <= totalMatches; m++) {
Tuple<int, int> t = null;
int longestPlayed = -1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (pendingMatches[i,j]) {
int moreRecentlyPlayed = Math.Max(lastPlayed[i], lastPlayed[j]);
int timeSinceLastPlay = m - moreRecentlyPlayed;
if (timeSinceLastPlay > longestPlayed) {
longestPlayed = timeSinceLastPlay;
t = Tuple.Create(i,j);
}
}
}
}
lastPlayed[t.Item1] = lastPlayed[t.Item2] = m;
pendingMatches[t.Item1, t.Item2] = false;
yield return t;
}
}
The part that chooses the next match are the two most nested fors. The complexity of that part can be improved if you use some kind of priority queue adapted to adjust the priority of edges that involve the teams of the last picked edge after updating the lastPlayed array.
Hope that helps.

Related

Add Items to List depending on Loop Value c#

I have a method that returns a list. It takes 6 parameters (leg numbers). There is also a property that keeps track of the Number of Legs (it can have a value between 1-6). I can't seem to figure out if its possible to incorporate a loop and only add the list items depending on the Number of legs.
private List<T> ReturnListOfTypes<T>(T leg1, T leg2, T leg3,
T leg4, T leg5, T leg6)
{
List<T> legs = new List<T>();
for (int i = 0; i < NumberOfLegs; i++)
{
}
legs.Add(leg1);
legs.Add(leg2);
legs.Add(leg3);
legs.Add(leg4);
legs.Add(leg5);
legs.Add(leg6);
return legs;
}
If NumberOfLegs = 3, I only want the list to add:
legs.Add(leg1);
legs.Add(leg2);
legs.Add(leg3);
If NumberOfLegs = 1, it should only add leg 1 to the list:
legs.Add(leg1);
Right now, the list items are outside of the loop, because I can't figure out a way to only add the specified items depending on what the loop value is.
Maybe you could make it a params []:
private List<T> ReturnListOfTypes<T>(params T[] legs)
{
List<T> legList = new List<T>(NumberOfLegs);
for (int i = 0; i < Math.Min(NumberOfLegs, legs.Length); i++)
{
legList.Add(legs[i]);
}
return legList;
}
You can now still use it like in your code, but it's treated as an array. This presumes that the order of "legs" is always the order you want to return.
So you could use it for example so:
List<string> list = ReturnListOfTypes("1", "2", "3");
If I guess correctly, its all about adding number to particular leg.
So in this case:
private List<T> ReturnListOfTypes<T>(T leg1, T leg2, T leg3,
T leg4, T leg5, T leg6)
{
int NumberOfLegs = 6;
int j=1;
List<T> legs = new List<T>();
for (int i = 0; i <= (NumberOfLegs+1); i++)
{
j = j+i;
var newLeg = "leg"+j;
///Dont forget here, your for loop starts with 0, if you dont have declared leg0 then there would be error)
legs.Add(newLeg);
}
return legs;
}
///Start with i=1
int NumberOfLegs = 6;
List<T> legs = new List<T>();
for (int i = 1; i < (NumberOfLegs+1); i++)
{
var newLeg = "leg"+i;
legs.Add(newLeg);
}
return legs;
}
Also you could avoid outofRange with if statement before the loop: if(NumberOfLegs > 0)
I'm not sure I understood properly your problem, but the following code should do what you are looking for:
List<T> ReturnListOfTypes<T>(T leg1, T leg2, T leg3, T leg4, T leg5, T leg6)
{
List<T> legs = new List<T>();
if (NumberOfLegs >= 1) { legs.Add(leg1); }
if (NumberOfLegs >= 2) { legs.Add(leg2); }
if (NumberOfLegs >= 3) { legs.Add(leg3); }
if (NumberOfLegs >= 4) { legs.Add(leg4); }
if (NumberOfLegs >= 5) { legs.Add(leg5); }
if (NumberOfLegs >= 6) { legs.Add(leg6); }
return legs;
}
Hope it helps

Figure out max number of consecutive seats

I had an interviewer ask me to write a program in c# to figure out the max number of 4 members families that can sit consecutively in a venue, taking into account that the 4 members must be consecutively seated in one single row, with the following context:
N represents the number of rows availabe.
The Columns are labeled from the letter "A" to "K", purposely ommiting the letter "i" (in other words, {A,B,C,D,E,F,G,H,J,K})
M represents a list of reserved seats
Quick example:
N = 2
M = {"1A","2F","1C"}
Solution = 3
In the representation you can see that, with the reservations and the size given, only three families of 4 can be seated in a consecutive order.
How would you solve this? is it possible to not use for loops? (Linq solutions)
I got mixed up in the for loops when trying to deal with the reservations aray: My idea was to obtain all the reservations that a row has, but then I don't really know how to deal with the letters (Converting directly from letter to number is a no go because the missing "I") and you kinda need the letters to position the reserved sits anyway.
Any approach or insight on how to go about this problem would be nice.
Thanks in advance!
Here is another implementation.
I also tried to explain why certain things have been done.
Good luck.
private static int GetNumberOfAvailablePlacesForAFamilyOfFour(int numberOfRows, string[] reservedSeats)
{
// By just declaring the column names as a string of the characters
// we can query the column index by colulmnNames.IndexOf(char)
string columnNames = "ABCDEFGHJK";
// Here we transform the reserved seats to a matrix
// 1A 2F 1C becomes
// reservedSeatMatrix[0] = [0, 2] -> meaning row 1 and columns A and C, indexes 0 and 2
// reservedSeatMatrix[1] = [5] -> meaning row 2 and column F, index 5
List<List<int>> reservedSeatMatrix = new List<List<int>>();
for (int row = 0; row < numberOfRows; row++)
{
reservedSeatMatrix.Add(new List<int>());
}
foreach (string reservedSeat in reservedSeats)
{
int seatRow = Convert.ToInt32(reservedSeat.Substring(0, reservedSeat.Length - 1));
int seatColumn = columnNames.IndexOf(reservedSeat[reservedSeat.Length - 1]);
reservedSeatMatrix[seatRow - 1].Add(seatColumn);
}
// Then comes the evaluation.
// Which is simple enough to read.
int numberOfAvailablePlacesForAFamilyOfFour = 0;
for (int row = 0; row < numberOfRows; row++)
{
// Reset the number of consecutive seats at the beginning of a new row
int numberOfConsecutiveEmptySeats = 0;
for (int column = 0; column < columnNames.Length; column++)
{
if (reservedSeatMatrix[row].Contains(column))
{
// reset when a reserved seat is reached
numberOfConsecutiveEmptySeats = 0;
continue;
}
numberOfConsecutiveEmptySeats++;
if(numberOfConsecutiveEmptySeats == 4)
{
numberOfAvailablePlacesForAFamilyOfFour++;
numberOfConsecutiveEmptySeats = 0;
}
}
}
return numberOfAvailablePlacesForAFamilyOfFour;
}
static void Main(string[] args)
{
int familyPlans = GetNumberOfAvailablePlacesForAFamilyOfFour(2, new string[] { "1A", "2F", "1C" });
}
Good luck on your interview
As always, you will be asked how could you improve that? So you'd consider complexity stuff like O(N), O(wtf).
Underlying implementation would always need for or foreach. Just importantly, never do unnecessary in a loop. For example, if there's only 3 seats left in a row, you don't need to keep hunting on that row because it is not possible to find any.
This might help a bit:
var n = 2;
var m = new string[] { "1A", "2F", "1C" };
// We use 2 dimension bool array here. If it is memory constraint, we can use BitArray.
var seats = new bool[n, 10];
// If you just need the count, you don't need a list. This is for returning more information.
var results = new List<object>();
// Set reservations.
foreach (var r in m)
{
var row = r[0] - '1';
// If it's after 'H', then calculate index based on 'J'.
// 8 is index of J.
var col = r[1] > 'H' ? (8 + r[1] - 'J') : r[1] - 'A';
seats[row, col] = true;
}
// Now you should all reserved seats marked as true.
// This is O(N*M) where N is number of rows, M is number of columns.
for (int row = 0; row < n; row++)
{
int start = -1;
int length = 0;
for (int col = 0; col < 10; col++)
{
if (start < 0)
{
if (!seats[row, col])
{
// If there's no consecutive seats has started, and current seat is available, let's start!
start = col;
length = 1;
}
}
else
{
// If have started, check if we could have 4 seats.
if (!seats[row, col])
{
length++;
if (length == 4)
{
results.Add(new { row, start });
start = -1;
length = 0;
}
}
else
{
// // We won't be able to reach 4 seats, so reset
start = -1;
length = 0;
}
}
if (start < 0 && col > 6)
{
// We are on column H now (only have 3 seats left), and we do not have a consecutive sequence started yet,
// we won't be able to make it, so break and continue next row.
break;
}
}
}
var solution = results.Count;
LINQ, for and foreach are similar things. It is possible you could wrap the above into a custom iterator like:
class ConsecutiveEnumerator : IEnumerable
{
public IEnumerator GetEnumerator()
{
}
}
Then you could start using LINQ.
If you represent your matrix in simple for developers format, it will be easier. You can accomplish it either by dictionary or perform not so complex mapping by hand. In any case this will calculate count of free consecutive seats:
public static void Main(string[] args)
{
var count = 0;//total count
var N = 2; //rows
var M = 10; //columns
var familySize = 4;
var matrix = new []{Tuple.Create(0,0),Tuple.Create(1,5), Tuple.Create(0,2)}.OrderBy(x=> x.Item1).ThenBy(x=> x.Item2).GroupBy(x=> x.Item1, x=> x.Item2);
foreach(var row in matrix)
{
var prevColumn = -1;
var currColumn = 0;
var free = 0;
var div = 0;
//Instead of enumerating entire matrix, we just calculate intervals in between reserved seats.
//Then we divide them by family size to know how many families can be contained within
foreach(var column in row)
{
currColumn = column;
free = (currColumn - prevColumn - 1)/familySize;
count += free;
prevColumn = currColumn;
}
currColumn = M;
free = (currColumn - prevColumn - 1)/familySize;
count += free;
}
Console.WriteLine("Result: {0}", count);
}

Sorting an array of enumeration

Hello I have an array of enumerations, and I'm trying sort that array by their enumeration value and get the array index's of the top
private enum Values
{
LOW,
MEDIUM,
HIGH
}
private Values[] Settings = new Values[10];
Settings[0] = LOW;
Settings[1] = HIGH;
Settings[2] = MEDIUM;
Settings[3] = LOW;
Settings[4] = LOW;
Settings[5] = LOW;
Settings[6] = LOW;
Settings[7] = LOW;
Settings[8] = MEDIUM;
Settings[9] = MEDIUM;
Basically now, with what I have above, I need to sort the settings array by enumeration value and get the array indexes of the top (let's say 3) items;
So I'd get back values 1, 2, 8
The platform I'm using does not support LINQ so those handy functions are not available.
I've been trying to wrap my head around this but it would help to have another pair of eyes.
thanks.
Implement a wrapper reference type,
class ValueWrapper : IComparable<ValueWrapper>
{
public Values Value { get; set; }
public int Index { get; set; }
public int CompareTo(ValueWrapper other)
{
return this.Value.CompareTo(other.Value) * -1; // Negating since you want reversed order
}
}
Usage -
ValueWrapper[] WrappedSettings = new ValueWrapper[10];
for(int i = 0; i < WrappedSettings.Length; i++)
{
WrappedSettings[i] = new ValueWrapper { Value = Settings[i], Index = i };
}
Array.Sort(WrappedSettings);
WrappedSettings will be sorted as you specified, preserving the indexes they were in the original array.
how about this:
Values first = Values.Low,second = Values.Low,third = Values.Low;
int firstI = -1,secondI = -1, thirdI = -1;
for(int i = 0;i < Settings.Length;i++)
{
if(Settings[i] > first || firstI == -1)
{
third = second;
thirdI = secondI;
second= first;
secondI= firstI;
first = Settings[i];
firstI = i;
}
else if(Settings[i] > second || secondI == -1)
{
third = second;
thirdI = secondI;
second = Settings[i];
secondI = i;
}
else if(Settings[i] > third || thirdI == -1)
{
third = Settings[i];
thirdI = i;
}
}
So, since you say you work in an environment where Linq is not available, I assume that other things like generics, nullables and so on will not be available either. A very low-tech solution.
Basic idea:
For every possible enum value, from highest to lowest, go through the list. If we find that value, output it and remember how many we have output. If we reach 3, stop the algorithm.
So, we first look for HIGH in the list, then for MEDIUM and so on.
class Program
{
private enum Values
{
LOW,
MEDIUM,
HIGH
}
static void Main(string[] args)
{
// Sample data
Values[] settings = new Values[10];
settings[0] = Values.LOW;
settings[1] = Values.HIGH;
settings[2] = Values.MEDIUM;
settings[3] = Values.LOW;
settings[4] = Values.LOW;
settings[5] = Values.LOW;
settings[6] = Values.LOW;
settings[7] = Values.LOW;
settings[8] = Values.MEDIUM;
settings[9] = Values.MEDIUM;
// Get Values of the enum type
// This list is sorted ascending by value but may contain duplicates
Array enumValues = Enum.GetValues(typeof(Values));
// Number of results found so far
int numberFound = 0;
// The enum value we used during the last outer loop, so
// we skip duplicate enum values
int lastValue = -1;
// For each enum value starting with the highest to the lowest
for (int i= enumValues.Length -1; i >= 0; i--)
{
// Get this enum value
int enumValue = (int)enumValues.GetValue(i);
// Check whether we had the same value in the previous loop
// If yes, skip it.
if(enumValue == lastValue)
{
continue;
}
lastValue = enumValue;
// For each entry in the list where we are searching
for(int j=0; j< settings.Length; j++)
{
// Check to see whether it is the currently searched value
if (enumValue == (int)settings[j])
{
// if yes, then output it.
Console.WriteLine(j);
numberFound++;
// Stop after 3 found entries
if (numberFound == 3)
{
goto finished;
}
}
}
}
finished:
Console.ReadLine();
}
}
Output is as requested 1,2,8
I'm not sure if this is exactly what you want because it doesn't sort the original array, but one way to get the indexes of the top three values is to simply store the indexes of the top values in another array. Then we can loop through the original array, and for each item, see if it's larger than any of the items at the indexes we've stored so far. If it is, then swap it with that item.
For example:
// Start the topIndexes array with all invalid indexes
var topIndexes = new[] {-1, -1, -1};
for (var settingIndex = 0; settingIndex < Settings.Length; settingIndex++)
{
var setting = Settings[settingIndex];
var topIndexLessThanSetting = -1;
// Find the smallest topIndex setting that's less than this setting
for (int topIndex = 0; topIndex < topIndexes.Length; topIndex++)
{
if (topIndexes[topIndex] == -1)
{
topIndexLessThanSetting = topIndex;
break;
}
if (setting <= Settings[topIndexes[topIndex]]) continue;
if (topIndexLessThanSetting == -1 ||
Settings[topIndexes[topIndex]] < Settings[topIndexes[topIndexLessThanSetting]])
{
topIndexLessThanSetting = topIndex;
}
}
topIndexes[topIndexLessThanSetting] = settingIndex;
}
// topIndexes = { 1, 2, 8 }

I need to find the max value in a array with C#

I have a multidimensional array with five countries and some medals. The last column is the total and i need to return the country with most medals. My code is "working" as long as there are no two countries with the same number of medals and those being the max number.
Problably i just don't have enough experience with arrays, but how can i return more than one contry, if i need?
string [,] medals = new string[5, 5];
Random rdn = new Random();
medals[0, 0] = "Brazil";
medals[1, 0] = "USA";
medals[2, 0] = "Russia";
medals[3, 0] = "France";
medals[4, 0] = "Egypt";
for (int i = 0; i < medals.GetLength(0); i++)
for (int j = 1; j < medals.GetLength(1); j++)
{
//This fills the gold, silver and copper medals
if (j < medals.GetLength(1)-1)
medals[i, j] = rdn.Next(1, 6).ToString();
else
//This fills the total medals
medals[i, j] = (int.Parse(medals[i, 1]) + int.Parse(medals[i,2]) + int.Parse(medals[i,3])).ToString();
}
//The first index stores the max number so far and the second stores the name of the country
string [] winner = { "0", "0" };
for (int i = 0; i < medals.GetLength(0); i++)
{
if (int.Parse(medals[i,4]) > int.Parse(winner[0]))
{
winner[0] = medals[i, 4];
winner[1] = medals[i, 0];
}
}
You could use a list to store the winners.
var listWinners = new List<KeyValuePair<string,string>>();
for (int i = 0; i < medals.GetLength(0); i++)
{
if ((listWinners.Count() == 0)||(int.Parse(medals[i,4]) > int.Parse(listWinners.First().Value)))
{
listWinners.Clear();
listWinners.Add(new KeyValuePair<string,string>(medals[i,0],medals[i,4]));
}
else if (int.Parse(medals[i,4]) == int.Parse(listWinners.First().Value))
{
listWinners.Add(new KeyValuePair<string,string>(medals[i,0],medals[i,4]));
}
}
But I would like reiterate what has been said in the comments, it would be much better if you could use a List of Object rather than multi-dimentional array. It would increase your readability of code and make things much simpler.
A dictionary would be a much easier starting place. You could then just sort based on the values in the dict and use a simple loop to check for duplicate values.
There are a lot of things I would do differently than you, but that's besides the point (see note at the end). Your issue is how to deal with the situation where you have multiple countries with equal and max number of medals.
How to return multiple countries?
Well, that's easy: imagine you return the winner from the below function:
string[] GetWinner() {
// your code
return new[] { "20", "USA" };
}
Then there is no change in the data type, simply add another country to the array and the calling code should check how many you have
string[] GetWinner() {
// your code
return new[] { "20", "USA", "CAN" };
}
Now if you have a problem as to how to create an array whose size is unknown while creating it, then the usual solution is to use List<>, which in this case will act as a re-sizable array for you.
The final code would look something like below
static string[] GetWinner(string[,] medals) {
int rows = medals.GetLength(0);
var winners = new List<string>();
int maxMedals = 0;
for (var i = 0; i < rows; i++) {
var n = int.Parse(medals[i, 4]);
maxMedals = n > maxMedals ? n : maxMedals;
}
for (var i = 0; i < rows; i++) {
var n = int.Parse(medals[i, 4]);
if (n == maxMedals)
winners.Add(medals[i, 0]);
}
winners.Insert(0, maxMedals.ToString());
return winners.ToArray();
}
Now, I would advise you to use a better data structure to store your medals. But since I don't know the full situation, I would guess, something like below is reasonable. Instead of using named types, you can use ValueTuple - to make the code concise, if the code related to these objects are restricted to a small area of a the code file. This should avoid allocating big chunk of memory associated with 2D arrays, and also storing of int in string.
public class CountryMedalTally {
public string Country { get; set; }
public string[] Medals { get; set; }
public int MedalCount { get; set; }
}
public class Winner {
public List<string> Country { get; set; }
public int MedalCount { get; set; }
}
List<CountryMedalTally> AllMedals;

C#: Dice Permutation without Repetition

How can I change my C# code below to list all possible permutations without repetitions? For example: The result of 2 dice rolls would produce 1,1,2 so that means 2,1,1 should not appear.
Below is my code:
string[] Permutate(int input)
{
string[] dice;
int numberOfDice = input;
const int diceFace = 6;
dice = new string[(int)Math.Pow(diceFace, numberOfDice)];
int indexNumber = (int)Math.Pow(diceFace, numberOfDice);
int range = (int)Math.Pow(diceFace, numberOfDice) / 6;
int diceNumber = 1;
int counter = 0;
for (int i = 1; i <= indexNumber; i++)
{
if (range != 0)
{
dice[i - 1] += diceNumber + " ";
counter++;
if (counter == range)
{
counter = 0;
diceNumber++;
}
if (i == indexNumber)
{
range /= 6;
i = 0;
}
if (diceNumber == 7)
{
diceNumber = 1;
}
}
Thread.Sleep(1);
}
return dice;
}
The simplest possible way I could think of:
List<string> dices = new List<string>();
for (int i = 1; i <= 6; i++)
{
for (int j = i; j <= 6; j++)
{
for (int k = j; k <= 6; k++)
{
dices.Add(string.Format("{0} {1} {2}", i, j, k));
}
}
}
I have written a class to handle common functions for working with the binomial coefficient, which is the type of problem that your problem falls under. It performs the following tasks:
Outputs all the K-indexes in a nice format for any N choose K to a file. The K-indexes can be substituted with more descriptive strings or letters. This method makes solving this type of problem quite trivial.
Converts the K-indexes to the proper index of an entry in the sorted binomial coefficient table. This technique is much faster than older published techniques that rely on iteration. It does this by using a mathematical property inherent in Pascal's Triangle. My paper talks about this. I believe I am the first to discover and publish this technique, but I could be wrong.
Converts the index in a sorted binomial coefficient table to the corresponding K-indexes.
Uses Mark Dominus method to calculate the binomial coefficient, which is much less likely to overflow and works with larger numbers.
The class is written in .NET C# and provides a way to manage the objects related to the problem (if any) by using a generic list. The constructor of this class takes a bool value called InitTable that when true will create a generic list to hold the objects to be managed. If this value is false, then it will not create the table. The table does not need to be created in order to perform the 4 above methods. Accessor methods are provided to access the table.
There is an associated test class which shows how to use the class and its methods. It has been extensively tested with 2 cases and there are no known bugs.
To read about this class and download the code, see Tablizing The Binomial Coeffieicent.
I'm bad at math as well, this may or may not be helpful...
Program.cs
namespace Permutation
{
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Generating list.");
var dice = new List<ThreeDice>();
for (int x = 1; x <= 6; x++)
{
for (int y = 1; y <= 6; y++)
{
for (int z = 1; z <= 6; z++)
{
var die = new ThreeDice(x, y, z);
if (dice.Contains(die))
{
Console.WriteLine(die + " already exists.");
}
else
{
dice.Add(die);
}
}
}
}
Console.WriteLine(dice.Count + " permutations generated.");
foreach (var die in dice)
{
Console.WriteLine(die);
}
Console.ReadKey();
}
}
}
ThreeDice.cs
namespace Permutation
{
using System;
using System.Collections.Generic;
public class ThreeDice : IEquatable<ThreeDice>
{
public ThreeDice(int dice1, int dice2, int dice3)
{
this.Dice = new int[3];
this.Dice[0] = dice1;
this.Dice[1] = dice2;
this.Dice[2] = dice3;
}
public int[] Dice { get; private set; }
// IEquatable implements this method. List.Contains() will use this method to see if there's a match.
public bool Equals(ThreeDice other)
{
// Get the current dice values into a list.
var currentDice = new List<int>(this.Dice);
// Check to see if the same values exist by removing them one by one.
foreach (int die in other.Dice)
{
currentDice.Remove(die);
}
// If the list is empty, we have a match.
return currentDice.Count == 0;
}
public override string ToString()
{
return "<" + this.Dice[0] + "," + this.Dice[1] + "," + this.Dice[2] + ">";
}
}
}
Good luck.
The important part of the question is that you want distinct sets (regardless of order). So for example, a dice roll of [1, 2, 1] is equal to a dice roll of [1, 1, 2].
I'm sure there are a number of ways to skin this cat, but the first thought that comes to mind is to create a EqualityComparer which will compare the list of dice in the way you want, and then use LINQ with the Distinct() method.
Here is the EqualityComparer, which takes 2 List<int> and says they are equal if the elements are all equal (regardless of order):
private class ListComparer : EqualityComparer<List<int>>
{
public override bool Equals(List<int> x, List<int> y)
{
if (x.Count != y.Count)
return false;
x.Sort();
y.Sort();
for (int i = 0; i < x.Count; i++)
{
if (x[i] != y[i])
return false;
}
return true;
}
public override int GetHashCode(List<int> list)
{
int hc = 0;
foreach (var i in list)
hc ^= i;
return hc;
}
}
And here is the code that uses it. I'm using LINQ to build up the list of all combinations... you could also do this with nested for loops but I like this better for some reason:
public static void Main()
{
var values = new[] { 1,2,3,4,5,6 };
var allCombos = from x in values
from y in values
from z in values
select new List<int>{ x, y, z };
var distinctCombos = allCombos.Distinct(new ListComparer());
Console.WriteLine("#All combos: {0}", allCombos.Count());
Console.WriteLine("#Distinct combos: {0}", distinctCombos.Count());
foreach (var combo in distinctCombos)
Console.WriteLine("{0},{1},{2}", combo[0], combo[1], combo[2]);
}
Hope that helps!
Here is generic c# version using recursion (basically the recursive method takes number of dices or number of times the dice has been tossed) and returns all the combinations strings ( for ex, for '3' as per the question - there will be 56 such combinations).
public string[] GetDiceCombinations(int noOfDicesOrnoOfTossesOfDice)
{
noOfDicesOrnoOfTossesOfDice.Throw("noOfDicesOrnoOfTossesOfDice",
n => n <= 0);
List<string> values = new List<string>();
this.GetDiceCombinations_Recursive(noOfDicesOrnoOfTossesOfDice, 1, "",
values);
return values.ToArray();
}
private void GetDiceCombinations_Recursive(int size, int index, string currentValue,
List<string> values)
{
if (currentValue.Length == size)
{
values.Add(currentValue);
return;
}
for (int i = index; i <= 6; i++)
{
this.GetDiceCombinations_Recursive(size, i, currentValue + i, values);
}
}
Below are corresponding tests...
[TestMethod]
public void Dice_Tests()
{
int[] cOut = new int[] { 6, 21, 56, 126 };
for(int i = 1; i<=4; i++)
{
var c = this.GetDiceCombinations(i);
Assert.AreEqual(cOut[i - 1], c.Length);
}
}

Categories