Рассчитать точный результат сложного броска двух Д30

StackOverflow https://stackoverflow.com/questions/302379

Вопрос

Ладно, это беспокоило меня уже несколько лет.Если ты в школе проваливал статистику и высшую математику, отвернись, сейчас.Слишком поздно.

Хорошо.Сделайте глубокий вдох.Вот правила.Брать два тридцатигранный кубик (да, они существуют) и перекатывайте их одновременно.

  • Сложите два числа
  • Если на обоих кубиках выпало <= 5 или >= 26, бросьте еще раз и добавлять результат тот, что у вас есть
  • Если один <= 5, а другой >= 26, выдайте еще раз и вычесть результат от чего у вас есть
  • Повторяйте до тех пор, пока одно из значений не станет > 5 и < 26!

Если вы напишите какой-нибудь код (см. ниже), бросите эти кубики несколько миллионов раз и подсчитаете, как часто вы получаете каждое число в качестве окончательного результата, вы получите довольно плоскую кривую слева от 1, около 45° градусов между 1 и 1. 60 и ровно выше 60.Шанс выбросить 30,5 или выше составляет более 50%, выбросить лучше 18 — 80%, а выбросить лучше 0 — 97%.

Теперь вопрос:Можно ли написать программу вычислить тот точный значение f(x), т.е.вероятность выбросить определенное значение?

Фон:Для нашей ролевой игры «Джунгли звезд» мы искали способ контролировать случайные события.Приведенные выше правила гарантируют гораздо более стабильный результат для того, что вы попробуете :)

Для гиков, код на 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
Это было полезно?

Решение

Мне пришлось сначала переписать ваш код, прежде чем я смог его понять:

def OW60(sign=1):
    r1 = random.randint (1, 30)
    r2 = random.randint (1, 30)
    val = sign * (r1 + r2)

    islow  = (r1<=5)  + (r2<=5)
    ishigh = (r1>=26) + (r2>=26)

    if islow == 2 or ishigh == 2:
        return val + OW60(1)
    elif islow == 1 and ishigh == 1:
        return val + OW60(-1)
    else:
        return val

Возможно, вам это покажется менее читабельным;Я не знаю.(Проверьте, эквивалентно ли это тому, что вы имели в виду.) Кроме того, относительно того, как вы используете «результат» в своем коде: знаете ли вы о Python диктоватьс?

В любом случае, вопросы стиля программирования в стороне:Предположим, F(x) — это CDF OW60(1), т.е.

F(x) = the probability that OW60(1) returns a value ≤ x.

Аналогично пусть

G(x) = the probability that OW60(-1) returns a value ≤ x.

Затем вы можете вычислить F(x) из определения, суммируя все (30×30) возможные значения результата первого броска.Например, если первый бросок равен (2,3), то вы бросите еще раз, поэтому этот член вносит вклад (1/30)(1/30)(5+F(x-5)) в выражение для F( Икс).Таким образом, вы получите какое-то неприлично длинное выражение типа

F(x) = (1/900)(2+F(x-2) + 3+F(x-3) + ... + 59+F(x-59) + 60+F(x-60))

что представляет собой сумму более 900 членов, по одному на каждую пару (a,b) в [30]×[30].Пары (a,b) с обоими ≤ 5 или обоими ≥26 имеют терм a+b+F(x-a-b), пары с одним ≤5 и одним ≥26 имеют терм a+b+G(x-a-b), и у остальных есть член типа (a+b), потому что вы не бросаете снова.

Аналогично у вас есть

G(x) = (1/900)(-2+F(x-2) + (-3)+F(x-3) + ... + (-59)+F(x-59) + (-60)+F(x-60))

Конечно, вы можете собирать коэффициенты;единственные термины F, которые встречаются, это от F(x-60) до F(x-52) и от F(x-10) до F(x-2) (для a, b≥26 или обоих≤5), и единственные встречающиеся термины G - это от G (x-35) до G (x-27) (для одного из a, b≥26, а другого ≤5), поэтому терминов меньше, чем 30.В любом случае, определив вектор V(x) как

