C#'s Random producing recognisable pattern when using sequential seeds - c#

Given the following:
for(int seed = 0; seed < 100; seed++) {
var random = new Random(seed);
var roll = (random.Next() % 6) + 1;
Console.Write(roll + " ");
}
I'm seeing a clear pattern in the output:
1 1 2 2 3 3 4 4 5 5 6 6 1 1 1 2 2 3 3 4 4 5 5 6 6
1 1 2 2 3 3 4 4 5 5 5 6 6 1 1 2 2 3 3 4 4 5 5 6 6
1 1 2 2 3 3 4 4 4 5 5 6 6 1 1 2 2 3 3 4 4 5 5 6 6
1 1 2 2 3 3 3 4 4 5 5 6 6 1 1 2 2 3 3 4 4 5 5 6 6
I do understand that recreating a new Random object every time with a sequential seed isn't the "proper" way of using the class, and that strictly speaking I should be keep the Random instance around, calling Next() on it each time.
However, my use case is that I need to be able to exit my program and restore the state at any moment, and continue the sequence of numbers.
I also need the user to be able to set a seed manually, so I was hoping to be able to do something like:
var random = new Random(userSeed + nextValueIndex);
var chosenValue = random.Next();
I've also tried random.Next(min, max), and it gives different results, but also in a clear pattern.
Clearly my approach isn't the right way to use the Random class, but what is, for my use case?
I understand that random number generation is a very complex topic, so while I'm sort of interested in an explanation of why this is happening, I'm most interested in a practical (easy?) solution ;-)

Here's my solution, which seems to work nicely. Rather than seeding Random with userSeed + index (where index counts from zero), I simply cache the exact result from Random as previousRandom, and then seed with userSeed + previousRandom. That way, I get nice big numbers as the seed, and it avoids the sequential pattern. (previousRandom is initialised as zero, but that's not a problem.)
So, this is roughly what my code looks like:
var nextSeed = userSeed + this.previousRandom;
var randomObj = new Random(nextSeed);
var nextRandomValue = randomObj.Next ();
var chosenValue = (nextRandomValue % randomRange) + rangeMin;
this.previousRandom = nextRandomValue;
return chosenValue;
The one remaining thing I should probably do is make sure that userSeed + previousRandom doesn't overflow (or overflows predictably), because previousRandom might get very close to int's upper bound (up to whatever range I allow for userSeed).

Related

Why Random range give same output value every time? [duplicate]

This question already has answers here:
Random.Next returns always the same values [duplicate]
(4 answers)
Closed 8 months ago.
Here is the code which i used to for implementing Random class
class Program
{
public static void Main()
{
for (int j = 0; j < 5; j++)
{
foreach (var item in GenerateRandomList(new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }))
Console.Write(item + " ");
Console.WriteLine(" ");
}
}
public static List<int> GenerateRandomList(List<int> arr)
{
var random = new Random(new Random().Next(1,10000));
var ls = new List<int>();
while(arr.Count>0)
{
var index = random.Next(0, arr.Count-1);
ls.Add(arr[index]);
arr.RemoveAt(index);
}
return ls;
}
}
below are the results I am getting
first time
4 3 8 2 1 6 7 5 9 0
4 3 8 2 1 6 7 5 9 0
4 3 8 2 1 6 7 5 9 0
4 3 8 2 1 6 7 5 9 0
4 3 8 2 1 6 7 5 9 0
second time
6 3 4 2 8 5 9 1 7 0
6 3 4 2 8 5 9 1 7 0
6 3 4 2 8 5 9 1 7 0
6 3 4 2 8 5 9 1 7 0
6 3 4 2 8 5 9 1 7 0
third time
9 2 4 8 1 6 3 5 7 0
9 2 4 8 1 6 3 5 7 0
9 2 4 8 1 6 3 5 7 0
9 2 4 8 1 6 3 5 7 0
7 1 3 6 5 2 8 9 4 0
and So on..
what am i Missing?
sometimes it give different result but again it starts to repeat previous result.
new Random() creates a new random number generator instance, with the default seed.
In .NET Framework, the default seed value is time-dependent. In .NET Core, the default seed value is produced by the thread-static, pseudo-random number generator. From docs:
If the same seed is used for separate Random objects, they will generate the same series of random numbers.
If the default seed is time-based, and you create five Random objects in a quick succession, each of your generators will get the same seed (since time is not as precise that each instruction executes on a different timestamp), and thus produce the same pseudorandom number sequence.
You will want to create a Random instance only once; for example, as a class field.

How do I make a round robin match schedule?

