views:

388

answers:

4

Okay, this bugged me for several years, now. If you sucked in statistics and higher math at school, turn away, now. Too late.

Okay. Take a deep breath. Here are the rules. Take two thirty sided dice (yes, they do exist) and roll them simultaneously.

  • Add the two numbers
  • If both dice show <= 5 or >= 26, throw again and add the result to what you have
  • If one is <= 5 and the other >= 26, throw again and subtract the result from what you have
  • Repeat until either is > 5 and < 26!

If you write some code (see below), roll those dice a few million times and you count how often you receive each number as the final result, you get a curve that is pretty flat left of 1, around 45° degrees between 1 and 60 and flat above 60. The chance to roll 30.5 or better is greater than 50%, to roll better than 18 is 80% and to roll better than 0 is 97%.

Now the question: Is it possible to write a program to calculate the exact value f(x), i.e. the probability to roll a certain value?

Background: For our role playing game "Jungle of Stars" we looked for a way to keep random events in check. The rules above guarantee a much more stable outcome for something you try :)

For the geeks around, the code in Python:

import random
import sys

def OW60 ():
    """Do an open throw with a "60" sided dice"""
    val = 0
    sign = 1

    while 1:
        r1 = random.randint (1, 30)
        r2 = random.randint (1, 30)

        #print r1,r2
        val = val + sign * (r1 + r2)
        islow = 0
        ishigh = 0
        if r1 <= 5:
            islow += 1
        elif r1 >= 26:
            ishigh += 1
        if r2 <= 5:
            islow += 1
        elif r2 >= 26:
            ishigh += 1

        if islow == 2 or ishigh == 2:
            sign = 1
        elif islow == 1 and ishigh == 1:
            sign = -1
        else:
            break

        #print sign

    #print val
    return val

result = [0] * 2000
N = 100000
for i in range(N):
    r = OW60()
    x = r+1000
    if x < 0:
        print "Too low:",r
    if i % 1000 == 0:
        sys.stderr.write('%d\n' % i)
    result[x] += 1

i = 0
while result[i] == 0:
    i += 1

j = len(result) - 1
while result[j] == 0:
    j -= 1

pSum = 0
# Lower Probability: The probability to throw this or less
# Higher Probability: The probability to throw this or higher
print "Result;Absolut Count;Probability;Lower Probability;Rel. Lower Probability;Higher Probability;Rel. Higher Probability;"
while i <= j:
    pSum += result[i]
    print '%d;%d;%.10f;%d;%.10f;%d;%.10f' % (i-1000, result[i], (float(result[i])/N), pSum, (float(pSum)/N), N-pSum, (float(N-pSum)/N))
    i += 1
A: 

Well, let's see. The second throw (which will sometimes be added or subtracted to the first roll) has a nice easily predictable bell curve around 31. The first roll, of course, is the problem.

For the first roll, we have 900 possible combinations.

  • 50 combinations result in adding the second roll.
  • 25 combinations result in subtracting the second roll.
  • Leaving 825 combinations which match the bell curve of the second roll.

The subtracting set (pre-subtraction) will form a bell curve in the range (27..35). The lower half of the adding set will form a bell curve in the range (2..10), while the upper half will form a bell curve in the range (52...60)

My probablity is a bit rusty, so I can't figure the exact values for you, but it should be clear that these lead to predictable values.

James Curran
You seem to have read the problem the same way I did, which is incorrect. His source code illustrates that there may be a third roll, or a fourth, or a fifth...
Sparr
But the additional rolls all have a probability which quickly approaches 0. So it should be possible to bound them.
Aaron Digulla
This attempt is not so bad. I can confirm these "child" bell curves (you can easily see them by importing the output in Excel and rendering the respective columns). Now all that is missing is the correct limes function for the recursive probabilities.
Aaron Digulla
+1  A: 

Compound unbounded probability is... non-trivial. I was going to tackle the problem the same way as James Curran, but then I saw from your source code that there could be a third set of rolls, and a fourth, and so on. The problem is solvable, but far beyond most die rolling simulators.

Is there any particular reason that you need a random range from -Inf to +Inf with such a complex curve around 1-60? Why is the bell curve of 2D30 not acceptable? If you explain your requirements, it is likely someone could provide a simpler and more bounded algorithm.

Sparr
We needed something with these properties:- Return a value between 0 and 100 (very roughly; we got 1 to 60 and that was "good enough")- Simple to execute many time in a game- Values below 0 should be really rare
Aaron Digulla
+6  A: 
ShreevatsaR
re: dict -> I was too lazy to fill the gaps between the keys in the dict. If you run my code, you'll see that some results have a count of 0. So an array was much more simple.
Aaron Digulla
I need a random number generator which people can use during a role playing game. The generators works well enough; we just wondered what its properties were.
Aaron Digulla
... during a role playing game *without* a computer :)
Aaron Digulla
I've read this several times, now, and I can't quite follow you. How do you get "(1/30)(1/30)(5+F(x-5))"?? Why do you add the value of the roll to "F(x-5)"?
Aaron Digulla
Probability of an event = sum_(over each way of that event happening){Probability of that way}.So here, F(x) = the probability of the "final result" being ≤x = sum_(over all possible results of the first throw){(probability of that throw)*(what should happen in the rest of the throws)}.
ShreevatsaR
That is, if in the first throw you get (2,3) (prob. 1/900), then you'll throw again-- and what you're going to do from now on is exactly the original problem (look at the recursive code/definition), and to get ≤x for the original set of throws, you should get ≤x-5 in the "from now on" set of throws.
ShreevatsaR
Ahhh... things are clearing up. You say: To get the final result of F(5) (with 5 being not the first throw but the final result), I take p(5) (single roll) and then I have to add F(0) because the next roll must yield 0 (or the final result wouldn't be 5). Correct?
Aaron Digulla
I feel this is a lot to ask but could you write a Python script which implements this? Or help me write one?
Aaron Digulla
Correct, your interpretation is exactly right
ShreevatsaR
+2  A: 
stefano palazzo