Kattis-problem: Building Pyramids - why is this not correct? - c#

I'm trying to solve this problem here in C#:
https://open.kattis.com/submissions/8340306 (edit: Full question below)
So, in short: Give is a number of bricks. The aim is to figure out, how high I can build a 3D-pyramid with these blocks. The top-level has 1 brick, the 2nd level from the top 9 bricks, the 3rd 25 bricks and so on.
I thought I solved it and with the given example it is correct. It is also correct in all the calculations I did. Yet: From the 5 sample-runs, in it only excepted in 3.
Edit - the full question: When initiating a larger project, like
building a pyramid, it’s best to think twice. Your task today is to
write a program that computes how high a pyramid can be built given a
certain number of blocks of stone.
We assume that the pyramid to be built is compact, i.e. there are no
cavities inside. Furthermore, we assume it is built according to the
principle in Figure 1. Each layer is square, with a side length that
is two less than the one below it. The top layer always consist of a
single block.
It is fine if you have leftover blocks, as long as you build a
complete pyramid.
Input The first and only line of input contains an integer N
(1≤N≤100000000), the number of blocks you have available.
Output Output a single integer – the maximum height of a pyramid that
can be built with at least N blocks.
The result of my solution:
Here is my code - please be gentle, I am just learning :)
public static void Main()
{
int bloecke = int.Parse(Console.ReadLine());
int neueBloecke = 1; // Bloecke, die für die neue Ebene benötigt werden
int sumBloecke = 1; // Blöcke in Summe
int seitenLaenge = 1; // Seitenlänge der Ebene
int ebene = 1; // Auf welcher Ebene wir uns aktuell befinden
while (sumBloecke < bloecke)
{
// Wenn wir weniger als 10 Blöcke haben, brauchen wir gar nicht anzufangen > wir haben 1 Ebene
if (bloecke < 10)
{
ebene = 1;
break;
}
// Andernfalls legen wir los
seitenLaenge += 2;
neueBloecke = seitenLaenge * seitenLaenge;
sumBloecke += neueBloecke;
if (sumBloecke>=bloecke)
{
break;
} else
{
ebene++;
}
}
Console.Write(ebene);
}

It looks like you've been just messing with this trying to get it to work. You've complicated it with code that has no real purpose, and now it's confusing.
Your loop has 2 exit conditions when only one is necessary, you have ebene = 1 in there for no reason, etc.
You are taking the wrong approach. If you just mess with your code until it works for whatever tests you're applying, then you'll never really be confident that it works for stuff you didn't test.
You need to prove to yourself that it is correct. Try something like this:
Ensure that, at the top of the loop, the total block count, base area, and edge length are valid for a pyramid size ebene+1. (This is called a loop invariant)
Exit the loop at the top if the total number of blocks required by that pyramid is more than you have
Otherwise, increment ebene and calculate the variables for the next size, and loop.
If you implement this simple procedure, then you will be confident that the result is correct.