I am building a tournament schedule with round robin style match order. I have an algorithm set up to build the matches, however the match order is not what I am looking for. I am struggling to develop an algorithm that will build the matches in my desired order. See example below with a 6 team bracket. Each vertical line represents a row in the tournament. The far left number represents the base team seed and who they will play in each round.
Note: The only thing that is really important to me is that the 1 and 2 seed play in the last round of the tournament. And preferably that 1 v 6, 2 v 5, 3 v 4 happens in the first round of the tournament. All other matches aren't as important. Thank you in advance for any help you can provide.
1: 6 5 4 3 2
2: 5 4 3 6 1
3: 4 6 2 1 5
4: 3 2 1 5 6
5: 2 1 6 4 3
6: 1 3 5 2 4
Here is my current code:
int numTeams = teamList.Count;
int rounds = (numTeams - 1);
int halfSize = numTeams / 2;
List<Team> teams = new List<Team>();
teams.AddRange(teamList); // Copy all the elements.
teams.RemoveAt(0); // To exclude the first team.
int teamSize = teams.Count;
for (int round = 0; round < rounds; round++)
{
int teamIdx = round % teamSize;
Team baseTeam1 = teams[teamIdx];
Team baseTeam2 = teamList[0];
// save each team to a match
for (int idx = 1; idx < halfSize; idx++)
{
int firstTeamIdx = (round + idx) % teamSize;
int secondTeamIdx = (round + teamSize - idx) % teamSize;
Team subTeam1 = teams[firstTeamIdx];
Team subTeam2 = teams[secondTeamIdx];
// save each team to a match
}
}
Sometimes asking the question helps figure out the answer. As it turns out, my current algorithm was creating what I wanted, just in opposite order. What I did to fix was create a new varible inside the first for loop called actualRound:
int actualRound = rounds - round; // this will reverse the round order

Lookup from a 3-column table and interpolate one of the columns?

I have a table with 3 columns: Temperature, Pressure, and Gain.
interface IEntry
{
public float Temperature {get;}
public int Pressure {get;}
public float Gain {get;}
}
Given a certain "input" pair (Temperature_input, Pressure_input), I would like to match this pair with the ones in the table and lookup the corresponding gains, interpolating in certain cases.
Example:
T P G
3 1 0
3 2 1
3 3 2
3 5 4
6 1 0
6 2 2
6 3 3
8 1 0
8 2 3
9 1 0
Input pair (T = 4, P = 2):
Step 1: As the exact temperature T = 4 is not in the table, the immediate lower and upper temperatures 3 and 6 are examined (8 table entries)
Step 2: The pairs for T = 3, T = 6 are sorted by Pressure (which is an int)
3 1 0
6 1 0
3 2 1
6 2 2
3 3 2
6 3 3
3 5 4
3 5 4
Step 3: Linear interpolation between temperatures of the same pressure:
3 2 1
6 2 2
Result is the interpolated Gain = 1 + (2-1)/(6-3) = 1.33
What is an efficient way of implementing the lookup part of the table, that will be accessed millions of times?

permutations of k objects from a set of n algorithm

I'm looking for an efficient way to achieve this:
you have a set of numbers, let's say that our set is equal to 4 (N = 4);
you have to generate all permutations of 3 elements (K = 3);
Output for N = 4 and K = 3:
1 2 3
1 2 4
1 3 2
1 3 4
1 4 2
1 4 3
2 1 3
2 1 4
2 3 1
2 3 4
2 4 1
2 4 3
3 1 2
3 1 4
3 2 1
3 2 4
3 4 1
3 4 2
4 1 2
4 1 3
4 2 1
4 2 3
4 3 1
4 3 2
Anyone have a great, nice'n'quick algorithm up their sleeve or web reference??
Thanks!
Something like this pseudocode:
permute(set, output, len) //output will hold all the permutations
for each number in the set do
choose number and store it at output[0]
if(!empty(set))
call permute(set{without the number}, output + (len - 1)!, len-1) //adjust the position
Invoke by permute(set, output, k)

Generate N random and unique numbers within a range

