我正在尝试计算不断变化的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-loop使用 floor 和模数函数的魔力来创建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个可能的结果,并在3和18之间的$ distribution数组中的相应插槽中加1。非常简单。然而,它只能工作到大约8d6,之后我得到服务器超时,因为它现在正在做数十亿的计算。
但我不认为这是必要的,因为死亡概率遵循甜蜜的钟形曲线分布。我想知道是否有办法跳过数字运算并直接进入曲线本身。有没有办法做到这一点,例如,80d6(范围:80-480)?可以在不进行6 ^ 80计算的情况下预测分布吗?
我不是一个专业的程序员,对我来说概率仍然是新的,所以感谢所有的帮助!
斯蒂芬
答案 0 :(得分:3)
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 :(得分:1)
答案 2 :(得分:1)
好的,让我们开始只滚动一个骰子。我们知道平均值是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的正态分布,假设你滚动了相当数量的骰子,这将是一个非常合适的选择。
答案 3 :(得分:0)
我想知道是否有办法跳过数字运算并直接进入曲线本身。有没有办法做到这一点,例如,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,您会发现数字越来越像正态分布。这是中心极限定理的例证。
完全有可能我在索引方面犯了一些错误,所以你要检查一下。希望这能够解决这个问题。