Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is System.Random giving '1' a lot of times in a row, then not for a while, then again?

Tags:

Not sure how else to explain this, so the title pretty much describes the problem.

Random is not being re-initialised every part of the loop. It's a static member of a class which I always call on from other classes.

I am not using a custom seed.

The initialisation code is:

    public static Random random = new Random();          for (int x = 0; x < 75; x++)         {             if (main.random.Next(11) == 1)             {                 tiles[heightMap[x] - 1][x] = 4;                 tiles[heightMap[x] - 2][x] = 4;                 tiles[heightMap[x] - 3][x] = 4;                 tiles[heightMap[x] - 4][x] = 4;                 tiles[heightMap[x] - 5][x] = 4;                 tiles[heightMap[x] - 5][x - 1] = 5;                 tiles[heightMap[x] - 6][x - 1] = 5;                 tiles[heightMap[x] - 6][x] = 5;                 tiles[heightMap[x] - 5][x + 1] = 5;                 tiles[heightMap[x] - 6][x + 1] = 5;             }         } 

This (I am aware this is not a great way - it's rudimentary and temporary) generates a tree.

However my terrain often looks something like this, with many clustered trees:

☁☁☁☁🌲🌲🌲☁☁🌲🌲☁☁☁☁🌲🌲🌲

Can anyone give insight into why this is happening? Is there a better alternative than using the System.Security.Cryptography.Random class?

I'd expect an average of 9 gap per tree, but it's more like 7 and then 3 trees closely clustered together.

enter image description here

like image 248
Ashley Davies Avatar asked Apr 28 '12 21:04

Ashley Davies


People also ask

How does SystemRandom work?

SystemRandom is a class that uses the os. urandom() function to generate random numbers from sources provided by the operating system. This class does not rely on software state and the generated number sequences are not reproducible. This means that the seed() method has no effect and is ignored.

How Random class works in C#?

The current implementation of the Random class is based on a modified version of Donald E. Knuth's subtractive random number generator algorithm. Every time you do new Random() it is initialized using the clock. This means that in a tight loop you get the same value lots of times.

How to initialize Random in C#?

C# random numbersvar rand = new Random(); Console. WriteLine(rand. NextDouble()); Console.


1 Answers

This is a probability misunderstanding; all you know is that at any point, the chance of getting a tree in the next slot is, assuming uniform distribution, 1 in 11.

The chance of getting a gap of 0 is thus 1/11

The chance of getting a gap of 1 is thus 10/11 * 1/11

The chance of getting a gap of 2 is thus 10/11 * 10/11 * 1/11

etc

All those 10/11 add (well, multiply) up! So let's write a utility:

decimal accountedFor = 0M; for (int i = 0; i <= 20; i++) {     decimal chance = 1M / 11M;     for (int j = 0; j < i; j++) chance *= 10M / 11M;     accountedFor += chance;     Console.WriteLine("{0:00}: {1:00.0%}\t({2:00.0%})", i, chance, accountedFor); } 

Which gives:

00: 09.1%       (09.1%) 01: 08.3%       (17.4%) 02: 07.5%       (24.9%) 03: 06.8%       (31.7%) 04: 06.2%       (37.9%) 05: 05.6%       (43.6%) 06: 05.1%       (48.7%) 07: 04.7%       (53.3%) 08: 04.2%       (57.6%) 09: 03.9%       (61.4%) 10: 03.5%       (65.0%) 11: 03.2%       (68.1%) 12: 02.9%       (71.0%) 13: 02.6%       (73.7%) 14: 02.4%       (76.1%) 15: 02.2%       (78.2%) 16: 02.0%       (80.2%) 17: 01.8%       (82.0%) 18: 01.6%       (83.6%) 19: 01.5%       (85.1%) 20: 01.4%       (86.5%) 

which explains the bias for small gaps. Note; by the time we get up to a gap of size 20, we're into below 1.5% chance territory, and have accounted for 85% of all possible outcomes - the remaining 15% will be spread over the rest of infinity (i.e. a gap of size 13212 is possible, but very unlikely).

So here's a simulation:

int[] gapCounts = new int[21];  int gap = 0; // simulate a few gaps using your algo var random = new Random(); for (int x = 0; x < 100000; x++) {     if (random.Next(11) == 1)     { // count that gap         gapCounts[gap]++;         gap = 0;     }     else     {         gap++;         if(gap >= gapCounts.Length)         { // just skip anything too large, sorry             gap = 0;         }     } }  decimal total = gapCounts.Sum(); for(int i = 0 ; i < gapCounts.Length ; i++) {     Console.WriteLine("{0:00}: {1:00.0%}", i, gapCounts[i] / total); } 

with output nothing that these values will change every run:

00: 11.0% 01: 09.4% 02: 08.6% 03: 07.9% 04: 07.3% 05: 06.5% 06: 05.4% 07: 05.4% 08: 04.7% 09: 04.5% 10: 04.4% 11: 03.4% 12: 03.5% 13: 03.0% 14: 02.9% 15: 02.4% 16: 02.5% 17: 02.2% 18: 01.9% 19: 01.5% 20: 01.7% 
like image 186
Marc Gravell Avatar answered Jan 12 '23 00:01

Marc Gravell