Is there a standard algorithm to balance overlapping objects into buckets? - c#

I have a bunch of users, with a given start and end time, e.g.:
{ Name = "Peter", StartTime = "10:30", EndTime = "11:00" },
{ Name = "Dana", StartTime = "11:00", EndTime = "12:30" },
{ Name = "Raymond", StartTime = "10:30", EndTime = "14:00" },
{ Name = "Egon", StartTime = "12:00", EndTime = "13:00" },
{ Name = "Winston", StartTime = "10:00", EndTime = "12:00" }
I want to put them into buckets, based on times that they overlap (based on a configurable threshold, e.g., they need to overlap at least half an hour). I want buckets to be ideally 4 items big, but any range from 2-5 is acceptable.
In the example above, no 4 people match, so I'd have a bucket of 3 (Peter, Raymond, Winston) and one of 2 (Dana, Egon).
I've prototyped an algorithm that seems to rely on chance rather than science:
Order the List by StartTime
Create an empty bucket
Pick the first user from the List
Check that user against all users in the bucket
If that user overlaps with everyone in the bucket, put that person in it and remove it from the list
If the bucket has the ideal size (4) or if I'm looping and checking the same user more than three times, close the bucket and create a new, empty one
This works well for the first few buckets, but leads to buckets with only 2 people that could be combined better.
I could change the algorithm to remove all ideal buckets from the list and reshuffle and try some more, but I feel that this should be a common problem - it's like shift assignments for workers, or maybe the knapsack problem.
Does anyone know a standard algorithm for this type of problem?
(Tagged combinatorics because I think this is the area of math it applies, correct me if wrong)

tl;dr: dynamic programming for the win (O(sort(n)) time).
First, note that bucketing contiguously in start-time order is fine.
Proposition (defragging): Let a, b, c, d be distinct users such that StartTime(a) ≤ StartTime(b) ≤ StartTime(c) ≤ StartTime(d). If X and Y are valid buckets such that a, c ∈ X and b, d ∈ Y, then X - {c} ∪ {b} and Y - {a} ∪ {d} are valid buckets as well.
I only know how to prove this by tedious case analysis (omitted).
The upshot is, you can pretend as though you're breaking a paragraph into lines, where the “paragraph“ is the list of users in start-time order, and each “line” is a bucket. There is an algorithm due to Knuth and Plass for line breaking optimally, where the penalty for a given line is a more or less arbitrary function. You could make buckets of 4 cost 0, buckets of 3 cost 1, buckets of 2 cost 2, and buckets of 1 cost 100, for example.

