dice rolling problem

J

Jose Durazo

Hello, I'm doing an exercise to simulate rolling a pair of dice 36,000
times, then count and display how many times the simulation rolls each
possible sum.

For some reason each time I run my simulation, one or more of my sums is
zero, which is highly unlikely. So I'm sure there's a bug in my code. But
I can't tell what it is, even after re-reading it several times. Can
someone give me a hint?

Here is my code:

static void SimulateDiceRolls()
{
// When you roll two dice, there are 11 possible sums:
// 2, 3, ..., 10, 11, 12. Define an array of 13 elements
// where the first two elements are unused, but the third element
// where index equals 2, corresponds to sum = 2, and so on.
// make sure they're initialized to 0, which C# does by default
int[] sum = new int[13];

// for 36,000 times, roll two dice, calculate the sum,
// and increment the index corresponding to that sum.
int currentResult = 0;
for (int numberOfRolls = 1; numberOfRolls <= 36000; ++numberOfRolls)
{
currentResult = RollTwoDice();
++sum[currentResult];
}

// print how many times each sum comes up and what percent of the time
// to a 3 decimal precision that sum comes up
for (int sumIndex = 2; sumIndex <= 12; ++sumIndex)
{
Console.WriteLine("{0,5} {1,5} {2:F3}", sumIndex, sum[sumIndex],
(double)sum[sumIndex] / 36000 * 100);
}
}

// return the sum of two randomly generated integers from 1 to 6
// inclusive
static int RollTwoDice()
{
Random r = new Random();
return r.Next(1, 7) + r.Next(1, 7);
}

Thanks in advance,
Jose
 
J

Jose Durazo

I changed my code to the folllowing which fixes the problem.
But I don't care about getting the program to work as much as I would like
to know what I did wrong in my first example. Can anyone help explain what
I did wrong in the code from my earlier post?

Thanks!

Working code:

static void SimulateDiceRolls()
{
// When you roll two dice, there are 11 possible sums:
// 2, 3, ..., 10, 11, 12. Define an array of 13 elements
// where the first two elements are unused, but the third element
// where index equals 2, corresponds to sum = 2, and so on.
// make sure they're initialized to 0, which C# does by default
int[] sum = new int[13];
Random r = new Random();

// for 36,000 times, roll two dice, calculate the sum,
// and increment the index corresponding to that sum.
int currentResult = 0;
for (int numberOfRolls = 1; numberOfRolls <= 36000; ++numberOfRolls)
{
currentResult += r.Next(1, 7);
currentResult += r.Next(1, 7);
++sum[currentResult];
currentResult = 0;
}

// print how many times each sum comes up and what percent of the time
// to a 2 decimal precision that sum comes up
for (int sumIndex = 2; sumIndex <= 12; ++sumIndex)
{
Console.WriteLine("{0,5} {1,5} {2:F3}", sumIndex, sum[sumIndex],
(double)sum[sumIndex] / 36000 * 100);
}
}
 
M

Marc Gravell

I believe the problem is your usage of Random; for tight-loops you need
to use a single instance, otherwise successive objects in a similar
time interval (tiny amounts of time apart) will have the same seed.

This shoud fix it:

static readonly Random _rand = new Random();
static int RollTwoDice()
{
return _rand.Next(1,7) + _rand.Next(1,7);
}

Marc
 
J

Jose Durazo

I see. So my super short loop would call RollTwoDice(), increment a
counter, and then, say, before the next millisecond, it would call
RollTwoDice(), perhaps many times before a new seed would be used. So
given that usually two or three out of 11 dice sums never got rolled in
36,000 tries, I'd guess one number got rolled several thousand times in a
row, then another number several thousand times after that, and so on.

I'll make a version of my original program that will tell me if this is the
case.

Thanks for pointing me in what really looks like the right direction!

-Jose
 
J

Jose Durazo

I put a Console.Write() statement in my loop which tells me which number was
generated i each iteration. This makes each iteration take a lot longer,
but still each "random" number was generated dozens of times in a row. So
you were right!

Once again, thanks Marc.

-Jose
 
M

Marc Gravell

An oft-found trap ;-p