What is an efficient way of generating N unique numbers within a given range using C#? For example, generate 6 unique numbers between 1 and 50. A lazy way would be to simply use Random.Next() in a loop and store that number in an array/list, then repeat and check if it already exists or not etc.. Is there a better way to generate a group of random, but unique, numbers?
To add more context, I would like to select N random items from a collection, using their index.
thanks
Take an array of 50 elements: {1, 2, 3, .... 50}
Shuffle the array using any of the standard algorithms of randomly shuffling arrays. The first six elements of the modified array is what you are looking for. HTH
For 6-from-50, I'm not too sure I'd worry about efficiency since the chance of a duplicate is relatively low (30% overall, from my back-of-the-envelope calculations). You could quite easily just remember the previous numbers you'd generated and throw them away, something like (pseudo-code):
n[0] = rnd(50)
for each i in 1..5:
n[i] = n[0]
while n[1] == n[0]:
n[1] = rnd(50)
while n[2] == any of (n[0], n[1]):
n[2] = rnd(50)
while n[3] == any of (n[0], n[1], n[2]):
n[3] = rnd(50)
while n[4] == any of (n[0], n[1], n[2], n[3]):
n[4] = rnd(50)
while n[5] == any of (n[0], n[1], n[2], n[3], n[4]):
n[5] = rnd(50)
However, this will break down as you move from 6-from-50 to 48-from-50, or 6-from-6, since the duplicates start getting far more probable. That's because the pool of available numbers gets smaller and you end up throwing away more and more.
For a very efficient solution that gives you a subset of your values with zero possibility of duplicates (and no unnecessary up-front sorting), Fisher-Yates is the way to go.
dim n[50] // gives n[0] through n[9]
for each i in 0..49:
n[i] = i // initialise them to their indexes
nsize = 50 // starting pool size
do 6 times:
i = rnd(nsize) // give a number between 0 and nsize-1
print n[i]
nsize = nsize - 1 // these two lines effectively remove the used number
n[i] = n[nsize]
By simply selecting a random number from the pool, replacing it with the top number from that pool, then reducing the size of the pool, you get a shuffle without having to worry about a large number of swaps up front.
This is important if the number is high in that it doesn't introduce an unnecessary startup delay.
For example, examine the following bench-check, choosing 10-from-10:
<------ n[] ------>
0 1 2 3 4 5 6 7 8 9 nsize rnd(nsize) output
------------------- ----- ---------- ------
0 1 2 3 4 5 6 7 8 9 10 4 4
0 1 2 3 9 5 6 7 8 9 7 7
0 1 2 3 9 5 6 8 8 2 2
0 1 8 3 9 5 6 7 6 6
0 1 8 3 9 5 6 0 0
5 1 8 3 9 5 2 8
5 1 9 3 4 1 1
5 3 9 3 0 5
9 3 2 1 3
9 1 0 9
You can see the pool reducing as you go and, because you're always replacing the used one with an unused one, you'll never have a repeat.
Using the results returned from that as indexes into your collection will guarantee that no duplicate items will be selected.
var random = new Random();
var intArray = Enumerable.Range(0, 4).OrderBy(t => random.Next()).ToArray();
This array will contain 5 random numbers from 0 to 4.
or
var intArray = Enumerable.Range(0, 10).OrderBy(t => random.Next()).Take(5).ToArray();
This array will contain 5 random numbers between 0 to 10.
int firstNumber = intArray[0];
int secondNumber = intArray[1];
int thirdNumber = intArray[2];
int fourthNumber = intArray[3];
int fifthNumber = intArray[4];
For large sets of unique numbers, put them in an List..
Random random = new Random();
List<int> uniqueInts = new List<int>(10000);
List<int> ranInts = new List<int>(500);
for (int i = 1; i < 10000; i++) { uniqueInts.Add(i); }
for (int i = 1; i < 500; i++)
{
int index = random.Next(uniqueInts.Count) + 1;
ranInts.Add(uniqueInts[index]);
uniqueInts.RemoveAt(index);
}
Then randomly generate a number from 1 to myInts.Count. Store the myInt value and remove it from the List. No need to shuffle the list nor look to see if the value already exists.
instead of using List use Dictionary!!
In case it helps anyone else, I prefer allocating the minimum number of items necessary. Below, I make use of a HashSet, which ensures that new items are unique. This should work with very large collections as well, up to the limits of what HashSet plays nice with.
public static IEnumerable<int> GetRandomNumbers(int numValues, int maxVal)
{
var rand = new Random();
var yieldedValues = new HashSet<int>();
int counter = 0;
while (counter < numValues)
{
var r = rand.Next(maxVal);
if (yieldedValues.Add(r))
{
counter++;
yield return r;
}
}
}
generate unique random nos from 1 to 40 :
output confirmed :
class Program
{
static int[] a = new int[40];
static Random r = new Random();
static bool b;
static void Main(string[] args)
{
int t;
for (int i = 0; i < 20; i++)
{
lab: t = r.Next(1, 40);
for(int j=0;j<20;j++)
{
if (a[j] == t)
{
goto lab;
}
}
a[i] = t;
Console.WriteLine(a[i]);
}
Console.Read();
}
}
sample output :
7
38
14
18
13
29
28
26
22
8
24
19
35
39
33
32
20
2
15
37

Categories

Resources