C
C#9mo ago
!Scofield

❔ Random problem

When choosing a random number in c#, I don't want the same number to come across again, how can I do it?
40 Replies
Mayor McCheese
Mayor McCheese9mo ago
Do you mean 1, 2, 3, 4 is okay; But 1, 2, 3, 4, 1 is not okay?
mtreit
mtreit9mo ago
Yes, define your exact requirements. How many random numbers do you expect to generate and does it matter what range of values they fall into? There are probably several ways of solving this depending on your exact needs.
wwww
wwww9mo ago
List<int> list = new List<int>();
Random rnd = new Random();

const int randmomNumbersAmount = 10;
const int randomLeftBorder = 0, randomRightBorder = 10;

while(list.Count < randmomNumbersAmount)
{
int number = rnd.Next(randomLeftBorder, randomRightBorder);
if (!list.Contains(number))
list.Add(number);
}
List<int> list = new List<int>();
Random rnd = new Random();

const int randmomNumbersAmount = 10;
const int randomLeftBorder = 0, randomRightBorder = 10;

while(list.Count < randmomNumbersAmount)
{
int number = rnd.Next(randomLeftBorder, randomRightBorder);
if (!list.Contains(number))
list.Add(number);
}
You can try something like this if I understand correctly what you want.
mtreit
mtreit9mo ago
A HashSet would be a much better data structure than a List for keeping track of the values that have already been used
Mayor McCheese
Mayor McCheese9mo ago
That's what I was thinking but I didn't want to type it out on my phone without being sure of reqs
Google
Google9mo ago
Fisher-Yates shuffle and call it a day
hiyosilver
hiyosilver9mo ago
How does shuffling help?
Mayor McCheese
Mayor McCheese9mo ago
Doesn't that require a known set of values first?
Google
Google9mo ago
1..int.maxvalue
!Scofield
!Scofield9mo ago
yes i want this I'm sorry for the late reply i will try thank you all in advance
Mayor McCheese
Mayor McCheese9mo ago
like @mtreit said, a HashSet will better handle the case of I've already got it
mtreit
mtreit9mo ago
How many random numbers do you need to generate? Because by the pigeon hole principle you will eventually have to repeat.
!Scofield
!Scofield9mo ago
estimated 45-50
mtreit
mtreit9mo ago
Do they need to be within a certain range?
!Scofield
!Scofield9mo ago
OK, let it be repeated, but for example, coming back to 5 after 5 is a problem for me. yes
mtreit
mtreit9mo ago
What's the range?
Mayor McCheese
Mayor McCheese9mo ago
1-2
mtreit
mtreit9mo ago
sus
Mayor McCheese
Mayor McCheese9mo ago
There's an infinite set of numbers between 1 and 2 3.14 to 3.15 is interesting too
!Scofield
!Scofield9mo ago
I don't understand much from c#. I'm working on Unity and I want to turn off the random number doubles in my game. It doesn't feel right for me to share because there are Unity codes rndm = Random.Range(1, 45);
mtreit
mtreit9mo ago
So you want to hand out 50 random numbers in the range of 1 to 45 without duplicates?
!Scofield
!Scofield9mo ago
I can explain like this. 34,23,43,43 I don't want the same number to appear side by side again. I want the incoming number to be less likely to come back Im really sorry if I cant explain but my english is not that clear or if a number comes up once, number must wait for the other 50 numbers to finish before it comes back
mtreit
mtreit9mo ago
I think you can achieve this by simply generating a pool of numbers, shuffling them, and then keeping a pointer into that shuffled set and using that to hand out the next value. Do you want to hand out more numbers than is in the range (like the example I gave?) Because that's obviously a problem since duplicates are guaranteed 🙂
!Scofield
!Scofield9mo ago
Yes it makes sense. I will do it. Thanks for your help No, I know I can't explain my problem fully, but I think I'd better do what you just said.
mtreit
mtreit9mo ago
I'm not sure it's the best approach, you could still end up with duplicates next to each other in the sequence. @!Scofield here is a completely brute force way to do this that will definitely work. It has theoretical performance problems if the same number that has already been sent keeps getting generated, but in your example it's probably fine.
wwww
wwww9mo ago
like, 34, 23, 43, 20, 43 is ok?
mtreit
mtreit9mo ago
using System;
using System.Collections.Generic;

namespace Test
{
class TestClass
{
static void Main()
{
var gen = new RandomGenerator(1, 45);
for (int i = 0; i < 100; i++)
{
Console.Write(gen.Next());
Console.Write(" ");
}
}
}

internal class RandomGenerator
{
int _numberSeen;
int _minVal;
int _maxVal;
HashSet<int> _previous;

public RandomGenerator(int minVal, int maxValInclusive)
{
_previous = new HashSet<int>();
_minVal = minVal;
_maxVal = maxValInclusive;
}

public int Next()
{
while (true)
{
var candidate = Random.Shared.Next(_minVal, _maxVal + 1);
var maxAllowed = _maxVal + 1 - _minVal;

if (_numberSeen >= maxAllowed)
{
_numberSeen = 0;
_previous.Clear();
Console.WriteLine();
}

if (!_previous.Contains(candidate))
{
_previous.Add(candidate);
_numberSeen++;
return candidate;
}
}
}
}
}
using System;
using System.Collections.Generic;