This works for me:
public static void Main()
{
int availableBlocks = int.Parse(Console.ReadLine());
int newBlocks = 0; // blocks for the new level
int sumBlocks = 0; // sum of used blocks
int edgeLength = 0; // blocks on one side of the level
int level = 0; // which level are we
while (true)
{
// e.g. availableBlocks = 15
//
// edge | new | sum | ok? | level
// 0 0 0 yes 0
// 1 1 1 yes 1
// 3 9 10 yes 2
// 4 16 26 no -
// edgeLength increases by 2, except for the first top-level
if (edgeLength==0)
{
edgeLength++;
} else
{
edgeLength += 2;
}
newBlocks = edgeLength * edgeLength;
sumBlocks += newBlocks;
// Check, if we still have enought blocks
if (sumBlocks <= availableBlocks)
{
level++;
} else
{
Console.WriteLine(level);
break;
}
}
}```

Related

multiplicative persistence - recursion?

I'm working on this:
Write a function, persistence, that takes in a positive parameter num
and returns its multiplicative persistence, which is the number of
times you must multiply the digits in num until you reach a single
digit.
For example:
persistence(39) == 3 // because 3*9 = 27, 2*7 = 14, 1*4=4
// and 4 has only one digit
persistence(999) == 4 // because 9*9*9 = 729, 7*2*9 = 126,
// 1*2*6 = 12, and finally 1*2 = 2
persistence(4) == 0 // because 4 is already a one-digit number
This is what I tried:
public static int Persistence(long n)
{
List<long> listofints = new List<long>();
while (n > 0)
{
listofints.Add(n % 10);
n /= 10;
}
listofints.Reverse();
// list of a splited number
int[] arr = new int[listofints.Count];
for (int i = 0; i < listofints.Count; i++)
{
arr[i] = (int)listofints[i];
}
//list to array
int pro = 1;
for (int i = 0; i < arr.Length; i++)
{
pro *= arr[i];
}
// multiply each number
return pro;
}
I have a problem with understanding recursion - probably there is a place to use it. Can some1 give me advice not a solution, how to deal with that?
It looks like you've got the complete function to process one iteration. Now all you need to do is add the recursion. At the end of the function call Persistence again with the result of the first iteration as the parameter.
Persistence(pro);
This will recursively call your function passing the result of each iteration as the parameter to the next iteration.
Finally, you need to add some code to determine when you should stop the recursion, so you only want to call Persistence(pro) if your condition is true. This way, when your condition becomes false you'll stop the recursion.
if (some stop condition is true)
{
Persistence(pro);
}
Let me take a stab at explaining when you should consider using a recursive method.
Example of Factorial: Factorial of n is found by multiplying 1*2*3*4*..*n.
Suppose you want to find out what the factorial of a number is. For finding the answer, you can write a foreach loop that keeys multiplying a number with the next number and the next number until it reaches 0. Once you reach 0, you are done, you'll return your result.
Instead of using loops, you can use Recursion because the process at "each" step is the same. Multiply the first number with the result of the next, result of the next is found by multiplying that next number with the result of the next and so on.
5 * (result of rest)
4 * (result of rest )
3 * (result of rest)
...
1 (factorial of 0 is 1).---> Last Statement.
In this case, if we are doing recursion, we have a terminator of the sequence, the last statement where we know for a fact that factorial of 0 = 1. So, we can write this like,
FactorialOf(5) = return 5 * FactorialOf(4) = 120 (5 * 24)
FactorialOf(4) = return 4 * FactorialOf(3) = 24 (4 * 6)
FactorialOf(3) = return 3 * FactorialOf(2) = 6 (3 * 2)
FactorialOf(2) = return 2 * FactorialOf(1) = 2 (2 * 1)
FactorialOf(1) = return 1 * FactorialOf(0) = 1 (1 * 1)
FactorialOf(0) = Known -> 1.
So, it would make sense to use the same method over and over and once we get to our terminator, we stop and start going back up the tree. Each statement that called the FactorialOf would start returning numbers until it reaches all the way to the top. At the top, we will have our answer.
Your case of Persistence
It calls for recursive method as well as you are taking the result and doing the same process on it each time.
Persistence(39) (not single) = return 1 + Persistence(3 * 9 = 27) = 3
Persistence(27) (not single) = return 1 + Persistence(2 * 7 = 14) = 2
Persistence(14) (not single) = return 1 + Persistence(1 * 4 = 4) = 1
Persistence(4) (single digit) = Known -> 0 // Terminator.
At the end of the day, if you have same process performed after each calculation / processing with a termination, you can most likely find a way to use recursion for that process.
You definitely can invoke your multiplication call recursively.
You will need initial sate (0 multiplications) and keep calling your method until you reach your stop condition. Then you return the last iteration you've got up to as your result and pass it through all the way up:
int persistence(int input, int count = 0) {} // this is how I would define the method
// and this is how I see the control flowing
var result = persistence(input: 39, count: 0) {
//write a code that derives 27 out of 39
//then keep calling persistence() again, incrementing the iteration count with each invocation
return persistence(input: 27, count: 1) {
return persistence(input: 14, count: 2) {
return persistence(input: 4, count: 3) {
return 3
}
}
}
}
the above is obviously not a real code, but I'm hoping that illustrates the point well enough for you to explore it further
Designing a simple recursive solution usually involves two steps:
- Identify the trivial base case to which you can calculate the answer easily.
- Figure out how to turn a complex case to a simpler one, in a way that quickly approaches the base case.
In your problem:
- Any single-digit number has a simple solution, which is persistence = 1.
- Multiplying all digits of a number produces a smaller number, and we know that the persistence of the bigger number is greater than the persistence of the smaller number by exactly one.
That should bring you to your solution. All you need to do is understand the above and write that in C#. There are only a few modifications that you need to make in your existing code. I won't give you a ready solution as that kinda defeats the purpose of the exercise, doesn't it. If you encounter technical problems with codifying your solution into C#, you're welcome to ask another question.
public int PerRec(int n)
{
string numS = n.ToString();
if(numS.Length == 1)
return 0;
var number = numS.ToArray().Select(x => int.Parse(x.ToString())).Aggregate((a,b) => a*b);
return PerRec(number) + 1;
}
For every recursion, you should have a stop condition(a single digit in this case).
The idea here is taking your input and convert it to string to calculate that length. If it is 1 then you return 0
Then you need to do your transformation. Take all the digits from the string representation(in this case from the char array, parse all of them, after getting the IEnumerable<int>, multiply each digit to calculate the next parameter for your recursion call.
The final result is the new recursion call + 1 (which represents the previous transformation)
You can do this step in different ways:
var number = numS.ToArray().Select(x => int.Parse(x.ToString())).Aggregate((a,b) => a*b);
convert numS into an array of char calling ToArray()
iterate over the collection and convert each char into its integer representation and save it into an array or a list
iterate over the int list multiplying all the digits to have the next number for your recursion
Hope this helps
public static int Persistence(long n)
{
if (n < 10) // handle the trivial cases - stop condition
{
return 0;
}
long pro = 1; // int may not be big enough, use long instead
while (n > 0) // simplify the problem by one level
{
pro *= n % 10;
n /= 10;
}
return 1 + Persistence(pro); // 1 = one level solved, call the same function for the rest
}
It is the classic recursion usage. You handle the basic cases, simplify the problem by one level and then use the same function again - that is the recursion.
You can rewrite the recursion into loops if you wish, you always can.

How to write in multiple positions in a console application at the same time? C#

I want lines as many as the width of the console to simultaneously write downwards one char to the height of the console. I've done most of it, but it goes from top to bottom to right etc...
If you need help picturing what I mean, think of the matrix code rain.
int w = Console.WindowWidth;
int h = Console.WindowHeight;
int i = 0;
while (i < w)
{
int j = 0;
while (j < h)
{
Thread.Sleep(1);
Console.SetCursorPosition(i, j);
Console.Write(".");
j++;
}
i++;
}
What I would do is construct a List<string> lines; that would contain the lines you want to write to the console window, where each line is as wide as the console width. Then just print the list out to the console window in reverse order, so the first line (at lines[0]) will always be the last one printed, and will always be at the bottom of the console window.
Sample Implementation -- someone mentioned this might be homework. I did not think so, but if it is, then please try your own implementation of the above idea first.
We can add new items to the list in the same loop that we use to print out its items. Before we add a line, however, we first check to see if there are already as many lines in the list as there are in the console window (Console.WindowHeight). If there are, then we just remove the line at lines[0] before we add a new one. In this way, the List<string> lines is "scrolling" along with the console window.
The scrolling speed is controlled by a Thread.Sleep, but this code could easily be added to a Timer instead, so that other work could happen in the background (like if this was intended to be a "screensaver", and you wanted to wait for user input to "wake up"). But no matter how we decide to implement the speed, I decided to create an enum with values that represent the number of milliseconds a Thread.Sleep implementation would use:
class Program
{
enum MatrixCodeSpeed
{
Fastest = 0,
Faster = 33,
Fast = 67,
Normal = 100,
Slow = 333,
Slower = 667,
Slowest = 1000
}
I would also create a helper method that creates a "random" line for you. It could take in an integer that specifies the "density", which means how many characters you'd want in the line. density represents a percentage, so if 10 is specified, then we pick a random number between 0 and 99, and if it's less than 10 then we add a random matrix character to the string (otherwise we add a space character).
Also, in order to replicate the matrix a little closer, I've also chosen 4 different characters to print, each one slightly darker than the previous. This adds to the three dimensional effect, where the faded blocks look further away than the solid ones:
private static Random rnd = new Random();
// Add whatever 'matrix' characters you want to this array. If you prefer to have one
// character chosen more often than the others, you can write code to favor a specific
// index, or just add more instances of that character to the array below:
private static char[] matrixChars = new[] { '░', '▒', '▓', '█' };
static string GetMatrixLine(int density)
{
var line = new StringBuilder();
for (int i = 0; i < Console.WindowWidth; i++)
{
// Choose a random number from 0-99 and see if it's greater than density
line.Append(rnd.Next(100) > density
? ' ' // If it is, add a space to reduce line density
: matrixChars[rnd.Next(matrixChars.Length)]); // Pick a random character
}
return line.ToString();
}
Next, we have the main method, which populates a list with random lines (using a density of 10%), then prints them out one at a time, in reverse order, in an endless loop (removing the first line if we need to):
static void Main()
{
var lines = new List<string>();
var density = 10; // (10% of each line will be a matrix character)
var speed = MatrixCodeSpeed.Normal;
// Hide the cursor - set this to 'true' again before accepting user input
Console.CursorVisible = false;
Console.ForegroundColor = ConsoleColor.DarkGreen;
while (true)
{
// Once the lines count is greater than the window height,
// remove the first item, so that the list "scrolls" also
if (lines.Count >= Console.WindowHeight)
{
lines.Remove(lines[0]);
}
// Add a new random line to the list, which will be the new topmost line.
lines.Add(GetMatrixLine(density));
Console.SetCursorPosition(0, 0);
// Print the lines out to the console in reverse order so the
// first line is always last, or on the bottom of the window
for (int i = lines.Count - 1; i >= 0; i--)
{
Console.Write(lines[i]);
}
Thread.Sleep(TimeSpan.FromMilliseconds((int)speed));
}
}
}
Here's a gif of it in action, up to the point where the screen is full (then the gif repeats, but the code version continues to scroll normally):
The task smells like an assignment, so I'm guiding you instead feeding the implementation. It is not ethical to feed you with an answer if it is a homework.
You are looking for a better fit of algorithm. The stated algorithm fill the console from top to bottom, as it iterate to fill through the Y-axis first (the nested loop) and followed by the X-axis (the outer loop).
What is needed is to iterate x-axis and y-axis alternatively so that it looks like it fills from the top left corner to the bottom right corner.
// 1 step to (0,0)
*
// 3 steps to (1,1)
**
**
// 5 steps for reaching (2,2)
***
***
***
// 7 steps for reaching (3,3)
****
****
****
// 9 steps for reaching (4,4) and 11 steps for (5,5)...
// I do think everyone could get this pattern
This draft would also be the final outcome of what it looks like.
Instead of filling them all at the same time, what you need is actually get the thread sleep after it reach the next square point.
(Computers are so fast that it probably do all its work to feed your screen within a second and the black console window is gone without any notice.)
At the time you posted the question, I'm also solving it from the very beginning. I thought of filling X and Y axis alternatively is the solution, but stopping at each time that the square expands is far more important to get the effect.
It is not a threading problem tag either at my point of view.
Let's sum up the above pattern:
Assume i and j are x and y coordinates respectively.
Each iteration takes you from (i, j) and n*2+1 steps to reach
(i+1,j+1)
Note that we are zero-based in this example.
We are about to construct the loop:
The n*2+1 step number is useful. It means you need to fill x-axis for
n times and y-axis for n times, and finally get the diagonal grid
(n+1,n+1) done.
In each inner loop, we first render the X frontier along y-axis and
then render the Y frontier along x-axis.
Let say the cycle start with the checkpoint (n,n), where n=3, and we
slept for a while, so we are in n=4 now.
To achieve this, we'd better first navigate to (n+1,0) then fill up
to (n+1,n)
Afterwards we navigate to (0,n+1) and fill to (n+1,n+1)
Then we are in m=n+1 now (sounds like a mathematical proving :(
The loop would be
//calculate how many checkpoints (n)
int checkpoints = 1080;
//n should indicate the actual turn we are instead of naming the total turns like sucks
//The main, the outermost For-loop
for (int n=0;n<checkpoints;n++)
{
// The nested step
for (int y=0;y<n;y++)
{
// Just fill in (n+1, y) grid
Console.SetCursorPosition(n+1, y);
Console.Write(".");
}
for (int x=0;x<n+1;x++)
{
// Just fill in (x, n+1) grid
Console.SetCursorPosition(x, n+1);
Console.Write(".");
}
// Upon completion of each main cycle we have a sleep, yah
Thread.Sleep(100);
}
Well, I expect the program to crash when the console size is smaller than 1080x1080.
This algorithm could only get you a square to fill, and a typical monitor with resolution 1920x1080 just fails as it is 16:9. This is intentional, if you're doing homework you need to configure it before shipping it to your teacher. (I've got no chance to do an assignment as I self learned programming :(
(The site continuously urging me to format my code, this has been half an hour and I just didn't do things wrong. So I decided to post it bit by bit to debug that. Finally I've got the job done...)
If you just want to write one line at a time you can use this:
int w = Console.WindowWidth;
int h = Console.WindowHeight;
int i = 0;
while (i < h)
{
Console.WriteLine(new string('.', w-1));
Thread.Sleep(20);
i++;
}
Just a bit of modification allow the code to simulate the matrix code rain.
int w = Console.WindowWidth;
int h = Console.WindowHeight;
int i = 0;
while (i < h)
{
int j = 0;
string s = "";
Thread.Sleep(10);
while (j < w)
{
Console.SetCursorPosition(j, i);
s += ".";
j++;
}
Console.Write(s);
i++;
}
basically what i did here is just some restructuring of the logic and putting in the proper delays at the right position. Hope it helps.

Don't know why my loop does not loop

I'm new at programming and have been working on a program that converts a saunas temperature from Fahrenheit to Celsius and then tells the user if he/she should turn the heat up or down depending on the original input number. I've done the majority of my code but now I can't see why it does not loop when I write a number below 73 degrees or over 77 degrees. Can anyone see the problem that my eyes seem to not find?
using System;
namespace BastunKP
{
class Program
{
public static int FahrToCels(int fahr)
{
int tempCels = (fahr - 32) * 5 / 9;
return tempCels;
}
public static void Main(string[] args)
{
Console.WriteLine("Skriv in Fahrenheit: ");
int fahr = int.Parse(Console.ReadLine());
int tempCels = FahrToCels(fahr);
do
{
if (tempCels < 73)
{
Console.WriteLine("Temperaturen är för kallt, skruva upp lite!");
}
else if (tempCels > 77)
{
Console.WriteLine("Temperaturen är för varmt, skruva ner lite!");
}
else
{
Console.WriteLine("Temperaturen är nu lagom, hoppa in!");
return;
}
fahr = int.Parse(Console.ReadLine());
tempCels = FahrToCels(fahr);
}
while (tempCels < 73 && tempCels > 77);
}
}
}
I also have a question regarding my assignment where the teacher has said that for a higher grade I should look into where the formula for converting fahrenheit to celsius and make it a double but I dont know how to do this change at all.
Thanks in advance
tempCels < 73 && tempCels > 77 is never true!
Most probably you wanted || so to run when temp is less than 73 or greater than 77, but who knows.
Welcome to StackOverflow! Now, let's get down to your question:
First off, consider your do-while loop.
do {
if (tempCels < 73) {
// Temperature too high
Console.WriteLine("Temperaturen är för kallt, skruva upp lite!");
} else if (tempCels > 77) {
// Temperature too low
Console.WriteLine("Temperaturen är för varmt, skruva ner lite!");
} else {
// Temperature just right, hop in!
Console.WriteLine("Temperaturen är nu lagom, hoppa in!");
return;
}
fahr = int.Parse(Console.ReadLine());
tempCels = FahrToCels(fahr);
}
while (tempCels < 73 || tempCels > 77);
As you can see, I removed the unnecessary else condition. What happens right now, is that all possible conditions are checked (temp < 73, temp > 77, and 73 < temp < 77).
One mistake you had, also pointed out in other answers, is that you had && (AND) instead of || (OR). And of course, a value cannot be both under 73 and above 77 :)
Now, I'd like to also point out some styling / general things I think you should 'fix':
1) Your temp conversion method contains an unnecessary variable creation and assignment. You can make it work just as well without it, like this:
public static int fahrToCels(int fahr) {
// It returns just the same, without needing to create a new,
// temporary temperature variable!
return (fahr - 32) * 5 / 9;
}
2) This might be debatable, but general naming conventions say that function names are written with camelCase.
3) While this is not a problem in your scenario specifically, it might become one when you scale up an application (or work on a bigger one).
It's best to use slightly more descriptive namings (in a bigger project, just fahr might be confusing). Again, it's not any big deal of a problem, just something for you to consider for the future :)
P.S. I did not change variable names in my examples, just to keep it more readable/relateable to the code you showed.
EDIT:
As per request, here is how to keep the values as double type.
// Notice the return type and the property types are both double.
public static double fahrToCels(double fahr) {
return (fahr - 32) * 5 / 9;
}
This way, values don't have to be only integers, and produce weird results on division - they can be of type double too!
Now, remember you will need to pass a variable of type double to the function, otherwise you will get a type error.
Hint:
double fahr = int.Parse(Console.ReadLine());
Will let the user pass a non-integer value (like, say, 17.7), and it will be stored properly.
Hint #2:
If you really want to do on the fly conversion, you can achieve this like this (example values):
int ourInteger = 4;
double ourNewDoubleNumber = (double)ourInteger / 23;
You can read more about types and type casting here: Types and Type Casting
tempCels (or any number, for that matter) can't be less than 73 and more than 77 at the same time. You should use the logical || operator, not the logical && operator:
do {
// code
} while (tempCels < 73 || tempCels > 77);
// Here ---------------^
while will loop when the condition is true, but tempCels cannot be <73 and >77 at the same time! Fix that condition and it will work.

Enter a code branch every ten percent till 100 percent

I can think of some very convoluted methods with loops and nested loops to solve this problem but I'm trying to be more professional than that.
My scenario is that I need to enter a section of code every ten percent but it isn't quite working as expected. It is entering the code about every percent which is due to my code but I lack the knowledge to know how to change it.
int currentPercent = Math.Truncate((current * 100M) / total);
//avoid divide by zero error
if (currentPercent > 0)
{
if (IsDivisible(100, currentPercent))
{
....my code that works fine other than coming in too many times
}
}
Helper referenced above where the trouble is:
private bool IsDivisible(int x, int y)
{
return (x % y) == 0;
}
So obviously it works as it should. Mod eliminates currentPercent of 3 but 1 & 2 pass when really I don't want a true value until currentPercent = 10 and then not again till 20...etc.
Thank you and my apologies for the elementary question
Mod will only catch exact occurrences of your interval. Try keeping track of your next milestone, you'll be less likely to miss them.
const int cycles = 100;
const int interval = 10;
int nextPercent = interval;
for (int index = 0; index <= cycles; index++)
{
int currentPercent = (index * 100) / cycles;
if (currentPercent >= nextPercent)
{
nextPercent = currentPercent - (currentPercent % interval) + interval;
}
}
I might misunderstand you, but it seems like you're trying to do something extremely simple more complex than it needs to be. What about this?
for (int i = 1; i <= 100; i++)
{
if (i % 10 == 0)
{
// Here, you can do what you want - this will happen
// every ten iterations ("percent")
}
}
Or, if your entire code enters from somewhere else (so no loop in this scope), the important part is the i % 10 == 0.
if (IsDivisible(100, currentPercent))
{
....my code that works fine other than coming in too many times
}
try changing that 100 to a 10. And I think your x and y are also backwards.
You can try a few sample operations using google calculator.
(20 mod 10) = 0
Not sure if I fully understand, but I think this is what you want? You also reversed the order of modulo in your code (100 mod percent, rather than the other way around):
int currentPercent = current * 100 / total;
if (currentPercent % 10 == 0)
{
// your code here, every 10%, starting at 0%
}
Note that code this way only works properly if you are guaranteed to hit every percentage-mark. If you could, say, skip from 19% to 21% then you'll need to keep track of which percentage the previous time was to see if you went over a 10% mark.
try this:
for (int percent = 1; percent <= 100; percent++)
{
if (percent % 10 == 0)
{
//code goes here
}
}
Depending on how you increment your % value, this may or may not work % 10 == 0. For example jumping from 89 to 91 % would effectively skip the code execution. You should store last executed value, 80 in this case. Then check if interval is >= 10, so 90 would work, as well as 91.

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