Ладно, вот уже несколько лет меня это беспокоит. Если в школе ты увлекался статистикой и высшей математикой, отвернись, Теперь. Слишком поздно.
Хорошо. Сделайте глубокий вдох. Вот правила. Возьмите тридцатигранный кубик два (да, они существуют) и бросьте их одновременно.
Если вы напишете какой-то код (см. Ниже), бросите эти кубики несколько миллионов раз и посчитаете, как часто вы получаете каждое число в качестве окончательного результата, вы получите довольно плоскую кривую слева от 1, около 45 градусов между 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
Знаю :) Я уже просил у профессоров статистику, и они не могли придумать ответа ...
это должны были быть «два профессора»






Что ж, посмотрим. Бросок второй (который иногда добавляется или вычитается из первого броска) имеет хорошую легко предсказуемую кривую колокола около 31. Первый бросок, конечно же, является проблемой.
Для первого броска у нас есть 900 возможных комбинаций.
Набор вычитания (предварительное вычитание) сформирует колоколообразную кривую в диапазоне (27..35). Нижняя половина добавляемого набора будет формировать колоколообразную кривую в диапазоне (2..10), а верхняя половина - колоколообразную кривую в диапазоне (52 ... 60).
Моя вероятность немного ржавая, поэтому я не могу определить для вас точные значения, но должно быть ясно, что они приводят к предсказуемым значениям.
Похоже, вы прочитали проблему так же, как и я, что неверно. Его исходный код показывает, что может быть третий, четвертый, пятый ...
Но все дополнительные броски имеют вероятность, которая быстро приближается к нулю, так что должна быть возможность связать их.
Эта попытка не так уж и плоха. Я могу подтвердить эти «дочерние» кривые колокола (вы можете легко увидеть их, импортировав вывод в Excel и отрендерив соответствующие столбцы). Теперь не хватает только правильной функции лаймов для рекурсивных вероятностей.
Сложная неограниченная вероятность ... нетривиальна. Я собирался решить эту проблему так же, как Джеймс Карран, но потом я увидел из вашего исходного кода, что может быть третий набор роликов, четвертый и так далее. Проблема разрешима, но выходит далеко за рамки большинства симуляторов прокатки штампа.
Есть ли какая-то особая причина, по которой вам нужен случайный диапазон от -Inf до + Inf с такой сложной кривой около 1-60? Почему колоколообразная кривая 2D30 неприемлема? Если вы объясните свои требования, вероятно, кто-то сможет предложить более простой и ограниченный алгоритм.
Нам нужно было что-то со следующими свойствами: - Возвращать значение от 0 до 100 (очень приблизительно; мы получили от 1 до 60, и это было «достаточно хорошо») - Простое выполнение много раз в игре - Значения ниже 0 должны быть действительно редкими
Мне пришлось сначала переписать ваш код, прежде чем я смог его понять:
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 диктоватьs?
В любом случае, оставим вопросы стиля программирования: предположим, что 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 (xab), пары с одним ≤5 и одним ≥26 имеют терм a + b + G (xab), и у остальных есть такой термин, как (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), так что это тоже выходит.)
Может быть способ получше. Но все сказано и сделано, почему вы это делаете? Какой бы вид распределения вы ни выбрали, существуют красивые и простые распределения вероятностей с соответствующими параметрами, которые обладают хорошими свойствами (например, небольшая дисперсия, односторонние ошибки и т. д.). Нет причин создавать свою собственную специальную процедуру для генерации случайных чисел.
re: dict -> Поленился заполнить пробелы между клавишами в dict. Если вы запустите мой код, вы увидите, что некоторые результаты имеют счетчик 0. Итак, массив был намного проще.
Мне нужен генератор случайных чисел, который люди могут использовать во время ролевой игры. Генераторы работают достаточно хорошо; нам просто интересно, каковы его свойства.
... во время ролевой игры без компьютер :)
Я прочитал это несколько раз и не могу понять вас. Как получить "(1/30) (1/30) (5 + F (x-5))" ?? Почему вы прибавляете ценность броска к «F (x-5)»?
Вероятность события = сумма_ (по каждому способу возникновения этого события) {Вероятность такого события}. Итак, здесь F (x) = вероятность того, что «конечный результат» будет ≤x = sum_ (по всем возможным результатам первого броска) {(вероятность этого броска) * (что должно произойти в остальных бросках) }.
То есть, если в первом броске вы получите (2,3) (вероятность 1/900), то вы бросите снова - и то, что вы собираетесь делать с этого момента, является в точности исходной задачей (см. рекурсивный код / определение), и чтобы получить ≤x для исходного набора бросков, вы должны получить ≤x-5 в наборе бросков «с этого момента».
Ааа ... все проясняется. Вы говорите: чтобы получить окончательный результат F (5) (где 5 - это не первый бросок, а конечный результат), я беру p (5) (одиночный бросок), а затем мне нужно добавить F (0), потому что следующий roll должен дать 0 (иначе окончательный результат не будет 5). Правильный?
Я чувствую, что это много вопросов, но не могли бы вы написать сценарий Python, который это реализует? Или помогите написать?
Я провел базовую статистику по выборке из 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:
На основе всего 991 значения. Я мог бы втиснуть в него больше значений, но они исказили бы результат. Этот образец оказался довольно типичным.
Изменить 1:
вот значения, указанные выше для одного шестидесятигранного кристалла, для сравнения:
Median: 30.5
Arithmetic Mean: 30.5
Standard Deviation: 7.68114574787
Root Mean Square: 35.0737318611
Обратите внимание, что эти значения являются расчетными, а не экспериментальными.
Кривая, которую вы описываете, представляет собой кумулятивную вероятность. Кривая плотности вероятности для этих данных интересна. Он имеет два четких пика (один на 26 и один на 36) с впадиной в середине (31). Уже одно это заставляет меня думать, что ваш ответ будет трудно определить!