Just for completeness, Jon Skeet has a StaticRandom class in one of
his tool-bags which may be of interest to anyone using occasional
random numbers (useful where this type of private Random instance is
inconvenient due to e.g. threading / scoping constraints). Assuming
your code is not threaded, then for this type of tight loop I would
recommend the solution already posted (a "local" fixed instance),
purely to avoid the overhead of locking etc associated with static
utilities; OK, an uncontested lock is still fast, but not locking at
all (where it isn't necessary) is faster. If your code *is* threaded,
then you should probably note the risk of two calls to the *same*
Random.Next at the same time, which may not be safe; in this case you
could either:
* do your own local locking in the static method
* use StaticRandom
* (my preferred option) give each thread a separate *instance* of the
DiceRoller class (or whatever), so that they don't conflict and each
has an uncontested (instance) _rand that doesn't need locking.

In the general case, static methods should be thread safe. Looking at
http://msdn2.microsoft.com/en-us/library/2dx6wyd4.aspx, it doesn't
make any claims about thread-safety, so I have to assume that it isn't
thread-safe, which means the code I posted also isn't. Oops.

http://www.yoda.arachsys.com/csharp/miscutil/
http://www.yoda.arachsys.com/csharp/threads/

Marc
 
F

Fabio Z

Hello, I'm doing an exercise to simulate rolling a pair of dice 36,000
times, then count and display how many times the simulation rolls each
possible sum.

For some reason each time I run my simulation, one or more of my sums is
zero, which is highly unlikely. So I'm sure there's a bug in my code.
But I can't tell what it is, even after re-reading it several times. Can
someone give me a hint?

For the truth you are rolling the same dice 72000 times, since the random
object is always the same.

Why you do a r.Next(1, 7) while a dice has only 6 faces?
 
M

Marc Gravell

OP>Hello, I'm doing an exercise to simulate rolling a pair of dice
36,000
OP>times,
For the truth you are rolling the same dice 72000 times, since the
random object is always the same.

The random object being the same doesn't allow you to make this
statement.
Mathematically, unless the dice know about eachother and the results
directly influence eachother, then there is no difference in rolling
one die twice versus rolling two dice once. I think therefore that the
OPs statement is perfectly valid. However, given I already posted the
answer to this, you could state that "many of your successive
observations are based on the same roll of 2 dice, themselves
separate", meaning that the OP might have observed "1+5,1+5,...1+5,
3+3, 3+3, ..., 3+3, 4+4, 4+4, ... 4+4" etc.
Why you do a r.Next(1, 7) while a dice has only 6 faces?

The upper number to Random.Next is exclusive; hence Next(1,7) will
return a number in the range "[1,7)", or the set "{1,2,3,4,5,6}".

Marc
 
D

DeveloperX

r.Next(x,y) returns a minimum of x and a maximum of < y, so in this
case it will return 1 to 6.
 
M

Marc Gravell

Ammendment; I see what you are trying to say in your "same dice 72000
times", but it is still incorrect; the OPs code actually rolls 36000
dice, each twice

Again, at the math level it isn't going to make the slightest damned
difference, but yes; to *truly* roll two dice you would need 2 Random
instances - however, you would need to be very careful when
initialising them, e.g.

Random dice1 = new Random(), dice2 = new Random(dice1.Next());

A simple dice1 = new Random(), dice2 = new Random() would almost
always end up reporting doubles each time "1+1, 5+5, 3+3" etc due to
the seed and call-frequency being equal. But again: mathematically it
makes no distinction (1 twice versus 2 once), *provided* Random
provides a uniform distribution and lack-of-memory-function (i.e.
regardless of the first roll, the second roll is still uniform [over
the bigger picture; successive calls to Random are obviously
predictable on a 1-by-1 basis if you know the initial seed]).

Marc
 
F

Fabio Z

DeveloperX said:
r.Next(x,y) returns a minimum of x and a maximum of < y, so in this
case it will return 1 to 6.

I thinked so, but I was "deviated" by the intellisense description :)
 
F

Fabio Z

OP>Hello, I'm doing an exercise to simulate rolling a pair of dice 36,000
OP>times,


The random object being the same doesn't allow you to make this statement.
Mathematically, unless the dice know about eachother and the results
directly influence eachother, then there is no difference in rolling one
die twice versus rolling two dice once.

Yes, you're right, I just like OOP, so, 2 dices = 2 objects.
 
T

Tom Leylan

I see by the thread that you fixed the original problem. If you're
interested in tidying up the code you see where you do the following?
You're forcing yourself to reset currentResult to 0 simply because you used
the += operator on the first assignment.

