循环随机排序数组时的概率算法

时间:2013-01-17 22:18:10

标签: php math random probability

我认为,通过查看代码,问题非常简单。我有一个随机数组(数组必须随机化,一些代码已被排除,因为它与实际问题无关,但需要随机化)。对于数组中的每个元素,都有一个“概率”索引(此处描述为值本身,在$rules中),如果满足其他条件(这里为了非相关性),数组元素被“触发”的概率(在这种情况下,数组元素的分数将增加1)

考虑代码:

<?php
  // Taken from php.net/shuffle user notes
  // Shuffles an array order for the sake of foreach while maintaining
  // key => value associations
  function shuffle_assoc(&$array) {
    $keys = array_keys($array);
    shuffle($keys);
    foreach($keys as $key) {
      $new[$key] = $array[$key];
    }
    return $new;
  }

  $i = 1000000; // How many tests to perform

  // This is my rule list.  Each key is a simple color
  // and each value is a probability represented as a percent
  $rules = array(
    'black' => 20,
    'white' => 10,
    'red' => 40,
    'green' => 5,
    'blue' => 25,
  );

  // Initialize the scores array with all 0's
  // The "outs" will be used when the probability does not
  // occur in any of the rules
  $scores = array('outs' => 0);
  foreach($rules as $k => $v) { 
    $scores[$k] = 0;
  }

  $count = count($rules);

  for($x = 0; $x < $i; $x++) { 
    $rules = shuffle_assoc($rules);

    foreach($rules as $k => $probability) {
      $rand = mt_rand(1,100);
      //$probability = ??; I've tried applying many different operations here to "correct" the probability

      if($rand > $probability) { 
        continue; 
      } else {
        $scores[$k]++;
        continue 2;
      }
    }
    $scores['outs']++;
  }


  foreach($scores as $k => $v) { 
    echo "$k: " . (($v/$i)*100) . "% ($v/$i)\n";
  }

?>

预期输出(伪)。请注意,百分比与$rules

的值相对应
outs: less than 1% (.../1000000)
black: 20% (.../1000000)
white: 10% (.../1000000)
red: 40% (.../1000000)
green: 5% (.../1000000)
blue: 25% (.../1000000)

示例输出:

outs: 30.7128% (307128/1000000)
black: 13.2114% (132114/1000000)
white: 6.3381% (63381/1000000)
red: 29.5247% (295247/1000000)
green: 3.1585% (31585/1000000)
blue: 17.0545% (170545/1000000)

我尝试过的事情&amp;考虑:

  • 正如你所看到的,在循环中我有$probability = ??的注释部分,我尝试了各种明显的方法来计算每个元素中使用的实际概率,包括玩$count(规则数),这就是为什么该变量存在且未被使用的原因。

  • 显然不一定非常准确,但最好在较小的数字集合(e.x. 1,000次迭代)上获得稳定的结果。

  • 它可能非常模糊。方差+/- 5%不会伤害我的感受,特别是在较少的迭代次数中,我理解大数理论在这里发挥作用。

  • 只要不超过1%-2%,出局次数就不多了。我也尝试使用各种方法来消除掉外观,看看是否只有出现偏差,有趣的是,当我有一次这样做时,我得到了20%的分割(即使是偶数)。

  • 此外,在“出局”上,我能够非常接近正确的分割,基本上是粗暴的 - 强迫概率“数字”(即$rules的值)从100倒退,但我从来没有找到一个精确,最佳的方法。每一次,我都会接近一种颜色的结果,这会使其他颜色在一个小而明显的尺度上倾斜。在这些数字中没有易于掌握的相关性,并且看起来是随机的,尽管很明显结果与概率和大数字一致。

告诉我有一个精确的计算方法。这让我疯了。

  

编辑:我的代码的最终版本,在下面两个答案的帮助下,这样做无需在循环开始之前知道概率百分比,也没有额外的或嵌套的循环(这是我特别需要的,我想我应该在那个部分更直接)..在每个迭代的意义上,你可以根据特定迭代的属性动态地提取概率。这里的所有答案都是非常宝贵,这是我最终代码的版本:http://pastebin.com/eB3TVP1E

2 个答案:

答案 0 :(得分:4)

只需将结果标准化,累积它们然后就完成了。

我的意思是:

  • 总结为数组的每个项目提供的所有概率以获得总数(在您的情况下为100,但它很容易推广)
  • 除以总计
  • 的每个概率

例如:

$rules = array(
    'black' => 20,
    'white' => 10,
    'red' => 40,
    'green' => 5,
    'blue' => 25,
  );

将标准化为:

$rules_norm = array(
    'black' => 0.2,
    'white' => 0.1,
    'red' => 0.4,
    'green' => 0.05,
    'blue' => 0.25,
  );
  • 现在累积结果,以便为$rules_norm中的每个元素计算所有先前元素加上当前元素的总和。

所以:

$rules_norm = array(
    'black' => 0.2,
    'white' => 0.3,
    'red' => 0.7,
    'green' => 0.75,
    'blue' => 1.0,
  );

现在,您可以在[0,1)范围内提取随机浮点数,并根据结果选择增加哪些元素:增加一个元素的得分,从数组中的第一个开始增加那样$rand > $rules_norm[k]

答案 1 :(得分:2)

杰克的想法在您的代码中实现(如果概率总和> 100则不起作用):

php fiddle

<?php
  // Taken from php.net/shuffle user notes
  // Shuffles an array order for the sake of foreach while maintaining
  // key => value associations
  function shuffle_assoc(&$array) {
    $keys = array_keys($array);
    shuffle($keys);
    foreach($keys as $key) {
      $new[$key] = $array[$key];
    }
    return $new;
  }

  $i = 1000000; // How many tests to perform

  // This is my rule list.  Each key is a simple color
  // and each value is a probability represented as a percent
  $rules = array(
    'black' => 20,
    'white' => 10,
    'red' => 40,
    'green' => 5,
    'blue' => 25,
  );

  // Initialize the scores array with all 0's
  // The "outs" will be used when the probability does not
  // occur in any of the rules
  $scores = array('outs' => 0);
  foreach($rules as $k => $v) { 
    $scores[$k] = 0;
  }

  $count = count($rules);
//$limits is what Jack called $rules_norm
$limits=array();
$limit=0;
foreach($rules as $k=>$v)
{
    $limit+=$v;
    $limits[$k]=$limit;
}
  for($x = 0; $x < $i; $x++) { 
      $rand = mt_rand(1,100);
foreach($limits as $k=>$v)
{
    if($v>=$rand)
    {
        $scores[$k]++;
        continue(2);
    }

}
    $scores['outs']++;
  }


  foreach($scores as $k => $v) { 
    echo "$k: " . (($v/$i)*100) . "% ($v/$i)\n";
  }

?>