Based on your problem I would probably do something like first making a class called "Person" pr something like that. Give this class attributes of "Name", "Start Time", and "End Time".
class Person
{
public string name;
public double start_time;
public double end_time;
}
Then put them in some ordered list of type Person. (Also I'm currently storing the times as doubles. You can convert them back to times simply by multiplying any decimal part of the time that I have by 60/100.
Afterwords, you make a list of Buckets to which you can add new Buckets if need be. You then sort the list based on a threshold which you define and if two objects being compared overlap based on that threshold, then those both go into that Bucket. If they don't overlap, then move to the next Bucket, if there is an overlap there then add it to that Bucket, etc until you reach the last Bucket. If you have gone through all the Buckets and there is still no overlap, then create a new Bucket for that object.
class MainFunc
{
static void Main(string[] args)
{
//makes a function to check if 2 values overlap over a threshold
//st stands for start time and et stands for end time
public bool IsWithinThreshold(double st1, double st2, double et1, double et2)
{
double threshold = .5;
if(st1 >= et2 || st2 >= et1)
{
return false
}
else
{
if(st1+threshold <= et2 && st1+threshold <= et1 || st2+threshold <= et1 && st2+threshold <=et2)
{
return true;
}
else
{
return false;
}
}
}
// makes objects of type Person with attributes of name, start time, and end time
Person Peter = new Person();
Peter.name = "Peter"
Peter.start_time = 10.5
Peter.end_time = 11.0
Person Dana = new Person();
Dana.name = "Dana"
Peter.start_time = 11.0
Peter.end_time = 12.5
Person Raymond = new Person();
Raymond.name = "Raymond"
Raymond.start_time = 10.5
Raymond.end_time = 14.0
Person Egon = new Person();
Egon.name = "Egon"
Egon.start_time = 12.0
Egon.end_time = 13.0
Person Winston = new Person();
Winston.name = "Winston"
Winston.start_time = 10.0
Winston.end_time = 12.0
//puts objects of type Person into an unordered list
List<Person> people = new List<Person>();
people.Add(Peter);
people.Add(Dana);
people.Add(Raymond);
people.Add(Egon);
people.Add(Winston);
//sets up a list of lists of People (Buckets in our case)
List<List<Person>> Buckets = new List<List<Person>>;
//sets up an intial Bucket and adds the first person on the list to it
List<Person> Bucketinitial = new List<Person>;
Bucketinitial.add(people[0]);
for(var i = 1; i < people.Count; i++)
{
for(var j = 0; j< Buckets.count; j++)
{
//sets a checker to make sure that all objects in a given Bucket overlap with the person we are checking
bool overlap = true;
for(var k = 0; k< Buckets[k].count; k++)
{
overlap = overlap & IsWithinThreshold(people[i].start_time,Buckets[j][k].start_time,people[i].end_time,Buckets[j][k].end_time)
}
if (overlap == true)
{
Buckets[j].add(people[i])
}
//if all the objects in a bucket don't overlap with the person...
//... make a new Bucket with that person
else
{
List<Person> NewBucket = new List<Person>;
NewBucket.add(people[i]);
Buckets.add(NewBucket);
}
}
}
}
}
Then just add a print command to print out the name attributes of each object within each Bucket of the buckets list. Please leave a comment if you have any questions/concerns, cheers.

You can modify your algorithm to incorporate an interval tree to speed up the search
Order the List by StartTime
Add items to an interval tree
Create an empty bucket
Pick the first item from the List
Find earliest items within threshold time of first item that fill up a bucket using an interval search of the interval tree
Remove bucketed items from list
If list is empty stop otherwise go to step 4
Basically you're moving from left to right in interval steps (given by your configurable threshold) using the interval tree to quickly query for closest items as you move.

Related

Genetic Algorithm stops mutating

I'm currently trying to make my genetic algorithm "generate" or "evolve" towards an given word. The problem is, that it never fully reaches this word, it stops at an too high fitness score, even if it should continue mutating.
Heres an example:
User input = "HelloWorld"
After 500 generations = "XelgoWorfd"
And I have no clue why it won't continue mutating. Normally it just should resume with changing randomly some chars in the string.
So I would be very glad about some help.
Here's an basic step by step explanation:
Create 20 Chromosomes with fully randomized strings
Calculate the fitness score compared to the goal word.
(Counting Ascii ids differences)
Mate the two Chromosomes with the best score.
Mutate some of the Chromosomes randomly (change random string chars)
Kill 90% of the weak population and replace it with elite chromosomes (The chromosomes with the currently best fitness score).
Repeat everything.
So here the most important methods of my algorithm:
public Chromoson[] mate(string gene) {
Console.WriteLine("[MATING] In Progress : "+gens+" "+gene);
int pivot = (int)Math.Round((double)gens.Length / 2) - 1;
string child1 = this.gens.Substring(0, pivot) + gene.Substring(pivot);
string child2 = gene.Substring(0, pivot) + this.gens.Substring(pivot);
Chromoson[] list = new Chromoson[2];
list[0] = new Chromoson(child1);
list[1] = new Chromoson(child2);
Console.WriteLine("[MATING] Pivot : "+pivot);
Console.WriteLine("[MATING] Children : "+child1+" "+child2);
return list;
}
public void mutate(float chance, int possiblyChanges) {
if (random.Next(0,101) <= chance) return;
int changes = random.Next(0, possiblyChanges + 1);
//int index = (int) Math.Floor((double)random.Next() * this.gens.Length);
for (int i = 0; i < changes; i++) {
int index = random.Next(0, 13);
StringBuilder builder = new StringBuilder(gens);
int upOrDown = random.Next(0, 101);
if (upOrDown <= 50 && (int)builder[index] > 0 && chars.Contains(Convert.ToChar(builder[index] - 1)))
builder[index] = Convert.ToChar(builder[index] - 1);
else if (upOrDown >= 50 && (int)builder[index] < 127 && chars.Contains(Convert.ToChar(builder[index] + 1)))
builder[index] = Convert.ToChar(builder[index] + 1);
else
mutate(chance, possiblyChanges);
gens = builder.ToString();
}
Console.WriteLine("[MUTATING] In Progress");
}
public void calculateCost(string otherGens)
{
int total = 0;
for (int i = 0; i < gens.Length; i++)
{
total += (((int)gens[i] - (int)otherGens[i]) * ((int)gens[i] - (int)otherGens[i])) * (i*i);
}
Console.WriteLine("[CALCULATING] Costs : " + total);
this.cost = total;
}
Something is completely off in your timesteps:
Create 20 Chromosomes with fully randomized strings. Seems okay.
Calculate the fitness score compared to the goal word. (Counting Ascii ids differences). Seems okay.
Mate the two Chromosomes with the best score. What? Your only breeding the two fittest chromosomes to create the new population? That means you will have a population that is nearly completely similar. Breedfitness proportionally, so all genomes have a chance to have an offspring
Mutate some of the Chromosomes randomly (change random string chars)
Kill 90% of the weak population and replace it with elite chromosomes (The chromosomes with the currently best fitness score). You kill 90%? So basically, you're keeping the 2 best genomes every iteration and then replacing the other 18 with step 1? What you want is to keep the 2 fittest at step 3, and create the other 18 individuals by breeding.
Repeat everything.
So change your steps to:
INIT. Initialise population, create 20 random chromosomes
Calculate score for each chromsome
Save the two fittest chromosomes to the next population (aka elitism), getthe other 18 needed individuals by breeding fitness proportionally
Mutate the chromsomes with a certain chance
Repeat
Do not create random individuals every round. This turns your algorithm into a random search.
Your mutate and calculateCost functions are weird. In particular, mutate() looks designed to get trapped in local minimas. Any mutation up or down will be worse than the elites (which are probably identical so crossover changes nothing). Use a different mutate: Pick a random index and change it completely. Also remove i*i from cost().

C# and "Weighted probability" [duplicate]

This question already has answers here:
Random weighted choice
(12 answers)
Closed 6 years ago.
I am looking to pick 1 person from a list of people where each item in the list has a certain "weighting" to it. Let's assume the Person class has the necessary constructor.
public class Person {
public string Name { get; set; }
public float Weighting { get; set; }
}
public List<Person> People = new List<Person>();
People.Add(new Person("Tim", 1.0));
People.Add(new Person("John", 2.0));
People.Add(new Person("Michael", 4.0));
Now, I want to choose a person randomly from this list. But on average I want to pick Michael 4 times more often than Tim. And I want to pick John half (2/4) as often as Michael. And of course, I want to pick Michael double as often as John.
Does that make any sense?
I already have code in place to select people based on percentages. Wouldn't it work if I just multiplied the %chance of them by the weighting provided in this example?
Also, my current system only works with chances of up to 100%, nothing above. Any advice on how to overcome this limitation? I would probably have to scale every chance according to the largest factor in the list?
public static bool Hit(double pct) {
if (rand == null)
rand = new Random();
return rand.NextDouble() * 100 <= pct;
}
Or am I missing something?
You are not keeping percentages to start with.
I would create a random number in the range of 0 to the sum of all Weighting values. Then just walk over the list and check if the value is lower than the current plus own weight.
So:
float r = YourRandomNumber(People.Sum(p => p.Weighting));
float sum = 0;
foreach (Person p in People)
{
if (r < (sum + p.Weighting))
{
// hit
// save the person somewhere
break;
}
else
{
sum += p.Weighting;
}
}

Which crossing over method is best to give us quick changes in best values for TSP in GA?

I am trying to solve Travelling Salesman Problem using Genetic Algorithym in C#. But in my app best values changes so slowly. I have tried with different Crossing-Over methods such as classic, greedy and pmx but I have never got what I want. What is the most effective reason that causes slow approximation to local minimum in Genetic Algorithyms? Isn't it Crossing-Over methods?
I think my method for CO is correct, isn't it?.
My code:
Tour ClassicCrossingOver(Tour mother, Tour father)
{
int pos = N / 2;
City[] gens = new City[N];
for (int i = 0; i < pos; i++)
{
gens[i] = mother.Cities[i];
}
List<int> nonPos = new List<int>(); //Handles duplicate city positions
for (int i = pos; i < gens.Length; i++)
{
if (gens.Contains(father.Cities[i]))
nonPos.Add(i);
gens[i] = father.Cities[i];
}
List<City> noneGenes = new List<City>(); //Handles cities that doesnt exists in the child
foreach (City gene in map.Cities)
{
if (gens.Contains(gene)) continue;
noneGenes.Add(gene);
}
for (int i = 0; i < noneGenes.Count; i++)
{
int j = rnd.Next(nonPos.Count - 1);
gens[nonPos[j]] = noneGenes[i];
nonPos.RemoveAt(j);
}
Tour tur = new Tour(map) { Cities = gens };
return tur;
}
A crossover often called 'Double Point Ordered' can be very useful here. This type of crossover creates a child from a single parent. Two parents are selected and two random points along the chromosome are selected. The genes between the points are passed to the child. The remaining genes are transferred from the same parent, but in the order that they appear in the second parent. The result is that the child contains all of the values from a single parent but includes ordering, and therefore traits, from both parents.
I have a couple of examples of the TSP here which may help
http://johnnewcombe.net/blog/gaf-part-4/
http://johnnewcombe.net/blog/gaf-part-7/
In my experience using GA, Ordered Crossover (OX1) is one of the best crossover operators to solve TSP.
OX1:
A randomly selected portion of one parent is mapped to a portion
of the other parent. From the replaced portion on, the rest is filled
up by the remaining genes, where already present genes are omitted and
the order is preserved.
Other operators can influence the speed that GA reach best values. I usually use the operators bellow in Traveling Salesman Problem:
Crossover: Ordered Crossover (OX1)
Mutation: Reverse Sequence Mutation
Selection: Elite Selection
This is a sample code to solve TSP, using GeneticSharp, that found a good solution in a few seconds:
var numberOfCities = 20;
var selection = new EliteSelection();
var crossover = new OrderedCrossover();
var mutation = new ReverseSequenceMutation();
var fitness = new TspFitness(numberOfCities, 0, 1000, 0, 1000);
var chromosome = new TspChromosme(numberOfCities);
var population = new Population (100, 200, chromosome);
var ga = new GeneticAlgorithm(population, fitness, selection, crossover, mutation);
ga.Termination = new GenerationNumberTermination(100);
Console.WriteLine("GA running...");
ga.Start();
Console.WriteLine("Best solution found has {0} fitness.", ga.BestChromosome.Fitness);
You can see TspChromosome implementation at TspChromosome.cs and TspFitness at TspFitness.cs.

calculate average function of several functions

I have several ordered List of X/Y Pairs and I want to calculate a ordered List of X/Y Pairs representing the average of these Lists.
All these Lists (including the "average list") will then be drawn onto a chart (see example picture below).
I have several problems:
The different lists don't have the same amount of values
The X and Y values can increase and decrease and increase (and so on) (see example picture below)
I need to implement this in C#, altought I guess that's not really important for the algorithm itself.
Sorry, that I can't explain my problem in a more formal or mathematical way.
EDIT: I replaced the term "function" with "List of X/Y Pairs" which is less confusing.
I would use the method Justin proposes, with one adjustment. He suggests using a mappingtable with fractional indices, though I would suggest integer indices. This might sound a little mathematical, but it's no shame to have to read the following twice(I'd have to too). Suppose the point at index i in a list of pairs A has searched for the closest points in another list B, and that closest point is at index j. To find the closest point in B to A[i+1] you should only consider points in B with an index equal to or larger than j. It will probably by j + 1, but could be j or j + 2, j + 3 etc, but never below j. Even if the point closest to A[i+1] has an index smaller than j, you still shouldn't use that point to interpolate with, since that would result in an unexpected average and graph. I'll take a moment now to create some sample code for you. I hope you see that this optimalization makes sense.
EDIT: While implementing this, I realised that j is not only bounded from below(by the method described above), but also bounded from above. When you try the distance from A[i+1] to B[j], B[j+1], B[j+2] etc, you can stop comparing when the distance A[i+1] to B[j+...] stops decreasing. There's no point in searching further in B. The same reasoning applies as when j was bounded from below: even if some point elsewhere in B would be closer, that's probably not the point you want to interpolate with. Doing so would result in an unexpected graph, probably less smooth than you'd expect. And an extra bonus of this second bound is the improved performance. I've created the following code:
IEnumerable<Tuple<double, double>> Average(List<Tuple<double, double>> A, List<Tuple<double, double>> B)
{
if (A == null || B == null || A.Any(p => p == null) || B.Any(p => p == null)) throw new ArgumentException();
Func<double, double> square = d => d * d;//squares its argument
Func<int, int, double> euclidianDistance = (a, b) => Math.Sqrt(square(A[a].Item1 - B[b].Item1) + square(A[a].Item2 - B[b].Item2));//computes the distance from A[first argument] to B[second argument]
int previousIndexInB = 0;
for (int i = 0; i < A.Count; i++)
{
double distance = euclidianDistance(i, previousIndexInB);//distance between A[i] and B[j - 1], initially
for (int j = previousIndexInB + 1; j < B.Count; j++)
{
var distance2 = euclidianDistance(i, j);//distance between A[i] and B[j]
if (distance2 < distance)//if it's closer than the previously checked point, keep searching. Otherwise stop the search and return an interpolated point.
{
distance = distance2;
previousIndexInB = j;
}
else
{
break;//don't place the yield return statement here, because that could go wrong at the end of B.
}
}
yield return LinearInterpolation(A[i], B[previousIndexInB]);
}
}
Tuple<double, double> LinearInterpolation(Tuple<double, double> a, Tuple<double, double> b)
{
return new Tuple<double, double>((a.Item1 + b.Item1) / 2, (a.Item2 + b.Item2) / 2);
}
For your information, the function Average returns the same amount of interpolated points the list A contains, which is probably fine, but you should think about this for your specific application. I've added some comments in it to clarify some details, and I've described all aspects of this code in the text above. I hope it's clear, and otherwise feel free to ask questions.
SECOND EDIT: I misread and thought you had only two lists of points. I have created a generalised function of that above accepting multiple lists. It still uses only those principles explained above.
IEnumerable<Tuple<double, double>> Average(List<List<Tuple<double, double>>> data)
{
if (data == null || data.Count < 2 || data.Any(list => list == null || list.Any(p => p == null))) throw new ArgumentException();
Func<double, double> square = d => d * d;
Func<Tuple<double, double>, Tuple<double, double>, double> euclidianDistance = (a, b) => Math.Sqrt(square(a.Item1 - b.Item1) + square(a.Item2 - b.Item2));
var firstList = data[0];
for (int i = 0; i < firstList.Count; i++)
{
int[] previousIndices = new int[data.Count];//the indices of points which are closest to the previous point firstList[i - 1].
//(or zero if i == 0). This is kept track of per list, except the first list.
var closests = new Tuple<double, double>[data.Count];//an array of points used for caching, of which the average will be yielded.
closests[0] = firstList[i];
for (int listIndex = 1; listIndex < data.Count; listIndex++)
{
var list = data[listIndex];
double distance = euclidianDistance(firstList[i], list[previousIndices[listIndex]]);
for (int j = previousIndices[listIndex] + 1; j < list.Count; j++)
{
var distance2 = euclidianDistance(firstList[i], list[j]);
if (distance2 < distance)//if it's closer than the previously checked point, keep searching. Otherwise stop the search and return an interpolated point.
{
distance = distance2;
previousIndices[listIndex] = j;
}
else
{
break;
}
}
closests[listIndex] = list[previousIndices[listIndex]];
}
yield return new Tuple<double, double>(closests.Select(p => p.Item1).Average(), closests.Select(p => p.Item2).Average());
}
}
Actually that I did the specific case for 2 lists separately might have been a good thing: it is easily explained and offers a step before understanding the generalised version. Furthermore, the square root could be taken out, since it doesn't change the order of the distances when sorted, just the lengths.
THIRD EDIT: In the comments it became clear there might be a bug. I think there are none, aside from the mentioned small bug, which shouldn't make any difference except for at the end of the graphs. As a proof that it actually works, this is the result of it(the dotted line is the average):
I'll use a metaphor of your functions being cars racing down a curvy racetrack, where you want to extract the center-line of the track given the cars' positions. Each car's position can be described as a function of time:
p1(t) = (x1(t), y1(t))
p2(t) = (x2(t), y2(t))
p3(t) = (x3(t), y3(t))
The crucial problem is that the cars are racing at different speeds, which means that p1(10) could be twice as far down the race track as p2(10). If you took a naive average of these two points, and there was a sharp curve in the track between the cars, the average may be far from the track.
If you could just transform your functions to no longer be a function of time, but a function of the distance along the track, then you would be able to do what you want.
One way you could do this would be to choose the slowest car (i.e., the one with the greatest number of samples). Then, for each sample of the slowest car's position, look at all of the other cars' paths, find the two closest points, and choose the point on the interpolated line which is closest to the slowest car's position. Then average these points together. Once you do this for all of the slow car's samples, you have an average path.
I'm assuming that all of the cars start and end in roughly the same places; if any of the cars just race a small portion of the track, you will need to add some more logic to detect that.
A possible improvement (for both performance and accuracy), is to keep track of the most recent sample you are using for each car and the speed of each car (the relative sampling rate). For your slowest car, it would be a simple map: 1 => 1, 2 => 2, 3 => 3, ... For the other cars, though, it could be more like: 1 => 0.3, 2 => 0.7, 3 => 1.6 (fractional values are due to interpolation). The speed would be the inverse of the change in sample number (e.g., the slow car would have speed 1, and the other car would have speed 1/(1.6-0.7)=1.11). You could then ensure that you don't accidentally backtrack on any of the cars. You could also improve the calculation speed because you don't have to search through the whole set of all points on each path; instead, you can assume that the next sample will be somewhere close to the current sample plus 1/speed.
As these are not y=f(x) functions, are they perhaps something like (x,y)=f(t)?
If so, you could interpolate along t, and calculate avg(x) and avg(y) for each t.
EDIT This of course assumes that t can be made available to your code - so that you have an ordered list of T/X/Y triples.
There are several ways this can be done. One is to combine all of your data into one single set of points, and do a best-fit curve through the combined set.
you have e.g. 2 "functions" with
fc1 = { {1,0.3} {2, 0.5} {3, 0.1} }
fc1 = { {1,0.1} {2, 0.8} {3, 0.4} }
You want the arithmetic mean (slang: "average") of the two functions. To do this you just calculate the pointwise arithmetic mean:
fc3 = { {1, (0.3+0.1)/2} ... }
Optimization:
If you have large numbers of points you should first convert your "ordered List of X/Y Pairs" into a Matrix OR at least store the points column-wise like so:
{0.3, 0.1}, {0.5, 0.8}, {0.1, 0.4}

