使用php计算事件发生一次或多次不同赔率的赔率

时间:2016-08-01 11:45:32

标签: php statistics

我有一系列不同的事件,都有自己的可能性发生。有没有办法计算它们合并的几率,这样我得到的赔率为0,1 2等等。

数学很容易,但计算的数量增长很快,所以我希望有一个功能可以帮助我。

有三个因素的例子:

Event | Yes | No
A     | 3%  | 97%
B     | 4%  | 96%
C     | 5%  | 95%

0发生:$ A [no] * $ B [no] * $ c [no]

1发生:$ A [是] * $ B [否] * $ c [否] + $ A [否] * $ B [是] * $ c [否] + $ A [否] * $ B [no] * $ c [是]

2发生= $ A [是] * $ B [是] * $ c [否] + $ A [是] * $ B [否] * $ c [是] + $ A [否] * $ B [是] * $ c [是]

发生了3次:$ A [是] * $ B [是] * $ c [是]

这很容易在php中编写,我的问题是如果我添加更多事件,这是如何扩大规模。只需再添加一两个caclulations的数量,很快代码就会很长。

那么有更简单的方法吗?我会对任何提示或想法表示感谢。

2 个答案:

答案 0 :(得分:1)

执行速度很慢。

让我们考虑一个包含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

如果我们从02**$eventCount-1为每个案例分配一个号码,然后使用此号码的位来选择是否&#39,则可以轻松完成所有这些情况;或者没有'每个事件的选择都包含在乘法中,最后将每个乘法结果添加到$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";
}

答案 1 :(得分:0)

让我们看看我们在这里真正需要什么。让我们假设,我们有一系列的机会:

$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) < / p>

为实现这一点,我使用了this answer并稍微修改了代码。从上面提到的答案中,我们得到了非重复的机会组合(意思是,如果我们获得2次成功机会,0.80.150.150.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