int currentResult = 0;
for (int numberOfRolls = 1; numberOfRolls <= 36000; ++numberOfRolls)
{
currentResult += r.Next(1, 7);
currentResult += r.Next(1, 7);
++sum[currentResult];
currentResult = 0;
}

it could be this:

int currentResult = 0;
for (int numberOfRolls = 1; numberOfRolls <= 36000; ++numberOfRolls)
{
currentResult = r.Next(1, 7);
currentResult += r.Next(1, 7);
++sum[currentResult];
}

this:

int currentResult = 0;
for (int numberOfRolls = 1; numberOfRolls <= 36000; ++numberOfRolls)
{
currentResult = ( r.Next(1, 7) + r.Next(1, 7) );
++sum[currentResult];
}

of this eliminating currentResult altogether:

for (int numberOfRolls = 1; numberOfRolls <= 36000; ++numberOfRolls)
{
++sum[ ( r.Next(1, 7) + r.Next(1, 7) ) ];
}

Re: the rolling of each die independently... it's a simulation you don't
need to make it "real" in which case you can ask for a "Next" in the range
of 2 to 12. There is no reason to ask for two random numbers which you then
add.

for (int numberOfRolls = 1; numberOfRolls <= 36000; ++numberOfRolls)
{
++sum[ r.Next(2, 12) ];
}

Now modify SimulateDiceRolls() to accept the total number of rolls as a
parameter (so you could change the run size) and you're all set.


Jose Durazo said:
Hello, I'm doing an exercise to simulate rolling a pair of dice 36,000
times, then count and display how many times the simulation rolls each
possible sum.

For some reason each time I run my simulation, one or more of my sums is
zero, which is highly unlikely. So I'm sure there's a bug in my code.
But I can't tell what it is, even after re-reading it several times. Can
someone give me a hint?

Here is my code:

static void SimulateDiceRolls()
{
// When you roll two dice, there are 11 possible sums:
// 2, 3, ..., 10, 11, 12. Define an array of 13 elements
// where the first two elements are unused, but the third element
// where index equals 2, corresponds to sum = 2, and so on.
// make sure they're initialized to 0, which C# does by default
int[] sum = new int[13];

// for 36,000 times, roll two dice, calculate the sum,
// and increment the index corresponding to that sum.
int currentResult = 0;
for (int numberOfRolls = 1; numberOfRolls <= 36000; ++numberOfRolls)
{
currentResult = RollTwoDice();
++sum[currentResult];
}

// print how many times each sum comes up and what percent of the time
// to a 3 decimal precision that sum comes up
for (int sumIndex = 2; sumIndex <= 12; ++sumIndex)
{
Console.WriteLine("{0,5} {1,5} {2:F3}", sumIndex, sum[sumIndex],
(double)sum[sumIndex] / 36000 * 100);
}
}

// return the sum of two randomly generated integers from 1 to 6
// inclusive
static int RollTwoDice()
{
Random r = new Random();
return r.Next(1, 7) + r.Next(1, 7);
}

Thanks in advance,
Jose
 
F

Fabio

Re: the rolling of each die independently... it's a simulation you don't
need to make it "real" in which case you can ask for a "Next" in the range
of 2 to 12. There is no reason to ask for two random numbers which you
then add.

This is not true.
The sum of the different dices has not the same probability of a random
number between 2 to 12.
I.e. the most probable result of the sum is 7 because can be done with

1+6
2+5
3+4
4+3
5+2
6+1

the 2 and the 12 are the less probable because they can be done only with

1+1
6+6

For these reasons I suggested to do the 2 separated rolls.
 
B

Brian Gideon

Marc said:
The random object being the same doesn't allow you to make this
statement.
Mathematically, unless the dice know about eachother and the results
directly influence eachother, then there is no difference in rolling
one die twice versus rolling two dice once. I think therefore that the
OPs statement is perfectly valid. However, given I already posted the
answer to this, you could state that "many of your successive
observations are based on the same roll of 2 dice, themselves
separate", meaning that the OP might have observed "1+5,1+5,...1+5,
3+3, 3+3, ..., 3+3, 4+4, 4+4, ... 4+4" etc.

There's no particular reason I'm mentioning this, but conditional
probabilities can be very difficult to explain. The Monty Hall paradox
is a pretty good example that demonstrates how the wording of
probability problems can mislead even the most astute math minds.
 
T

Tom Leylan

Ah excellent point, my error, glad I wasn't be graded :) Important to
stick with the two independent rolls and the addition. I'd still eliminate
the interim variable.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top