Problems with randomizing a list - c#

So I'm trying to copy an array to a list in a randomized order, and it's "kind of" working, I'm getting a random order output except that it doesn't seem to be "completely" random, that is, there are certain sequences of numbers that seem to repeat many times they are specifically 1-9. Now they don't always appear in a sequence and the position of the sequence relative to the other numbers changes, but I am seeing them appear an abnormal amount of times for a "randomized list".
Here's my code:
class Program
{
static void Main(string[] args)
{
int[] originalDeck = new int[52];
for (int i = 1; i < originalDeck.Length+1; i++)
{
originalDeck[i-1] = i;
}
Random RNG = new Random();
List<int> split1 = originalDeck.OrderBy(x => x < RNG.Next(52)).ToList();
PrintList1(split1);
Console.ReadKey();
}
static void PrintList1(List<int> split)
{
foreach (int card in split)
{
Console.WriteLine(card);
}
}
}

What happens here is that you sort the array based on the probability of the number to be less than a random number between 0 and 51.
The OrderBy function in your case returns a boolean value of either True or False, the internal implementation works such that the sorted array will have all the false in the beginning followed by all the true. You can see this by sorting a list of Boolean values. In your case, you pick a random number between 0 and 51 and then compare it to your number. The probability of the check x < RNG.Next(52) to return True is much higher for small numbers and False for large numbers. Therefore you are actually putting the larger numbers at the beginning of the array and the smaller numbers at the end of the array with some randomness.
About the seed issue. It is not related directly since the seed is determined once when you create the Random instance.

Related

C# Random number doesn't Generate 0

I'm working on a football league fixture project on C# Console Application.
I'm trying to choose random teams from the array which contains the teams which plays at their home and away.
When I'm trying to generate 9 random numbers, only 8 numbers are generated and 0 are not, so the code can't break the for loop.
I suppose that the problem is that the if statement does not allow to generate the same number and int array's elements' default value is 0.
Here is the code and the output:
C# Code Output
int randomHome; //Random number genetator for choosing a random iteration value from home array which containss the teams which plays at their home
int randomAway; //Random number genetator for choosing a random iteration value from awayarray which containss the teams which plays at away
Random randomNum = new Random();
int[] randomHomeNumArray = new int[home.Length]; //array will hold the randomHome values and home array is the array which is holding the team's iteration values which plays at their home
int[] randomAwayNumArray = new int[away.Length]; //array will hold the randomAway values and away array is the array which is holding the team's iteration values which plays at away
for (int homeArrayCounter = 0; homeArrayCounter < randomHomeNumArray.Length; homeArrayCounter++)
{
randomHome = randomNum.Next(home.Length)
if (!randomHomeNumArray.Contains(randomHome) )
{
randomHomeNumArray[homeArrayCounter] = randomHome; //It will hold the randomHome values
Console.WriteLine(homeArrayCounter + ". iterasyon in Home " + randomHomeNumArray[homeArrayCounter]);
}
else
{
homeArrayCounter--;
}
}
Console.WriteLine("\n\n");
for (int awayArrayCounter = 0; awayArrayCounter < randomAwayNumArray.Length; awayArrayCounter++)
{
randomAway = randomNum.Next(randomAwayNumArray.Length);
if (!randomAwayNumArray.Contains(randomAway))
{
randomAwayNumArray[awayArrayCounter] = randomAway; //It holds the random valures from away array which contains the teams which plays at away
Console.WriteLine(awayArrayCounter + ". iterasyon in Away " + randomAwayNumArray[awayArrayCounter]);
}
else
{
awayArrayCounter--;
}
}
When you initalize an array, it has the value 0 by default for each index. When you are using the random number, it always skips 0 because it already exists.
You can try like this:-
for(int i= 0; i<randomHomeNumArray.Length; i++){
randomHomeNumArray[i] = -1;
}
for (int homeArrayCounter = 0; homeArrayCounter < randomHomeNumArray.Length; homeArrayCounter++)
{
do{
randomHome = randomNum.Next(home.Length);
} while(!randomHomeNumArray.Contains(randomHome));
randomHomeNumArray[homeArrayCounter] = randomHome; //It will hold the randomHome values
Console.WriteLine(homeArrayCounter + ". iterasyon in Home " + randomHomeNumArray[homeArrayCounter]);
}
It appears you're trying to just randomize arrays.
Try this instead:
Random randomNum = new Random();
int[] randomHomeNumArray = Enumerable.Range(0, home.Length).OrderBy(_ => randomNum.Next()).ToArray();
int[] randomAwayNumArray = Enumerable.Range(0, away.Length).OrderBy(_ => randomNum.Next()).ToArray();
That's it. Done.
Your problem is the default initialization of your arrays:
int[] randomHomeNumArray = new int[home.Length];
This creates an array filled with 0s, because 0 is the default value for int.
So your if condition
if (!randomHomeNumArray.Contains(randomHome))
is always false for 0 because 0 is already contained in the array.
You may initialize your arrays instead like this:
int[] randomHomeNumArray = Enumerable.Repeat(-1, home.Length).ToArray();
So you fill it with -1 instead of 0 and your if condition works.
Because int is not a null-able data type, by default, an int[] gets initialized with zeroes. So even if you think it's an empty array, it's actually an array with all elements set to zero.
To rectify the problem, you can consider using an int?[] (null-able int array) instead. Or, you can initialize the array with either a negative integer or some integer greater than the maximum inclusive upper bound. Better yet, to achieve what you want, in a better way, use the solution provided by #Enigmativity, and mark his answer accepted, if it helped.

