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.
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.
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.
C# random numbersvar rand = new Random(); Console. WriteLine(rand. NextDouble()); Console.
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%
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With