V(x) = [F(x-60) G(x-60) ... F(x-2) G(x-2) F(x-1) G(x-1) F(x) G(x)]

(скажем), у вас есть (из этих выражений для F и G) отношение вида

V(x) = A*V(x-1) + B

для соответствующей матрицы A и соответствующего вектора B (который вы можете вычислить), поэтому, начиная с начальных значений вида V(x) = [0 0] для достаточно малого x, вы можете найти F(x) и G(x ) для x в диапазоне, точность которого вы хотите произвольно уменьшить.(И ваша f(x), вероятность выбросить x, равна просто F(x)-F(x-1), так что это тоже получается.)

Возможно, есть лучший способ.Но все уже сказано и сделано, почему ты это делаешь?Какой бы тип распределения вы ни выбрали, существуют хорошие и простые распределения вероятностей с соответствующими параметрами, которые имеют хорошие свойства (например,небольшая дисперсия, односторонние ошибки и т. д.).Нет смысла создавать собственную специальную процедуру для генерации случайных чисел.

Другие советы

Я привел базовую статистику по выборке из 20 миллионов бросков.Вот результаты:

Median: 17 (+18, -?) # This result is meaningless
Arithmetic Mean: 31.0 (±0.1)
Standard Deviation: 21 (+1, -2)
Root Mean Square: 35.4 (±0.7)
Mode: 36 (seemingly accurate)

Погрешности определялись экспериментально.Среднее арифметическое и режим действительно точны, и изменение параметров, даже весьма агрессивное, похоже, не сильно на них влияет.Полагаю, поведение медианы уже объяснено.

Примечание:не принимайте эти числа за правильное математическое описание функции.Используйте их, чтобы быстро получить представление о том, как выглядит распределение.Во всем остальном они недостаточно точны (хотя могут быть и точными).

Возможно, это кому-то будет полезно.

Редактировать 2:

graph

На основе всего 991 значения.Я мог бы втиснуть в него больше значений, но они бы исказили результат.Этот образец оказался довольно типичным.

Редактировать 1:

вот для сравнения приведенные выше значения только для одного шестидесятигранного кубика:

Median: 30.5
Arithmetic Mean: 30.5
Standard Deviation: 7.68114574787
Root Mean Square: 35.0737318611

Обратите внимание, что эти значения расчетные, а не экспериментальные.

Сложная неограниченная вероятность - это...нетривиальный.Я собирался решить эту проблему так же, как Джеймс Карран, но затем из вашего исходного кода я увидел, что может быть третий набор бросков, четвертый и так далее.Проблема разрешима, но далеко за пределами большинства симуляторов прокатки штампов.

Есть ли какая-то особая причина, по которой вам нужен случайный диапазон от -Inf до +Inf с такой сложной кривой в районе 1-60?Почему колоколообразная кривая 2D30 неприемлема?Если вы объясните свои требования, вполне вероятно, что кто-то предложит более простой и ограниченный алгоритм.

Что ж, посмотрим.В второй Бросок (который иногда прибавляется или вычитается из первого броска) имеет красивую, легко предсказуемую колоколообразную кривую около 31.Конечно, проблема в первом рулоне.

Для первого броска у нас есть 900 возможных комбинаций.

  • 50 комбинаций приводят к добавлению второго броска.
  • 25 комбинаций приводят к вычитанию второго броска.
  • Оставляем 825 комбинаций, соответствующих колоколообразной кривой второго броска.

Набор вычитания (предварительное вычитание) сформирует колоколообразную кривую в диапазоне (27..35).Нижняя половина добавляемого набора будет формировать колоколообразную кривую в диапазоне (2..10), а верхняя половина будет формировать колоколообразную кривую в диапазоне (52...60).

Моя вероятность немного устарела, поэтому я не могу определить для вас точные значения, но должно быть ясно, что они приводят к предсказуемым значениям.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top