Efficient way to pair a limited number of random elements from two separate collections

I have two lists (lista and listb), each containing an unknown number of points (two ints in a struct).
I want to create a new list containing unique random pairings from lista and listb. So an example entry might be [12,14] where 12 is an index for lista and 14 is an index for listb.
I also want to set a maximum number of pairings when calling this function. So instead of pairing every element in lista with every element in listb, I could limit it to 200 random pairings as an example.
My first attempt at this was to simply generate every possible pairing. Shuffle that list and knock off any elements past my max. This solution isn't nearly efficient enough.
My next attempt was to make an array per original list of every possible index, shuffle those separately, and then just iterate over them both until I had the max number of pairings (or all of them). This has several problems I'm not certain how to solve. One of which, lista could have 10 million elements for all I know. Creating a new array of 10 million elements (the indices list) and shuffling that when my max pairs might only be 200? Seems silly to go that far.
I've considered just choosing random elements from both lista/listb and seeing if I've already paired them before adding them to the new list. This is also quite a silly option as a lot of time can be spent picking duplicate pairings over and over.
So, what's a good option here or is there one? I don't want to iterate over every possible combination, pairings need to be unique, removing options from a list is quite slow due to the array re-sizing when they are quite large, distribution needs to be pretty uniform in the selection process for each list, etc.
Thanks for any and all help.
Edit - I meant the unique aspect regarding the pairs themselves. So element 10 in lista could be used over and over as long as the element in listb is different each time. The only catch there is I don't want to limit lista and listb right off as I need fairly even distribution across both lists for every pairing.
To avoid duplicates completely, you could try doing a sparse Fisher-Yates shuffle.
Create a Dictionary<int, int> dict that will map "indices in the Fisher-Yates array that do not hold their own index" to "the value at that index".
For the nth item, pick a random number x from n (inclusive) to "size of ListA * size of ListB" (exclusive)
dict[x] ?? x is your selected item.
Store dict[n] ?? n in dict[x].
Map the selected item back to a pair of indices (divide by size of ListA for the ListB index, modulus by the size of ListA for the ListA index).
A math or statistics buff might give you a formula for evaluating this but I just wrote some test code.
The code simply picks random pairs, and every time it sees a duplicate it tries again. Then for each such "pick a random pair until unique" cycle it counts how many retries it did and tracks this. Then finally this is summed up into a global array to track the relative frequency of these things.
Here's the results after about 1 minute of execution:
84382319 81 0 0 0 0 0 0 0 0
The numbers mean this:
Out of 421912 cycles [(84382319+81)/200]:
81 duplicates were found but retrying did not find a duplicate (3rd number and up is 0)
84382319 unique pairs could be found on the first try without duplicates
So, obviously this will start to rise if you increase the number of pairs you want generated or lower the numbers to choose wrong, but I'm not sure this will pose a problem in practice.
Here's the LINQPad program I used:
static Random R = new Random();
void Main()
{
var a = 10000;
var b = 10000;
var n = 200;
int[] counts = new int[10];
var dc = new DumpContainer().Dump();
while (true)
{
var once = Test(a, b, n);
for (int i = 0; i < once.Length; i++)
counts[i] += once[i];
dc.Content = Util.HorizontalRun(true, counts);
}
}
public static int[] Test(int a, int b, int n)
{
var seen = new HashSet<Tuple<int, int>>();
var result = new int[10];
for (int index = 0; index < n; index++)
{
int tries = 0;
while (true)
{
var av = R.Next(a);
var bv = R.Next(a);
var t = Tuple.Create(av, bv);
if (seen.Contains(t))
tries++;
else
{
seen.Add(t);
break;
}
}
result[tries]++;
}
return result;
}

