Расчет распределения шансов с 6-сторонними кубиками - PullRequest
4 голосов
/ 21 апреля 2010

Я пытаюсь рассчитать распределение шансов меняющегося числа 6-ти сторонних бросков кубика.Например, 3d6 варьируется от 3 до 18 следующим образом:

3:1, 4:3, 5:6, 6:10, 7:15, 8:21, 9:25, 10:27, 11:27, 12:25, 13:21, 14:15, 15:10, 16:6, 17:3, 18:1

Я написал эту программу php для ее вычисления:

function distributionCalc($numberDice,$sides=6) {
for ( $i=0; $i<pow($sides,$numberDice); $i++)
    {
    $sum=0;
    for  ($j=0; $j<$numberDice; $j++)
        { $sum+=(1+(floor($i/pow($sides,$j))) % $sides); }
    $distribution[$sum]++;
    }
return $distribution;
}

Внутренний цикл $ j for использует магию floor и modulus функционируют для создания счетной последовательности base-6 с количеством цифр, равным числу игральных костей, поэтому 3d6 будет считаться:

111,112,113,114,115,116,121,122,123,124,125,126,131,etc.

Функция берет сумму каждого, поэтому она будет выглядеть следующим образом: 3,4,5,6,7,8,4,5,6,7,8,9,5 и т. Д.Он просматривает все 6 ^ 3 возможных результатов и добавляет 1 к соответствующему интервалу в массиве $ distribution между 3 и 18. Довольно просто.Тем не менее, он работает только до 8d6, после чего я получаю тайм-ауты сервера, потому что теперь он выполняет миллиарды вычислений.

Но я не думаю, что это необходимо, потому что вероятность умирает за милым распределением по кривой.Мне интересно, есть ли способ пропустить сжатие числа и перейти прямо к самой кривой.Есть ли способ сделать это, например, с 80d6 (диапазон: 80-480)?Можно ли спроецировать распределение без выполнения 6 ^ 80 вычислений?

Я не профессиональный кодер, и вероятность для меня все еще нова, поэтому спасибо за всю помощь!

Стивен

Ответы [ 4 ]

3 голосов
/ 14 декабря 2012

В PERL:

#!
my( $DieType, $NumDice, $Loaded ) = @ARGV;

my $subname = "D" . $DieType . ( ( $Loaded eq "Loaded" ) ? "Loaded" : "Normal" );
my $Prob = \&$subname;

my $width = 12;
my $precision = $width - 2;

printf "%5s  %-${width}s \n", "Pip:", "Frequency:"; 
for ( my $j = $NumDice; $j <= $DieType * $NumDice ; $j++ ) {
  printf "%5d  %${width}.${precision}f \n", $j, Frequency( $DieType, $NumDice, $j );
}

sub D6Normal {
  my $retval = 1/6;
}

sub D6Loaded {
  my $retval = 1/6;

  CASE: for ($_[0]) {
    /1/    && do { $retval -= 0.02/6;   last CASE; }; 
    /2..5/ && do { $retval += 0.0025/6; last CASE; }; 
    /6/    && do { $retval += 0.01/6;   last CASE; }; 
  }
  return $retval;
}

sub D8Normal {
  my $retval = 1/8;
}

sub D10Normal {
  my $retval = 1/10;
}

sub D10Loaded {
  my $retval = 1/10;

  CASE: for ($_[0]) {
    /1..8/ && do { last CASE; }; 
    /9/    && do { $retval -= 0.01/10;  last CASE; }; 
    /10/   && do { $retval += 0.01/10;  last CASE; }; 
  }
  return $retval;
}

sub D12Normal {
  my $retval = 1/12;
}

sub D20Normal {
  my $retval = 1/20;
}

sub D32Normal {
  my $retval = 1/32;
}

sub D100Normal {
  my $retval = 1/100;
}