Help with maths/coding on possible combinations of a set to make up a total - C#

I have a coding/maths problem that I need help translating into C#. It's a poker chip calculator that takes in the BuyIn, the number of players and the total amount of chips for each colour (there are x amount of colours) and their value.
It then shows you every possible combination of chips per person to equal the Buy In. The user can then pick the chipset distribution they would like to use. It's best illustrated with a simple example.
BuyIn: $10
Number of Players: 1
10 Red Chips, $1 value
10 Blue Chips, $2 value
10 Green Chips, $5 value
So, the possible combinations are:
R/B/G
10/0/0
8/1/0
6/2/0
5/0/1
4/3/0
2/4/0
1/2/1
etc.
I have spent a lot of time trying to come up with an algorithm in C#/.NET to work this out. I am stumbling on the variable factor - there's usually only 3 or 4 different chips colours in a set, but there could be any amount. If you have more than one player than you have to count up until TotalChips / NumberOfPlayers.
I started off with a loop through all the chips and then looping from 0 up to NumberOfChips for that colour. And this is pretty much where I have spent the last 4 hours... how do I write the code to loop through x amount of chips and check the value of the sum of the chips and add it to a collection if it equals the BuyIn? I need to change my approach radically methinks...
Can anyone put me on the right track on how to solve this please? Pseudo code would work - thank you for any advice!
The below is my attempt so far - it's hopeless (and wont compile, just an example to show you my thought process so far) - Might be better not to look at it as it might biased you on a solution...
private void SplitChips(List<ChipSuggestion> suggestions)
{
decimal valueRequired = (decimal)txtBuyIn.Value;
decimal checkTotal = 0;
ChipSuggestion suggestion;
//loop through each colour
foreach (Chip chip in (PagedCollectionView)gridChips.ItemsSource)
{
//for each value, loop through them all again
foreach (Chip currentChip in (PagedCollectionView)gridChips.ItemsSource)
{
//start at 0 and go all the way up
for (int i = 0; i < chip.TotalChipsInChipset; i++)
{
checkTotal = currentChip.ChipValue * i;
//if it is greater than than ignore and stop
if (checkTotal > valueRequired)
{
break;
}
else
{
//if it is equal to then this is a match
if (checkTotal == valueRequired)
{
suggestion = new ChipSuggestion();
suggestion.SuggestionName = "Suggestion";
chipRed.NumberPerPlayer = i;
suggestion.Chips.Add(chipRed);
chipBlue.NumberPerPlayer = y;
suggestion.Chips.Add(chipBlue);
chipGreen.NumberPerPlayer = 0;
suggestion.Chips.Add(chipGreen);
//add this to the Suggestion
suggestions.Add(suggestion);
break;
}
}
}
}
}
}
Here's an implementation that reads the number of chips, the chips (their worth and amount) and the buyin and displays the results in your example format. I have explained it through comments, let me know if you have any questions.
class Test
{
static int buyIn;
static int numChips;
static List<int> chips = new List<int>(); // chips[i] = value of chips of color i
static List<int> amountOfChips = new List<int>(); // amountOfChips[i] = number of chips of color i
static void generateSolutions(int sum, int[] solutions, int last)
{
if (sum > buyIn) // our sum is too big, return
return;
if (sum == buyIn) // our sum is just right, print the solution
{
for (int i = 0; i < chips.Count; ++i)
Console.Write("{0}/", solutions[i]);
Console.WriteLine();
return; // and return
}
for (int i = last; i < chips.Count; ++i) // try adding another chip with the same value as the one added at the last step.
// this ensures that no duplicate solutions will be generated, since we impose an order of generation
if (amountOfChips[i] != 0)
{
--amountOfChips[i]; // decrease the amount of chips
++solutions[i]; // increase the number of times chip i has been used
generateSolutions(sum + chips[i], solutions, i); // recursive call
++amountOfChips[i]; // (one of) chip i is no longer used
--solutions[i]; // so it's no longer part of the solution either
}
}
static void Main()
{
Console.WriteLine("Enter the buyin:");
buyIn = int.Parse(Console.ReadLine());
Console.WriteLine("Enter the number of chips types:");
numChips = int.Parse(Console.ReadLine());
Console.WriteLine("Enter {0} chips values:", numChips);
for (int i = 0; i < numChips; ++i)
chips.Add(int.Parse(Console.ReadLine()));
Console.WriteLine("Enter {0} chips amounts:", numChips);
for (int i = 0; i < numChips; ++i)
amountOfChips.Add(int.Parse(Console.ReadLine()));
int[] solutions = new int[numChips];
generateSolutions(0, solutions, 0);
}
}
Enter the buyin:
10
Enter the number of chips types:
3
Enter 3 chips values:
1
2
5
Enter 3 chips amounts:
10
10
10
10/0/0/
8/1/0/
6/2/0/
5/0/1/
4/3/0/
3/1/1/
2/4/0/
1/2/1/
0/5/0/
0/0/2/
Break the problem down recursively by the number of kinds of chips.
For the base case, how many ways are there to make an $X buy-in with zero chips? If X is zero, there is one way: no chips. If X is more than zero, there are no ways to do it.
Now we need to solve the problem for N kinds of chips, given the solution for N - 1. We can take one kind of chip, and consider every possible number of that chip up to the buy-in. For example, if the chip is $2, and the buy-in is $5, try using 0, 1, or 2 of them. For each of these tries, we have to use only the remaining N - 1 chips to make up the remaining value. We can solve that by doing a recursive call, and then adding our current chip to each solution it returns.
private static IEnumerable<IEnumerable<Tuple<Chip, int>>> GetAllChipSuggestions(List<Chip> chips, int players, int totalValue)
{
return GetAllChipSuggestions(chips, players, totalValue, 0);
}
private static IEnumerable<IEnumerable<Tuple<Chip, int>>> GetAllChipSuggestions(List<Chip> chips, int players, int totalValue, int firstChipIndex)
{
if (firstChipIndex == chips.Count)
{
// Base case: we have no chip types remaining
if (totalValue == 0)
{
// One way to make 0 with no chip types
return new[] { Enumerable.Empty<Tuple<Chip, int>>() };
}
else
{
// No ways to make more than 0 with no chip types
return Enumerable.Empty<IEnumerable<Tuple<Chip, int>>>();
}
}
else
{
// Recursive case: try each possible number of this chip type
var allSuggestions = new List<IEnumerable<Tuple<Chip, int>>>();
var currentChip = chips[firstChipIndex];
var maxChips = Math.Min(currentChip.TotalChipsInChipset / players, totalValue / currentChip.ChipValue);
for (var chipCount = 0; chipCount <= maxChips; chipCount++)
{
var currentChipSuggestion = new[] { Tuple.Create(currentChip, chipCount) };
var remainingValue = totalValue - currentChip.ChipValue * chipCount;
// Get all combinations of chips after this one that make up the rest of the value
foreach (var suggestion in GetAllChipSuggestions(chips, players, remainingValue, firstChipIndex + 1))
{
allSuggestions.Add(suggestion.Concat(currentChipSuggestion));
}
}
return allSuggestions;
}
}
For some large combinations this is propably not solvable in finite time.
(It is a NP problem)
http://en.wikipedia.org/wiki/Knapsack_problem
There are also links with Code? that could help you.
Hope this helps a bit.

Categories