Randomly select a specific quantity of indices from an array?

I have an array of boolean values and need to randomly select a specific quantity of indices for values which are true.
What is the most efficient way to generate the array of indices?
For instance,
BitArray mask = GenerateSomeMask(length: 100000);
int[] randomIndices = RandomIndicesForTrue(mask, quantity: 10);
In this case the length of randomIndices would be 10.
There's a faster way to do this that requires only a single scan of the list.
Consider picking a line at random from a text file when you don't know how many lines are in the file, and the file is too large to fit in memory. The obvious solution is to read the file once to count the lines, pick a random number in the range of 0 to Count-1, and then read the file again up to the chosen line number. That works, but requires you to read the file twice.
A faster solution is to read the first line and save it as the selected line. You replace the selected line with the next line with probability 1/2. When you read the third line, you replace with probability 1/3, etc. When you've read the entire file, you have selected a line at random, and every line had equal probability of being selected. The code looks something like this:
string selectedLine = null;
int numLines = 0;
Random rnd = new Random();
foreach (var line in File.ReadLines(filename))
{
++numLines;
double prob = 1.0/numLines;
if (rnd.Next() >= prob)
selectedLine = line;
}
Now, what if you want to select 2 lines? You select the first two. Then, as each line is read the probability that it will replace one of the two lines is 2/n, where n is the number of lines already read. If you determine that you need to replace a line, you randomly select the line to be replaced. You can follow that same basic idea to select any number of lines at random. For example:
string[] selectedLines = new int[M];
int numLines = 0;
Random rnd = new Random();
foreach (var line in File.ReadLines(filename))
{
++numLines;
if (numLines <= M)
{
selectedLines[numLines-1] = line;
}
else
{
double prob = (double)M/numLines;
if (rnd.Next() >= prob)
{
int ix = rnd.Next(M);
selectedLines[ix] = line;
}
}
}
You can apply that to your BitArray quite easily:
int[] selected = new int[quantity];
int num = 0; // number of True items seen
Random rnd = new Random();
for (int i = 0; i < items.Length; ++i)
{
if (items[i])
{
++num;
if (num <= quantity)
{
selected[num-1] = i;
}
else
{
double prob = (double)quantity/num;
if (rnd.Next() > prob)
{
int ix = rnd.Next(quantity);
selected[ix] = i;
}
}
}
}
You'll need some special case code at the end to handle the case where there aren't quantity set bits in the array, but you'll need that with any solution.
This makes a single pass over the BitArray, and the only extra memory it uses is for the list of selected indexes. I'd be surprised if it wasn't significantly faster than the LINQ version.
Note that I used the probability calculation to illustrate the math. You can change the inner loop code in the first example to:
if (rnd.Next(numLines+1) == numLines)
{
selectedLine = line;
}
++numLines;
You can make a similar change to the other examples. That does the same thing as the probability calculation, and should execute a little faster because it eliminates a floating point divide for each item.
There are two families of approaches you can use: deterministic and non-deterministic. The first one involves finding all the eligible elements in the collection and then picking N at random; the second involves randomly reaching into the collection until you have found N eligible items.
Since the size of your collection is not negligible at 100K and you only want to pick a few out of those, at first sight non-deterministic sounds like it should be considered because it can give very good results in practice. However, since there is no guarantee that N true values even exist in the collection, going non-deterministic could put your program into an infinite loop (less catastrophically, it could just take a very long time to produce results).
Therefore I am going to suggest going for a deterministic approach, even though you are going to pay for the guarantees you need through the nose with resource usage. In particular, the operation will involve in-place sorting of an auxiliary collection; this will practically undo the nice space savings you got by using BitArray.
Theory aside, let's get to work. The standard way to handle this is:
Filter all eligible indices into an auxiliary collection.
Randomly shuffle the collection with Fisher-Yates (there's a convenient implementation on StackOverflow).
Pick the N first items of the shuffled collection. If there are less than N then your input cannot satisfy your requirements.
Translated into LINQ:
var results = mask
.Select((i, f) => Tuple.Create) // project into index/bool pairs
.Where(t => t.Item2) // keep only those where bool == true
.Select(t => t.Item1) // extract indices
.ToList() // prerequisite for next step
.Shuffle() // Fisher-Yates
.Take(quantity) // pick N
.ToArray(); // into an int[]
if (results.Length < quantity)
{
// not enough true values in input
}
If you have 10 indices to choose from, you could generate a random number from 0 to 2^10 - 1, and use that as you mask.

Non repeating random number in c# for Mine sweeper Game

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.

How to generate a non-growing (int) random array c#?

I'm trying to make a Integer (int) array with random numbers NOT growing.
For example: 3 10 5 9 20
But NOT: 3 5 9 10 20 (because they just grow)
I'm using Random class with this code (but I always get a growing list like in the second example):
int[] array1 = new int[5];
Random random_istance = new Random();
for (int i=0;i<5;i++)
{
array1[i] = random.Next(0,999999);
}
I also tried with a code like (I know it is horrible programming) :
int[] array1 = new int[5];
Random random_istance = new Random();
for (int i=0;i<5;i++)
{
random = new Random(x-y*z); // re-instantation
array1[i] = random.Next(0,999999); // x,y and z are variable defined outside
}
(*) My final goal is to get an array of random int between 0 and 999999 but some are to not to be in a sequence (because later I'm going to apply an algorithm to order the array and would not make sense to order a already-ordered array).
Moreover I have to create ANOTHER array with elements just DECREASING (so one random array , and one decreasing array).
Any idea how to salve at least first problem (*)?
Thanks in advance for any help.
One way to ensure that your array is not sorted from low to high is by ordering it randomly when you detect its ordered based on the value, something like:
// while the array is sorted
var sortedCopy = array1.ToList();
sortedCopy.Sort();
while (array1.SequenceEqual(sortedCopy))
{
Array.Sort(array1, new Comparison<int>((left, right) => random.Next(-1, 1)));
}
You can shuffle array after generating:
for (int i = 0; i < array1.Length; i++)
{
array1 = array1.OrderBy(c => random_istance.Next()).ToArray();
}
Well, I don't know if this would be a good solution, but you may try this:
Create random array with desired size.
Sort this array ascending.
Swap random elements i times, where i is a random number > 0.
To get decreasing numbers, create random array and just sort it descending.
Edit Of course it is possible, that you will end up with unchanged sequention if i is even. I think it is enough to swap elements i times, where i is greater than 0 and odd.

Categories

Resources