sub Frequency {
  my( $DieType, $NumberofDice, $PipCount ) = @_;

  if ( ( $PipCount > ($DieType * $NumberofDice) ) || ( $PipCount < $NumberofDice ) ) { 
    return 0; 
  }

  if ( ! exists $Freq{$NumberofDice}{$PipCount} ) {
    if ( $NumberofDice > 1 ) {
      for ( my $i = max( 1, $PipCount - $DieType ); $i <= min( $DieType * ($NumberofDice - 1), $PipCount - 1 ); $i++ ) {
        $Freq{$NumberofDice}{$PipCount} += &$Prob( $PipCount - $i ) * Frequency( $DieType, $NumberofDice - 1, $i );
      }
    } else {
      $Freq{$NumberofDice}{$PipCount} = &$Prob( $PipCount );
    }
  }
  return $Freq{$NumberofDice}{$PipCount}; 
}

sub max {
  my $max = shift(@_);
  foreach my $arg (@_) {
    $max = $arg if $max < $arg;
  }
  return $max;
}

sub min {
  my $min = shift(@_);
  foreach my $arg (@_) {
    $min = $arg if $min > $arg;
  }
  return $min;
}
1 голос
/ 21 апреля 2010

Хорошо, давайте начнем с броска только одного кубика.Мы знаем, что среднее значение составляет 3,5.Мы также можем вычислить дисперсию,

sum(p(x) * (x - M)^2), где М - среднее значение, х - результат игры в кости, а р - вероятность этого результата игры в кости.

Используя эту формулу,дисперсия одного броска костей составляет 35/12 = 1/6 * ((- 2,5) ^ 2 + (-1,5) ^ 2 + (-0,5) ^ 2 + 0,5 ^ 2 + 1,5 ^ 2 + 2,5 ^ 2)

Это также факт, что для нескольких независимых выборок из одного и того же распределения их дисперсия добавляет.Итак, если вы бросите N кубиков, вы должны получить новый дистрибутив со средним значением 3,5 * N и дисперсией 35 * N / 12.

Итак, если вы сгенерируете нормальное распределение со средним 3,5 * N и дисперсией 35 *N / 12, это будет очень хорошая посадка, при условии, что вы бросаете приличное количество кубиков.

1 голос
/ 21 апреля 2010
0 голосов
/ 21 декабря 2012

Мне интересно, есть ли способ пропустить сжатие числа и перейти прямо к самой кривой. Есть ли способ сделать это, например, с 80d6 (диапазон: 80-480)? Можно ли спроецировать распределение без выполнения 6 ^ 80 вычислений?

Да. Функция вероятности суммы независимых переменных - это свертка функции вероятности каждой переменной.

Свертка в этом случае - просто специальное суммирование. (В более общем смысле свертка является интегралом.) Пусть p и q две дискретные функции вероятности. Свертка условно обозначена звездочкой.

(p * q)[i] = sum_{j=1}^(n_p) p[j] q[i - j + 1]

где i варьируется от 1 до (n_p + n_q - 1), где n_p - количество элементов p, а n_q - количество элементов q. Если (i - j + 1) меньше 1 или больше n_q, то пусть q [i - j + 1] равно нулю (поэтому эти члены просто исчезают из суммы).

В данном случае p = q = [1/6, 1/6, 1/6, 1/6, 1/6, 1/6], n_p = n_q = 6. Распределение сумма 3 бросков равна (p * p * p). Распределение суммы 80 бросков равно (p * p * p * ... (еще 76 p) ... * p).

Я не знаю PHP, поэтому я написал небольшую программу на Maxima .

discrete_conv (p, q) := makelist (discrete_conv1 (p, q, i), i, 1, length (p) + length (q) - 1);
discrete_conv1 (p, q, i) := sum (p [j] * foo (q, i - j + 1), j, 1, length (p));
foo (a, i) := if 1 <= i and i <= length (a) then a [i] else 0;
r : [1/6, 1/6, 1/6, 1/6, 1/6, 1/6];
discrete_conv (r, discrete_conv (r, r));
 => [1/216,1/72,1/36,5/108,5/72,7/72,25/216,1/8,1/8,25/216,7/72,
     5/72,5/108,1/36,1/72,1/216]

Если вы продолжите повторять discrete_conv, вы увидите, что числа становятся все более похожими на нормальное распределение. Это иллюстрация центральной предельной теоремы.

Вполне возможно, что я допустил какую-то ошибку при индексировании, так что вы захотите проверить это. Надеюсь, что это проливает свет на проблему.

...