Random number generator with no more than 2 duplicates - c#

I have code that randomly selects 15 sprites from 51 sprites. It can generate 2 duplicated sprites (this is acceptable for me) but I want to avoid 3 duplicated values. How can I prevent that?
My code
for (int i = 0; i < 16; i++)
{
int arrIndex = UnityEngine.Random.Range(0, tasSprites.Length);
tasSprite = tasSprites[arrIndex];
tasName = tasSprite.name;
taslar.Add(Int32.Parse(tasName));
}

Have you tried checking if each generated index already appears twice in taslar and if so, generating another one?
while (taslar.Count < 16)
{
int arrIndex = UnityEngine.Random.Range(0, tasSprites.Length);
tasSprite = tasSprites[arrIndex];
tasName = tasSprite.name;
int value = Int32.Parse(tasName);
if (taslar.Count(t => t == value) < 2)
{
taslar.Add(value);
}
}

You could do something like:
int[] indexesCount = new int[tasSprites.Length];
while (taslar.Count < 16)
{
int arrIndex = UnityEngine.Random.Range(0, tasSprites.Length);
if (indexesCount[arrIndex] == 2)
{
continue;
}
indexesCount[arrIndex]++;
tasSprite = tasSprites[arrIndex];
tasName = tasSprite.name;
taslar.Add(Int32.Parse(tasName));
}
Note that this solution is "good" while tasSprites.Length is relatively small. We are creating a temporary array of size tasSprites.Length to see which numbers have already been used.

Generate an array with each of your sprites in twice
Select 15 values from the array using e.g. a Fisher-Yates shuffle
This will save you the chance of possibly repeatedly generating values that have already been used twice.

Related

How to create unique pairs of numbers

I'm trying to create some pairs of unique numbers using pretty simple algorithm.
For some unknown reason after compiling Unity goes into an endless "not responding" state. Seems like it's stuck in a do..while loop, but I don't see any reason for that.
//Creating two lists to store random numbers
List<int> xList = new List<int>();
List<int> yList = new List<int>();
int rx, ry;
for(int i = 0; i < 10; i++)
{
// look for numbers until they are unique(while they are in lists)
do
{
rx = rand.Next(0, width);
ry = rand.Next(0, height);
}
while(xList.Contains(rx) || yList.Contains(ry));
//add them to lists
xList.Add(rx);
yList.Add(ry);
Debug.Log(rx + ", " + ry);
// some actions with these numbers
gridArray[rx,ry].isBomb = true;
gridArray[rx,ry].changeSprite(bombSprite);
}
As mentioned the issue is that once all unique numbers have been used once you are stuck in the do - while loop.
Instead you should rather simply
generate the plain index lists for all possible pairs.
I will use the Unit built-in type Vector2Int but you could do the same using your own struct/class
For each bomb to place pick a random entry from the list of pairs
Remove according random picked item from the pairs so it is not available anymore in the next go
Something like
// create the plain pair list
var pairs = new List<Vector2Int>(width * height);
for(var x = 0; x < width; x++)
{
for(var y = 0; y < height; y++)
{
pairs.Add(new Vector2Int(x,y));
}
}
// so now you have all possible permutations in one list
if(pairs.Count < BOMB_AMOUNT_TO_PLACE)
{
Debug.LogError("You are trying more bombs than there are fields in the grid!");
return;
}
// Now place your bombs one by one on a random spot in the grid
for(var i = 0; i < BOMB_AMOUNT_TO_PLACE; i++)
{
// now all you need to do is pick one random index from the possible entries
var randomIndexInPairs = Random.Range(0, pairs.Count);
var randomPair = pairs[randomIndexInPairs];
// and at the same time remove the according entry
pairs.RemoveAt(randomIndexInPairs);
// Now you have completely unique but random index pairs
var rx = randomPair.x;
var ry = randomPair.y;
gridArray[rx, ry].isBomb = true;
gridArray[rx, ry].changeSprite(bombSprite);
}
Depending on your use-case as alternative to generate the pairs list and then remove entries again you could also generate it once and then use
if(pairs.Count < BOMB_AMOUNT_TO_PLACE)
{
Debug.LogError("You are trying more bombs than there are fields in the grid!");
return;
}
var random = new System.Random();
var shuffledPairs = pairs.OrderBy(e => random.Next());
for(var i = 0; i < BOMB_AMOUNT_TO_PLACE; i++)
{
// then you can directly use
var randomPair = shuffledPairs[i];
// Now you have completely unique but random index pairs
var rx = randomPair.x;
var ry = randomPair.y;
gridArray[rx, ry].isBomb = true;
gridArray[rx, ry].changeSprite(bombSprite);
}
Although your algorithm is maybe not the best way to generate ten bombs in a grid, it should work.
The problem is that your while condition is using a OR statement, which means that if you have a bomb in the first line (in any column), it will not be able to add another bomb in that line.
Therefore you will pretty soon end up with an infinite loop because for every bomb you lock the line and column.
If you put an AND condition, you make sure the pair is unique because you lock only that cell.
Provided of course that width x height is more than ten.

