Does anyone know of an algorithm that can generate unique bingo card faces? I'm looking to implement this algorithm in C#.
Thanks,
get 5 sets containing 15 numbers each (1-15 for set 1, 16-30 for set 2...)
select 5 different numbers in sets 1,2,4,5
select 4 different numbers in set 3
To check if that card already exists
Check each existing card for top left correspondance with new card
if both numbers are equal, then move to the second number
if you get 24 times the same number at the same place then both cards are equal and new card must be rejected
This is an interesting problem, but as Michael Madsen reported, given the number of possibilities, you would probably be better generate them randomly and after, check if you have duplicates. (Unless you want to generate all 111 quadrillion possibilities, which I hope you have data storage space for!)
Here's a function for generating a random subset of integers from a given range which you might find useful:
private static IEnumerable<int> RandomSubsetOfRange(int min, int max, int count)
{
Random random = new Random();
int size = max - min + 1;
for (int i = 0; i <= size; i += 1)
{
if (random.NextDouble() <= ((float)count / (float)(size - i + 1)))
{
yield return min + i;
count -= 1;
}
}
}
Related
I need some help to do my homework. I should write non-duplicate random numbers. I'm able to show random numbers but I don't know about non-duplicate.
Here's my code:
Random r = new Random();
for (int i = 0; i < 40; i++)
{
int temp = r.Next(0, 100);
Console.WriteLine(temp);
}
What do I need to do to generate non-duplicate number?
Note that this answer only deals with (relatively) small, pre-determined sets.
The reason the other (simple) solution is inefficient is this: you want to generate 100 random numbers between 0 and 99. You get to the point where you have generated 90 random numbers, and just need 10 more.
The problem is that you're still generating numbers between 0 and 99 every time, except now your chance of finding a number that hasn't already been generated is 1 in 10. So 9 of every 10 numbers you generate has already been added to the list.
Once you get down to just needing 1 number, your chance of generating the remaining 1 that hasn't already been generated is 1 in 100. So for every 100 numbers you generate, only 1 of them will be the last possible number.
I'm sure this is simplifying things given that the Random class is pseudo-random (i.e. it's an algorithm that appears random), but this does explain your situation and why the other answer will be slower.
An improved solution would be this:
// Add all of the numbers 0 to 100 to a list
var availableNumbers = new List<int>();
for (int i = 0; i < 100; ++i)
{
availableNumbers.Add(i);
}
Random random = new Random();
for (int i = 0; i < 40; ++i)
{
// Choose a random position in the available numbers list
var idx = random.Next(0, availableNumbers.Count);
// Print the number from this position in the list
Console.WriteLine(availableNumbers[idx]);
// Remove the item at this position
availableNumbers.RemoveAt(idx);
}
Because we start with a list of all available numbers, we are able to choose numbers from it at random. Removing items from the available numbers list means that they are not available to be chosen a second time. We no longer have to try many times to find an unused number, as removing them when we select them ensures that all of the numbers in the available numbers list are always only unused numbers.
You may use a HashSet to store the numbers and make sure there are no duplicates. Here's an example:
HashSet<int> numbers = new HashSet<int>();
Random r = new Random();
for (int i = 0; i < 40; i++)
{
int temp;
do
{
temp = r.Next(0, 100);
} while (numbers.Add(temp) == false); // If the `.Add()` method returns false,
// that means the number already exists.
// So, we try to generate another number.
Console.WriteLine(temp);
}
I'm creating a game in which someone opens a chest and the chest will give them a random prize. The maximum I can give out is 85,000,000 in 10,000 chests which is 8,500 average however I want some to make it so some chests will be below this value and above and to be able to set a min lose of 2,500 and max win 250,000 but still get the total value of 85,000,000.
I'm really struggling to come up with an algorithm for this using my C# knowledge.
Here goes some OOP. You have Player class. Which stores some info - amount of gold he has, chests left to open, and total amount of gold in chests he will find.
public class Player
{
private int gold = 0;
private int goldLeftInChests = 85000000;
private int chestsToOpen = 10000;
private Random random = new Random();
public void OpenChest()
{
if (chestsToOpen == 0)
return; // or whatever you want after 10000 chests.
int goldInChest = CalculateGoldInNextChest();
goldLeftInChests -= goldInChest;
chestsToOpen--;
gold += goldInChest;
}
private int CalculateGoldInNextChest()
{
if (chestsToOpen == 1)
return goldLeftInChests;
var average = goldLeftInChests / chestsToOpen;
return random.Next(average);
}
}
When next chest is opened, gold in chest is calculated and player data ajusted - we add some gold to player and reduce total amount of gold in chests, and chests left to open.
Calculating gold in a chest is very simple. We get average amount left and calculate number between 1 and average. First time this value will always be below 8500. But next time average will be little bit bigger. So player will have chance to find more than 8500. If he will be unlucky again, average will grow. Or it will be reduced if palyer gets lot of gold.
UPDATE: As #Hans pointed, I didn't count min and max restrictions for gold in chests. Also there is a problem in #Hans solution - you should move gold between 10000 chests lot of time to get some chests close to 250000 value. And you have to fill and keep all 10000 values. Next problem I thought about was random numbers distribution in .NET. Values have equal probability on all interval we are using. So if we are generating value from 2500 to 250000, chance that we'll get value around 8500 (average) is like 12000 (8500±6000) vs 235500 (250000-12000-2500). That means generating default random numbers from given range will give you lot of big numbers in the begining, and then you will stick near lowest boundary (2500). So you need random numbers with different distribution - Gaussian variables. We still want to have 8500 gold with highest probablity, and 250000 with lowest probability. Something like that:
And last part - calculation. We need to update only one method :)
private int CalculateGoldInNextChest()
{
const int mean = 8500;
var goldPerChestRange = new Range(2500, 250000);
var averageRange = new Range(mean - 2500, mean + 2500);
if (chestsToOpen == 1)
return goldLeftInChests;
do
{
int goldInChest = (int)random.NextGaussian(mu: mean, sigma: 50000);
int averageLeft = (goldLeftInChests - goldInChest) / (chestsToOpen - 1);
if (goldPerChestRange.Contains(goldInChest) && averageRange.Contains(averageLeft))
return goldInChest;
}
while (true);
}
Note: I used range to make code more readable. Running tests several times produces nice top values more than 200000.
pseudocode algoritm:
use an array of chests
index of array is chest number; length of array is amount of chests
value in array is amount in chest at that index
initial value is total amount divided by number of chests
now repeat a number of times (say: 10 times the number of chests)
get two random chests
work out the maximum amount you can transfer from chest 1 to chest 2, so that 1 doesn't get below the minimum and 2 doesn't get above the maximum
get a random value below that maximum and transfer it
Now try and implement this in C#.
This should be a good starting point. Each chest gets filled randomly with the limits adapting to make sure the remaining chests can also get valid values.
Random rand = new Random();
int[] chests = new int[numOffChests];
int remaining = TotalValue;
for(int i = 0; i < numOffChests; i++)
{
int minB = Math.Max(remaining / (numOffChests - i), maxChestValue);
int maxB = Math.Min(remaining - (numOffChests - i * minChestValue), maxChestValue);
int val = rand.Next(minB, maxB);
remaining -= val;
chests[i] = val;
}
The distribution has to be heavily skewed to get that range of values with that mean. Try an exponential formula, X=exp(a*U+b)+c where U is uniform on [0,1]. Then the conditions are
-2,500 = exp(b)+c
250,000 = exp(a+b)+c
8,500 = integral(exp(a*u+b), u=0..1)
= exp(b)/a*(exp(a)-1)+c
= 252,500/a+c
which gives the two equations
250,000+2,500*exp(a) = c*(1-exp(a))
8,500 = 252,500/a+c
A bit of graphical and numerical solution gives the "magic" numbers
a = 22.954545,
b = -10.515379,
c = -2500.00002711621
Now fill 10,000 chests according to that formula, compute the sum over the chest prices and distribute the, with high probability small, excess in any pattern you like.
If you want to hit the upper and lower bounds more regularly, increase the bounds at the basis of the computation and cut the computed value if outside the original bounds.
I assume that a probabilistic function gives the chance of a win/lose value V to occur. Let's say that the probability for V is proportional to (250000-V)**2, giving fewer chances to get high prizes.
To simplify some rounding issues, let's also assume that win/lose are multiple of 100. You may then make the following (untested) computations:
int minwin = -2500 ;
int maxwin = 250000;
int chestcount = 10000;
int maxamount = 85000;
// ----------- get probabilities for each win/lose amount to occur in all chests ----------
long probtotal = 0 ;
List<long> prob = new List<long> ;
for (long i=minwin;i<=maxwin;i++) if (i%100==0)
{ long ii=(maxwin-i)*(maxwin-i) ; prob.Add((float)ii) ; probtotal+=ii ; }
for (int i=0;i<prob.Count;i++) prob[i]=prob[i]/(probtotal) ;
for (int i=0;i<prob.Count;i++)
Console.writeLine("Win/lose amount"+((i-minwin)*100).ToString()+" probability="+(proba[i]*100).ToString("0.000")) ;
// Transform "prob" so as to indicate the float count of chest corresponding to each win/lose amount
for (int i=0;i<prob.Count;i++) prob[i]=prob[i]*chestcount ;
// ---------- Set the 10000 chest values starting from the highest win -------------
int chestindex=0 ;
List<int> chestvalues = new List<int>();
float remainder = 0 ;
int totalwin=0 ;
for (int i=0;i<prob.Count;i++)
{
int n = (int)(prob[i]+remainder) ; // only the integer part of the float ;
remainder = prob[i]+remainder-n ;
// set to n chests the win/lose amount
int curwin=(i-minwin)*100 ;
for (int j=0;j<n && chestvalues.count<chestcount;j++) chestvalues.Add(curwin) ;
totalwin+=curwin ;
}
// if stvalues.count lower than chestcount, create missing chestvalues
for (int i=chestvalues.Count;i<chestcount;i++) chestvalues.Add(0) ;
// --------------- due to float computations, we perhaps want to make some adjustments --------------
// i.e. if totalwin>maxamount (not sure if it may happen), decrease some chestvalues
...
// --------------- We have now a list of 10000 chest values to be randomly sorted --------------
Random rnd = new Random();
SortedList<int,int> randomchestvalues = new SortedList<int,int>() ;
for (int i=0;i<chestcount;i++) randomchestvalues.Add(rnd.Next(0,99999999),chestvalues[i]) ;
// display the first chests amounts
for (int i=0;i<chestcount;i++) if (i<234)
{ int chestamount = randomchestvalues.GetByIndex(i) ; Console.WriteLine(i.ToString()+":"+chestamount) ; }
}
i am developing a mine sweeper game in c# of dimension (8 x 8).The difficulty levels increase/decrease the number of mines on the grid.
I use a random class (with min,max set;) to generate a random cell number.The problem i am facing is ,the random object keeps repeating the same number.I tried to resolve this issue by maintaining a local list where i store the generated unique random numbers.The next time i call Next(), i would check it against the local list ,to see if its already present.If the number is already present i would keep calling Next() until i get a new number which is unique and not present in the list.But this doesnt look in itself a good solution as sometimes it takes painful amount of time to generate a new list.
Any suggestions on this please
Even if you use the same random number generator, repeating values are possible.
One way to avoid this would be to generate a list of possible values and using the random number generated to access a value in this list (using as indice) and reducing this list, as you find places to put mines to.
For 8 X 8 example, you have 64 possible places
List<int> possibleValues = new List<int>();
for (int i = 0; i < 64; i++)
{
possibleValues[i] = i;
}
List<int> result = new List<int>();
Random r = new Random();
int numberOfMines = 50; //say you want to put 50 mines there
for (int i = 0; i < numberOfMines; i++)
{
int indice = r.Next(possibleValues.Count);
int value = possibleValues[indice];
possibleValues.Remove(value);
result.Add(value);
}
It looks like you want a shuffle based on a fixed number of cells (8,8), e.g. a Fisher-Yates shuffle. This would guarantee that any coordinate only appears exactly once (as opposed to repeatedly using Random.Next() where you can draw the same number many times), and the order of appearance of coordinates is randomized.
Initialize an array that contains all the coordinates of your cells, shuffle the array and maintain an index of the next "random" cell, return the cell at the offset of the index and increase the index.
First calculate the number of mines, and empty fields.
Random rand=new Random();
int mines=GetMinesFromDifficulty(...);
int empty=TotalFields-mines;
Then for each field:
for(int y=0;y<height;y++)
for(int x=0;y<height;y++)
{
if(random.Next(mines+empty) < mines))
{
field[x,y]=Mine;
mines--;
}
else
{
field[x,y]=Empty;
empty--;
}
}
Instead of picking slots where the mines should be, loop through the slots and calculate the probability that there should be a mine there. The implementation for this becomes very simple, as you just need a single loop:
bool[] mines = new bool[64];
int cnt = 12;
Random rnd = new Random();
for (int i = 0; i < mines.Length; i++) {
if (rnd.Next(mines.Length - i) < cnt) {
mines[i] = true;
cnt--;
}
}
(Room for improvement: You can exit out of the loop when cnt reaches zero, if you don't need to initialise all slots.)
If your grid is 8x8, and you want to randomly choose an unused cell instead of pulling random numbers until you hit an unused one, then keep track of the number of unused cells. Say 8 have been used, leaving 55 unused. Then generate a random number between 0 and 54. You would then have to count through, and find the nth empty cell.
It would probably be easier to think of the problem in a more linear way. Instead of say a 2D array... Squares[8][8] think of it as a single dimension array Squares[64].
At this point you generate a number between 0-63 for your random mine placement. If say the value is 10 you could store for later to offset subsequent numbers. You can reduce your range now from 0-62, if you pulled out the value 16 you would want to add +1 for each value you'd already pulled out underneath it (so actually use 17 in this case, but square 10 has been excluded from our set).
Without seeing any code it's kind of hard to get the gist of things, but from what I can tell you have the following:
A multi-dimensional array [8][8] for the grid layout of the game, you're now trying to randomly place mines?
You'll need to keep one instance of Random for generating the numbers, else you will get the same number over and over again. Something like this
private readonly Random randomNumber = new Random();
for(int i = 0; i < 10; i++)
{
Console.WriteLine(this.randomNumber.Next(1, 10));
}
This will then generate 10 random numbers, each one different.
Say I have 10 prizes to give to 100 people. Each person gets a shot, one at a time. So if the first person fails to win a prize, the probability goes up, 10 in 99, and so one... Also all 10 prizes MUST go.
What would be the best way to write this in such a way that by the end if there is still a prize left, that person would have a 1 in 1 chance to get a prize...
What I was thinking like this:
int playersLeft = 100
int winners = 0
while (winners < 10)
winners += (random.Next(playersLeft--)<(10-winners)) ? 1 : 0;
I wanted to know if there was a better or more straight forward way to do it. I know it seems simple but this simple task is part of a very important aspect of the app and it must be right.
TO CLARIFY: Why I want to do something like this:
In reality there is an unlimited number of players, each with an X in Y probability to win, say 10/100 = 10%. However if I leave it to the random number generator, there is a chance that in 100 players, only 9 would win, or worst, 11. In my app, I must assure that no more and no less than 10 players for every 100 will win.
Should every person have equal chances of winning? In that case why not just select randomly 10 distinct numbers 1-100 and then pretend to do it in order?
var winners = new HashSet<int>();
while(winners.Count < 10)
{
var number = random.Next(100);
if(!winners.Contains(number)) winners.Add(number);
}
for(i = 0; i < 100; i++)
{
if(winners.Contains(i)) Console.WriteLine("{0} won!!!", i);
else Console.WriteLine("{0} didn't win, sorry...", i);
}
I have thought about this some more and have come up with the following. We can give the first guy a fair shot at winning and then if the rest of the rewards are distributed fairly among the rest of the people (no matter if he wins or loses) the whole thing will be fair. Of course that's far from formal proof, so feel free to correct me. The following should give a fair system:
int prizes = 10;
for(int i = 100; i >= 1; i++)
{
var result = random.Next(people);
if(result < prizes)
{
Console.WriteLine("{0} won", i);
prizes--;
}
}
Edit: Proof this works:
The first person trivially has n/k chance of winning (n being the number of prizes, k being the number of people.
Let's assume we distribute the remaining prizes fairly among the rest of the people. In that case they will have with probability n/k, n-1 prizes distributed between them and with probability (k-n)/k, n prizes. That adds up to (n*(n-1))/k + (n*(k-n))/k = n*(k-1)/k on average which is their fair share of the prizes.
We use the same method to either distribute n-1 or n prizes among the k-1 people. Q.E.D.
This will give you the behavior of forcing the probability of a winner to go to 1.0 as the number of people shrinks. However, as #obrok pointed out, the probability of a person winning a prize depends on their rank in the list of 100 people.
This is actually the same algorithm that is used for "N choose K" subset selection. http://mcherm.com/permalinks/1/a-random-selection-algorithm
int prizes = 10;
int people = 100;
while ( prizes > 0 ) {
double probOfWin = (double) prizes / people;
if ( random.NextDouble() <= probOfWin ) {
prizes--;
}
people--;
}
The perfectly fair way to do is to generate a random number from 1 to (100! / (90! * 10!)) (since this is the number of possible combinations of prizewinners) and use that to award the prizes.
However it's easier to use some multiple of that number, such as the number of permutations of prizewinners, which is (100! / 90!). One way of doing this is to populate an array of 100 integers but remove the winning integer from the array each time (swapping it with the last non-winning integer is the easiest way to achieve this).
Your algorithm effectively requires randomness of 100! so it is much less efficient, although I believe it is still perfectly fair.
I want to generate random numbers but controlled, meaning the numbers should be nearly equally separated, and spread through the range.
As an example, if the bounds were 1 and 50, then if the first generated number is 40 then the next number should not be close. Suppose it's 20, then 30 would be an acceptable third number.
Please help.
Rather than completely random numbers, you might want to look at noise functions like Perlin Noise to generate superficially random data in a predictable fashion.
http://en.wikipedia.org/wiki/Perlin_noise
There are a few variations out there - definitely worth researching if you can describe your segmentation of data algorithmically.
It's used a lot in gaming to smooth and add interest to otherwise randomly generated terrain textures.
There's a few sample implementations in C# out there, this one is used to generate a bitmap but could easily be adapted to fill a 2d array:
http://www.gutgames.com/post/Perlin-Noise.aspx
There's also plenty of questions here on SO about Perlin Noise too:
https://stackoverflow.com/search?q=perlin+noise
You may do something like this:
randomSpaced[interval_, mindistance_, lastone_] :=
(While[Abs[(new = RandomReal[interval])-lastone] < mindistance,];
Return[new];)
Randomnicity test drive:
For[i = 1, i < 500000, i++,
rnd[i] = randomSpaced[{0, 40}, 10, rnd[i - 1]];
];
Histogram[Table[rnd[i], {i, 500000}]]
You may see that the frequencies accumulates in the borders
Moreover, if you are not cautious, and ask for a distance too high, the results will be something like:
For[i = 1, i < 50000, i++,
AppendTo[rnd, randomSpaced[{0, 40}, 25, Last[rnd]]];];
Histogram[rnd
]
because you are not allowing points at the center.
Define a separation distance d the new number should have to the last. If the last number was, say, 20 the next random number should not be from 20-d to 20+d. That means the random interval should be [1, 20-d) and (20+d,50].
Since you can not call random.next() with two intervals you need to call it with an interval reduced by 2d and then map the random number to your original [1,50] interval.
static class RandomExcludingSurrounding
{
static Random random = new Random();
public static int Next(int x, int d, int min, int max)
{
int next = random.Next(min, max-2*d);
if (next > x-d)
next += 2*d;
return next;
}
}
int min = 1;
int max = 50;
Random random = new Random();
int next = random.Next(min, max);
while(true)
{
int next = RandomExcludingSurrounding.Next(next, 20, min, max);
Console.WriteLine(next);
}