Reputation: 1893
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.
Upvotes: 23
Views: 915
Reputation: 1062550
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%
Upvotes: 34