namespace Test
{
class TestClass
{
static void Main()
{
var gen = new RandomGenerator(1, 45);
for (int i = 0; i < 100; i++)
{
Console.Write(gen.Next());
Console.Write(" ");
}
}
}

internal class RandomGenerator
{
int _numberSeen;
int _minVal;
int _maxVal;
HashSet<int> _previous;

public RandomGenerator(int minVal, int maxValInclusive)
{
_previous = new HashSet<int>();
_minVal = minVal;
_maxVal = maxValInclusive;
}

public int Next()
{
while (true)
{
var candidate = Random.Shared.Next(_minVal, _maxVal + 1);
var maxAllowed = _maxVal + 1 - _minVal;

if (_numberSeen >= maxAllowed)
{
_numberSeen = 0;
_previous.Clear();
Console.WriteLine();
}

if (!_previous.Contains(candidate))
{
_previous.Add(candidate);
_numberSeen++;
return candidate;
}
}
}
}
}
Sample output:
44 6 31 5 19 29 17 43 35 34 2 1 39 23 27 13 11 45 40 26 37 22 36 18 24 16 20 7 32 28 12 25 15 8 10 30 41 42 14 3 33 4 38 9 21
4 34 13 1 43 19 42 39 26 32 5 2 15 28 33 44 38 21 35 41 10 25 3 18 9 37 36 45 27 29 11 7 12 17 24 8 23 20 16 30 14 22 31 40 6
44 28 20 26 25 43 6 34 33 39
44 6 31 5 19 29 17 43 35 34 2 1 39 23 27 13 11 45 40 26 37 22 36 18 24 16 20 7 32 28 12 25 15 8 10 30 41 42 14 3 33 4 38 9 21
4 34 13 1 43 19 42 39 26 32 5 2 15 28 33 44 38 21 35 41 10 25 3 18 9 37 36 45 27 29 11 7 12 17 24 8 23 20 16 30 14 22 31 40 6
44 28 20 26 25 43 6 34 33 39
No number will ever repeat until all possible values have been sent Performance of this seems totally fine after testing, I think it's a much better solution than my original suggestion.
Google
Google9mo ago
Fisher-Yates shuffle wins!
Mayor McCheese
Mayor McCheese9mo ago
Fyaseh-Resti Shuffle
mtreit
mtreit9mo ago
No, it was rejected
!Scofield
!Scofield9mo ago
Im really grateful, sorry if I stole your time
mtreit
mtreit9mo ago
Not at all, it was a fun problem to think about.
mtreit
mtreit9mo ago
Actually I guess if you re-shuffled after the entire sequence has been produced you would avoid repeating the same exact number sequence over and over again.
Google
Google9mo ago
Yep. It's deterministic.
mtreit
mtreit9mo ago
using System;

namespace Test
{
class TestClass
{
static void Main()
{
var gen = new RandomGenerator(1, 10);
for (int i = 0; i < 100; i++)
{
Console.Write(gen.Next());
Console.Write(" ");
}
}
}

internal class RandomGenerator
{
int _numberSeen;
int _minVal;
int _maxVal;
int[] _values;

public RandomGenerator(int minVal, int maxValInclusive)
{
_minVal = minVal;
_maxVal = maxValInclusive;
var maxAllowed = _maxVal + 1 - _minVal;
_values = new int[maxAllowed];
for (int i = minVal; i < _values.Length; i++)
{
_values[i] = i;
}

FisherYates(_values);
}

public int Next()
{
if (_numberSeen == _values.Length)
{
FisherYates(_values);
Console.WriteLine();
_numberSeen = 0;
}

return _values[_numberSeen++];
}

public void FisherYates<T>(T[] values)
{
for (int i = values.Length - 1; i >= 0; --i)
{
int n = Random.Shared.Next(i, values.Length);
Swap(i, n);
}

void Swap(int x, int y)
{
T tmp = values[x];
values[x] = values[y];
values[y] = tmp;
}
}
}
}
using System;

namespace Test
{
class TestClass
{
static void Main()
{
var gen = new RandomGenerator(1, 10);
for (int i = 0; i < 100; i++)
{
Console.Write(gen.Next());
Console.Write(" ");
}
}
}

internal class RandomGenerator
{
int _numberSeen;
int _minVal;
int _maxVal;
int[] _values;

public RandomGenerator(int minVal, int maxValInclusive)
{
_minVal = minVal;
_maxVal = maxValInclusive;
var maxAllowed = _maxVal + 1 - _minVal;
_values = new int[maxAllowed];
for (int i = minVal; i < _values.Length; i++)
{
_values[i] = i;
}

FisherYates(_values);
}

public int Next()
{
if (_numberSeen == _values.Length)
{
FisherYates(_values);
Console.WriteLine();
_numberSeen = 0;
}

return _values[_numberSeen++];
}

public void FisherYates<T>(T[] values)
{
for (int i = values.Length - 1; i >= 0; --i)
{
int n = Random.Shared.Next(i, values.Length);
Swap(i, n);
}

void Swap(int x, int y)
{
T tmp = values[x];
values[x] = values[y];
values[y] = tmp;
}
}
}
}
Here's the same program but using Fisher-Yates
Accord
Accord9mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.