使用 6 面骰子计算赔率分布

发布于 2024-08-30 05:50:18 字数 1015 浏览 3 评论 0原文

我正在尝试计算 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 循环使用了 floormodulus 的魔力函数创建一个以 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 次计算就可以预测分布吗?

我不是专业编码员,概率对我来说仍然是新事物,所以感谢您的所有帮助!

斯蒂芬

I'm trying to calculate the odds distribution of a changing number of 6-sided die rolls. For example, 3d6 ranges from 3 to 18 as follows:

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

I wrote this php program to calculate it:

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;
}

The inner $j for-loop uses the magic of the floor and modulus functions to create a base-6 counting sequence with the number of digits being the number of dice, so 3d6 would count as:

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

The function takes the sum of each, so it would read as: 3,4,5,6,7,8,4,5,6,7,8,9,5,etc. It plows through all 6^3 possible results and adds 1 to the corresponding slot in the $distribution array between 3 and 18. Pretty straightforward. However, it only works until about 8d6, afterward i get server time-outs because it's now doing billions of calculations.

But I don't think it's necessary because die probability follows a sweet bell-curve distribution. I'm wondering if there's a way to skip the number crunching and go straight to the curve itself. Is there a way to do this with, for example, 80d6 (range: 80-480)? Can the distribution be projected without doing 6^80 calculations?

I'm not a professional coder and probability is still new to me, so thanks for all the help!

Stephen

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

稀香 2024-09-06 05:50:18

在 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;
}

In 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;
}
梦里人 2024-09-06 05:50:18

您正在寻找二项分布

You are looking for a Binomial Distribution

江南月 2024-09-06 05:50:18

好的,让我们从滚动一个骰子开始吧。我们知道平均值是 3.5。我们还可以计算方差,

sum(p(x) * (x - M)^2),其中 M 是平均值,x 是骰子结果,p 是该骰子的概率结果。

使用此公式,单次掷骰子的方差为 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 的正态分布,那么假设您掷了相当多的骰子,那么这将是一个非常合适的分布。

Ok, so let's start with rolling just one die. We know that the mean is 3.5. We can also calculate the variance,

sum(p(x) * (x - M)^2), where M is the mean, x is a dice result, and p is the probability of that dice result.

Using this formula, the variance of a single dice roll is 35/12 = 1/6*((-2.5)^2 + (-1.5)^2 + (-0.5)^2 + 0.5^2 + 1.5^2 + 2.5^2)

It's also a fact that for multiple independent samples from the same distribution, their variances add. So, if you roll N dice, you should get a new distribution with mean 3.5*N and variance 35*N/12.

So, if you generate a normal distribution with mean 3.5*N and variance 35*N/12, it will be a pretty good fit, assuming you're rolling a decent number of dice.

单身情人 2024-09-06 05:50:18

我想知道是否有办法跳过数字运算并直接进入曲线本身。有没有办法用 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]

如果继续重复离散_卷积,您应该会发现数字变得越来越像正态分布。这是中心极限定理的一个例证。

我完全有可能在索引方面犯了一些错误,所以你需要检查一下。希望这能对这个问题有所帮助。

I'm wondering if there's a way to skip the number crunching and go straight to the curve itself. Is there a way to do this with, for example, 80d6 (range: 80-480)? Can the distribution be projected without doing 6^80 calculations?

Yes. The probability function of the sum of independent variables is the convolution of the probability function of each variable.

The convolution in this case is just a special summation. (More generally, the convolution is an integral.) Let p and q be two discrete probability functions. Convolution is conventionally indicated by an asterisk.

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

where i ranges from 1 to (n_p + n_q - 1) with n_p is the number of elements of p and n_q the number of elements of q. If (i - j + 1) is less than 1 or greater than n_q, then let q[i - j + 1] be zero (so those terms just disappear from the summation).

In the case at hand, you have p = q = [1/6, 1/6, 1/6, 1/6, 1/6, 1/6], n_p = n_q = 6. The distribution of the sum of 3 rolls is (p * p * p). The distribution of the sum of 80 rolls is (p * p * p * ... (76 more p's) ... * p).

I don't know PHP so I wrote a little program in 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]

If you continue repeating discrete_conv, you should find the numbers become more and more like a normal distribution. This is an illustration of the central limit theorem.

It's entirely possible that I've made some mistake with indexing so you'll want to check that. Hope this sheds some light on the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文