C# Checking if there is same random value in the array or not

I want to check if there is same value in the array or not as I mentioned in the title. And if there is, I want to pass that value and check another random value to add to listbox.
In my form, there is 2 textBox, 1 listbox and 1 button. When button is clicked, listbox has to show random numbers up to sum of textbox1 and textbox2. For instance;
5 entered from textbox1 and 10 entered from textbox2. Sum is of course 15 and listbox has to show 15 random numbers but those numbers have to be different from each other.
I wrote something like that and used Contains method to check if there is same value or not. But the program froze and didn't give any error.
int a, b;
Random rnd = new Random();
int[] array;
private void button1_Click(object sender, EventArgs e)
{
a = Convert.ToInt32(textBox1.Text);
b = Convert.ToInt32(textBox2.Text);
int c = a + b;
array = new int[c];
for (int i = 0; i < array.Length; i++)
{
int number = rnd.Next(c);
foreach(int numbers in array)
{
if (array.Contains(numbers))
{
i--;
}
else
{
array[i] = number;
listBox1.Items.Add(array[i]);
}
}
}
I also did it without foreach(Only Contains part I mean). Also didn't work. I wrote in "else";
array[i] += number;
it also didn't work.
I would be very appreciated if you help me. Thanks in advance.
instead of a for loop, use a while loop:
int = 0;
while(i<c)
{
int random rnd.Next(c);
if(!array.Contains(random))
array[i++] = random;
}
you may also create a list of numbers from 1-15 and then shuffle them (as your random function will create only random numbers from 1-15 just random):
array = Enumberable.Range(0,c).OrderBy(x => rnd.Next()).ToArray();
The above code is much faster, because imagine that we have generated 14 random numbers and only one number (5 for instance) left, it has to go through loop several times so that finally random number that is generated equals to 5, but in the above code there is no need to check that, we just have all numbers and then we shuffle it.
You can try to use do...while instead of for loop
Random.Next get the value from 0 to c - 1, so rnd.Next(c + 1); need to add 1 otherwise, the loop will not be stopped.
var array = new int[c];
int number;
for (int i = 0; i < array.Length; i++)
{
do
{
number = rnd.Next(c + 1);
} while (array.Contains(number));
array[i] = number;
listBox1.Items.Add(array[i]);
}
You basically need to shuffle your data. Create a collection with all values:
var temp = Enumerable.Range(0, c);
Now order it by random
temp = temp.OrderBy(_ => rnd.Next());
Now you can add temp to your listBox
Or, as single line:
listBox1.Items.AddRange(Enumerable.Range(0, c).OrderBy(_ => rnd.Next()));

Random Integer, no repeat, within specific range

Detailed scenario:
There are 300 possible numbers that fit into the discription.
That is, the number must between 1123 and 5543 inclusive
The number is unique and not repeated
The number only has the integers 1,2,3,4 and 5.
What I am trying to achieve is a program that can display all those numbers at once in ascending order.
My current code:
var chars = "12345";
var stringChars = new char[4];
var random = new Random();
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
var finalString = new String(stringChars);
Console.WriteLine(finalString)
This code works fine, but I have 2 additional requirements.
Loop 300 times
Display all results in ascending order
EDIT:
Sorry for all confusion. What this code produces is a 4 digit number like I want. But I want to it to do so 300 times. I guess I can use a Loop, but this is my first time using C# (I've only used vb.net in the past). What I mean by random and in ascending order is: Produce a random number, and when all numbers are generated, order them in ascending order before displaying them all.
However, if ordering them will be too complicated, then I'm fine without that.
It's not entirely clear what you're asking, but here is some code that will print the 611 (not 300) numbers matching the description you gave in ascending order.
If I've understood your question correctly, you will want to sample 300 distinct elements from this larger set, sort them, and them print them out. You might look at using a Fisher-Yates shuffle to do this. Shuffle the list, take the first 300, and sort them.
public static void Main (string[] args) {
var digits = new [] { 1, 1, 2, 3 };
for (var num = DigitsToInt(digits); num <= 5543; num = DigitsToInt(digits)) {
Console.WriteLine(num);
for (int i = 3; i >= 0; i--) {
digits[i]++;
if (digits[i] < 6) {
break;
} else {
digits[i] = 1;
}
}
}
}
private static int DigitsToInt(int[] digits) {
return 1000 * digits[0] + 100 * digits[1] + 10 * digits[2] + digits[3];
}

Generating filled sudoku board with shuffled order of filling and not

So I am trying to figure out why filling sudoku board in order of how I generate fields is many times faster then filling it which shuffled order, and I can't come to any reasonable conclusions.
Speed of filling and checking should be about the same no matter if fields are shuffled or not, only noticeable difference is the fact that filling boxes in a shuffled order causes the main loop to easily go for 60.000.000+ iterations not sure how many on average as I am not patient enough to check. And filling them in order of how they were generated gets finished in usually < 1.000 iterations rarely going over it and basically never going over 5.000.
I do understand that code doesn't follow C# standards too strictly but it's a project I am supposed to make and it's not my main language there are probably some possible optimizations but it's not what I am interested in all i want to know why shuffling the order of fill the boxes causes the process to take this much longer.
Edit: forgot to attach order of creating/filling boxes order of filling/creating boxes:
Edit2: more concise rephrased question:
The question is why generating an valid fully filled sudoku board in order from image creates a lot less invalid boards from which there is required backtracking, as compared to generating a board in a random order of choosing fields?
example of random order
Here is the code used for the project https://github.com/Piterm21/sudoku
And here are all the parts used in generation.
filledBox class:
public class filledBox
{
public int textBoxIndex;
public int value;
public int nextIndexToTry;
public int[] values;
public int lineIndex;
public int columnIndex;
public int groupIndex;
}
Here is a main loop:
filledBox[] boxes = this.shuffleBoxesCreateCheckingLists();
long iter = 0;
int nextIndexToFill = 0;
while (nextIndexToFill != 81) {
for (; boxes[nextIndexToFill].nextIndexToTry < 9; boxes[nextIndexToFill].nextIndexToTry++) {
// check if is valid
if (!createsError(boxes[nextIndexToFill])) {
boxes[nextIndexToFill].value = boxes[nextIndexToFill].values[boxes[nextIndexToFill].nextIndexToTry];
nextIndexToFill++;
break;
}
if (boxes[nextIndexToFill].nextIndexToTry == 8) {
boxes[nextIndexToFill].nextIndexToTry = 0;
boxes[nextIndexToFill].value = 0;
nextIndexToFill--;
boxes[nextIndexToFill].nextIndexToTry++;
while (boxes[nextIndexToFill].nextIndexToTry == 9) {
boxes[nextIndexToFill].nextIndexToTry = 0;
boxes[nextIndexToFill].value = 0;
nextIndexToFill--;
boxes[nextIndexToFill].nextIndexToTry++;
}
break;
}
}
iter++;
}
System.Diagnostics.Debug.WriteLine(iter);
Generation of boxes with setting of lists used for checking for errors:
List<filledBox>[] generationLines;
List<filledBox>[] generationColumns;
List<filledBox>[] generationGroups;
public filledBox[] shuffleBoxesCreateCheckingLists()
{
filledBox[] boxes = new filledBox[81];
for (int i = 0; i < 81; i++) {
boxes[i] = new filledBox();
boxes[i].values = new int[9]{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
boxes[i].values = shuffle(boxes[i].values, true);
}
generationLines = new List<filledBox>[9];
generationColumns = new List<filledBox>[9];
generationGroups = new List<filledBox>[9];
for (int i = 0; i < 9; i++) {
generationLines[i] = new List<filledBox>();
generationColumns[i] = new List<filledBox>();
generationGroups[i] = new List<filledBox>();
}
int boxIndex = 0;
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
for (int innerY = 0; innerY < 3; innerY++) {
for (int innerX = 0; innerX < 3; innerX++) {
int subPanelIndex = x + y * 3;
int lineIndex = innerY + y * 3;
int columnIndex = innerX + x * 3;
boxes[boxIndex].textBoxIndex = boxIndex;
boxes[boxIndex].groupIndex = subPanelIndex;
boxes[boxIndex].columnIndex = columnIndex;
boxes[boxIndex].lineIndex = lineIndex;
boxes[boxIndex].nextIndexToTry = 0;
boxes[boxIndex].value = 0;
generationLines[lineIndex].Add(boxes[boxIndex]);
generationColumns[columnIndex].Add(boxes[boxIndex]);
generationGroups[subPanelIndex].Add(boxes[boxIndex]);
boxIndex++;
}
}
}
}
#if !fast
boxes = shuffle(boxes);
#endif
return boxes;
}
Shuffling code:
public T[] shuffle<T> (T[] array)
{
int i = array.Length;
while (i > 1) {
i--;
int j = rnd.Next(0, i - 1);
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
And code responsible for checking for errors:
public bool hasError (List<filledBox> list, filledBox box)
{
bool result = false;
foreach (filledBox filledBoxToCheck in list) {
if (filledBoxToCheck.value == box.values[box.nextIndexToTry]) {
if (box.lineIndex != filledBoxToCheck.lineIndex ||
box.columnIndex != filledBoxToCheck.columnIndex) {
result = true;
break;
}
}
}
return result;
}
public bool createsError (filledBox box)
{
bool result = false;
if (hasError(generationGroups[box.groupIndex], box)) {
result = true;
} else if (hasError(generationLines[box.lineIndex], box)) {
result = true;
} else if (hasError(generationColumns[box.columnIndex], box)) {
result = true;
}
return result;
}
The reason the shuffled order of visiting boxes is worse is that, for the first few boxes, you've increased the odds of filling them in independently. I'm calling a 3x3 region a box. You could visit each little square randomly, but I'll confine my analysis to fillng in one 3x3 box at a time. It should illustrate the problem.
Lets assume, whether you used the in-order or random method, you filled in the top left box first.
VISITING BOXES IN ORDER:
Now consider filling in the box next to it, that is, the top middle box. Just look at filling in the top row of that box, there are 6x5x4 = 120 ways to fill it in.
VISITING BOXES RANDOMLY:
Now consider instead choosing any box to fill in next. If you happen to choose a box that is not in the top row or left column, filling it in is unaffected by the way the first box was filled. The top row of that box can be filled in 9x8x7 = 504 ways.
Taking this further, if you fill in the boxes sequentially, say, across the top, the third box (the top right one), begins with only 3x2x1 = 6 ways to fill in the top row of it. Whereas, if you select the next box randomly, you might, at worst, select a box that is not in the same row or column of either of the first 2 boxes, which means that the top row of that box has yet again 9x8x7 = 504 ways of being filled in.
If you tried randomly selecting the order for each little square to be filled in, the first few might all be in different rows and columns. At worst, none of the first 9 squares will align meaning there will be 9^9 = 387,420,489 options. But filling them in across the top row, the choices are guaranteed to be 9! = 362,880.
This illustrates one of the strategies for how to approach backtracking. Ideally, you first solve parts of the problem that most tightly constrain the rest of the problem. For some problems sequential is better, and for some random is better.

How to: Assign a unique number to every entry in a list?

What I want is to make tiles. These tiles (about 30 of them) should have a fixed position in the game, but each time I load the game they should have random numbers that should affect their graphical appearance.
I know how to use the Random method to give a single tile a number to change its appearance, but I'm clueless on how I would use the Random method if I were to make a list storing the position of multiple tiles. How can you assign each entry in a list a unique random number?
I need this for my game where you're in a flat 2D map, generated with random types of rooms (treasure rooms, arena rooms etc.) that you are to explore.
Take a look at the Fisher-Yates shuffle. It's super easy to use and should work well for you, if I read your question right.
Make an array of 30 consecutive numbers, mirroring your array of tiles. Then pick an array-shuffling solution you like from, say, here for instance:
http://forums.asp.net/t/1778021.aspx/1
Then tile[23]'s number will be numberArray[23].
if you have something like this:
public class Tile
{
public int Number {get;set;}
...
}
you can do it like this:
var numbers = Enumerable
.Range(1, tilesList.Count) // generates list of sequential numbers
.OrderBy(x => Guid.NewGuid()) // shuffles the list
.ToList();
for (int i = 0; i < tiles.Count; i++)
{
tile[i].Number = numbers[i];
}
I know, that Guid is not a Random alternative, but it should fit this scenario.
Update: As long as answer was downvoted, I've wrote simple test, to check if Guids are not usable for shuffling an array:
var larger = 0;
var smaller = 0;
var start = DateTime.Now;
var guid = Guid.NewGuid();
for (int i = 0; i < 10000000; i++)
{
var nextGuid = Guid.NewGuid();
if (nextGuid.CompareTo(guid) < 0)
{
larger++;
}
else
{
smaller++;
}
guid = nextGuid;
}
Console.WriteLine("larger: {0}", larger);
Console.WriteLine("smaller: {0}", smaller);
Console.WriteLine("took seconds: {0}", DateTime.Now - start);
Console.ReadKey();
What it does, it counts how many times next guid is smaller than current and how many times is larger. In perfect case, there should be equal number of larger and smaller next guids, which would indicate, that those two events (current guid and next guid) are independent. Also measured time, just to make sure, that it is not too slow.
And got following result (with 10 million guids):
larger: 5000168
smaller: 4999832
took seconds: 00:00:01.1980686
Another test is direct compare of Fisher-Yates and Guid shuffling:
static void Main(string[] args)
{
var numbers = Enumerable.Range(1, 7).ToArray();
var originalNumbers = numbers.OrderBy(x => Guid.NewGuid()).ToList();
var foundAfterListUsingGuid = new List<int>();
var foundAfterListUsingShuffle = new List<int>();
for (int i = 0; i < 100; i++)
{
var foundAfter = 0;
while (!originalNumbers.SequenceEqual(numbers.OrderBy(x => Guid.NewGuid())))
{
foundAfter++;
}
foundAfterListUsingGuid.Add(foundAfter);
foundAfter = 0;
var shuffledNumbers = Enumerable.Range(1, 7).ToArray();
while (!originalNumbers.SequenceEqual(shuffledNumbers))
{
foundAfter++;
Shuffle(shuffledNumbers);
}
foundAfterListUsingShuffle.Add(foundAfter);
}
Console.WriteLine("Average matching order (Guid): {0}", foundAfterListUsingGuid.Average());
Console.WriteLine("Average matching order (Shuffle): {0}", foundAfterListUsingShuffle.Average());
Console.ReadKey();
}
static Random _random = new Random();
public static void Shuffle<T>(T[] array)
{
var random = _random;
for (int i = array.Length; i > 1; i--)
{
// Pick random element to swap.
int j = random.Next(i); // 0 <= j <= i-1
// Swap.
T tmp = array[j];
array[j] = array[i - 1];
array[i - 1] = tmp;
}
}
By "direct compare" I mean, that I'm producing shuffled sequence and try to shuffle again to get same sequence, and assume, that the more tries I need to produce same sequence, the better random is (which is not necessary mathematically correct assumption, I think it is oversimplification).
So results for small set with 1000 iterations to reduce error, was:
Average matching order (Guid): 5015.097
Average matching order (Shuffle): 4969.424
So, Guid performed event better, if my metric is correct :)
with 10000 iterations they came closer:
Average matching order (Guid): 5079.9283
Average matching order (Shuffle): 4940.749
So in my opinion, for current usage (shuffle room number in game), guids are suitable solution.

Categories

Resources