我有一组不同的事件,它们都有自己的几率。有没有办法计算它们组合的赔率,以便我得到 0、1 2 等发生的赔率。
数学很简单,但计算的数量增长很快,所以我希望有一个函数可以帮助我。
具有三个因素的示例:
Event | Yes | No
A | 3% | 97%
B | 4% | 96%
C | 5% | 95%
0 发生 : $A[否] * $ B[否] * $c[否]
1 发生 : $A[是] * $ B[否] * $c[否] + $A[否] * $ B[是] * $c[否] + $A[否] *$ B[否] * $c[是]
2 发生 = $A[是] * $ B[是] * $c[否] + $A[是] * $ B[否] * $c[是] + $A[否] * $ B[是]* $c[是]
3 发生 : $A[是] * $ B[是] * $c[是]
这很容易用 php 编写,我的问题是如果我添加更多事件,这将如何扩展。只要再添加一个,计算的数量就会翻倍,很快代码就会变得非常长。
那么有没有更简单的方法呢?我将不胜感激任何提示或想法。
这是相当缓慢的实现。
让我们考虑一个包含 5 个事件的案例。
0 事件发生的几率是:
$no[0] * $no[1] * $no[2] * $no[3] * $no[4]
1 个事件发生的几率是:
$no[0] * $no[1] * $no[2] * $no[3] * $yes[4] +
$no[0] * $no[1] * $no[2] * $yes[3] * $no[4] +
$no[0] * $no[1] * $yes[2] * $no[3] * $no[4] +
...
您进行所有乘法的地方,其中正好有 1 个"是"选择。
发生 2 个事件的几率是:
$no[0] * $no[1] * $no[2] * $yes[3] * $yes[4] +
$no[0] * $no[1] * $yes[2] * $no[3] * $yes[4] +
$no[0] * $no[1] * $yes[2] * $yes[3] * $no[4] +
...
您进行所有乘法的地方,其中正好有 2 个"是"选择。
这可以概括:为了计算N个事件发生的几率,您需要经历所有乘法,其中正好有N个"是"选项。
现在,当您需要计算从 0 到 5 个事件发生的所有赔率时,您需要检查是/否选项的所有可能组合,并将每个乘法添加到 $odds[$yesCount]
中。
$no[0] * $no[1] * $no[2] * $no[3] * $no[4] ; added to $odds[0]
$no[0] * $no[1] * $no[2] * $no[3] * $yes[4] ; added to $odds[1]
$no[0] * $no[1] * $no[2] * $yes[3] * $no[4] ; added to $odds[1]
$no[0] * $no[1] * $no[2] * $yes[3] * $yes[4] ; added to $odds[2]
$no[0] * $no[1] * $yes[2] * $no[3] * $no[4] ; added to $odds[1]
...
$yes[0] * $yes[1] * $yes[2] * $yes[3] * $yes[4] ; added to $odds[5]
这里总共有2**5 = 32
种不同的乘法,或者通常2**$eventCount
。
如果我们为每个情况分配一个从 0
到 2**$eventCount-1
的数字,然后使用该数字的位来选择乘法中是否包含每个事件的"是"或"否"选择,最后将每个乘法结果添加到$odds[$yesCount]
,则很容易遍历所有这些情况:
// number of events
$eventCount = 5;
// odds of each event happening
$yes = [ 0.10, 0.50, 0.32, 0.66, 0.99 ];
// odds of each event not happening
$no = [];
for ($eventNumber = 0; $eventNumber < $eventCount; $eventNumber++) {
$no[$eventNumber] = 1 - $yes[$eventNumber];
}
// initialize combined $odds to zero
$odds = [];
for ($n = 0; $n <= $eventCount; $n++) {
$odds[$n] = 0;
}
// calculate combined odds
for ($case = 0; $case < 2 ** $eventCount; $case++) {
$multiplication = 1;
$yesCount = 0;
for ($eventNumber = 0; $eventNumber < $eventCount; $eventNumber++) {
if ($case & (1 << $eventNumber)) {
$yesCount++;
$multiplication *= $yes[$eventNumber];
} else {
$multiplication *= $no[$eventNumber];
}
}
$odds[$yesCount] += $multiplication;
}
// show combined odds
for ($n = 0; $n <= $eventCount; $n++) {
echo "Odds of " . $n . " events happening is " . $odds[$n] . "<br>'n";
}
让我们看看我们在这里实际需要什么。让我们假设,我们有一系列的机会:
$chances = [0.8, 0.3, 0.15];
我们需要的是使用以下计算获得这些结果:
// 0.119
$chance0 = (1-$array[0]) * (1-$array[1]) * (1-$array[2]);
// 0.548
$chance1 = $array[0] * (1-$array[1]) * (1-$array[2]) + (1-$array[0]) * $array[1] * (1-$array[2]) + (1-$array[0]) * (1-$array[1]) * $array[2];
// 0.297
$chance2 = $array[0] * $array[1] * (1-$array[2]) + $array[0] * (1-$array[1]) * $array[2] + (1-$array[0]) * $array[1] * $array[2];
// 0.036
$chance3 = $array[0] * $array[1] * $array[2];
我们真正需要的,是基于一个代表成功机会的整数,从数字池中获取每个非重复组合,并使其余组合"反向",即使它们(1 - chance)
为了实现这一点,我使用了这个答案并稍微修改了代码。从上述答案中,我们得到非重复的机会组合(这意味着,如果我们获得 2 次成功的机会,0.8
和0.15
与 0.15
和 0.8
相同(。接下来,我们使用array_diff
来查看剩余的内容,并假设这些是失败的机会。但由于这些机会失败,我们需要"逆转"它们,用相应的回调调用array_map
。最后一件事是计算array_product
以获得事件发生的机会。
最后,我们只是array_sum
所有的机会。
class Combinations implements Iterator
{
protected $c = null;
protected $s = null;
protected $n = 0;
protected $k = 0;
protected $pos = 0;
function __construct($s, $k) {
if(is_array($s)) {
$this->s = array_values($s);
$this->n = count($this->s);
} else {
$this->s = (string) $s;
$this->n = strlen($this->s);
}
$this->k = $k;
$this->rewind();
}
function key() {
return $this->pos;
}
function current() {
$r = array();
for($i = 0; $i < $this->k; $i++)
$r[] = $this->s[$this->c[$i]];
return is_array($this->s) ? $r : implode('', $r);
}
function next() {
if($this->_next())
$this->pos++;
else
$this->pos = -1;
}
function rewind() {
$this->c = range(0, $this->k);
$this->pos = 0;
}
function valid() {
return $this->pos >= 0;
}
protected function _next() {
$i = $this->k - 1;
while ($i >= 0 && $this->c[$i] == $this->n - $this->k + $i)
$i--;
if($i < 0)
return false;
$this->c[$i]++;
while($i++ < $this->k - 1)
$this->c[$i] = $this->c[$i - 1] + 1;
return true;
}
}
function getTotalChances($calculateFrom, $successCount)
{
$totalChance = [];
foreach(new Combinations($calculateFrom, $successCount) as $success)
{
$failure = array_map(function($element) {
return (1-$element);
}, array_diff($calculateFrom, $success));
$chance = array_product(array_merge($success, $failure));
$totalChance[] = $chance;
}
return(array_sum($totalChance));
}
$calculateFrom = [0.8, 0.3, 0.15];
var_dump(getTotalChances($calculateFrom, 2));
现在,如果您运行以下命令,您将获得所需的内容:
var_dump(getTotalChances($calculateFrom, 0)); // 0.119
var_dump(getTotalChances($calculateFrom, 1)); // 0.548
var_dump(getTotalChances($calculateFrom, 2)); // 0.297
var_dump(getTotalChances($calculateFrom, 3)); // 0.036