1. Repeted值
让我们假设我们具有其中每一个值对应于它的索引的相对概率的阵列。例如,给一枚硬币,折腾的可能结果是50%的尾巴和50%的头部。我们可以代表那些概率的数组,就像(我将使用PHP,因为这似乎是由OP使用的语言):
$coin = array(
'head' => 1,
'tails' => 1
);
虽然滚动两个骰子的结果可以表示为:
$dice = array('2' => 1, '3' => 2, '4' => 3, '5' => 4, '6' => 5, '7' => 6,
'8' => 5, '9' => 4, '10' => 3, '11' => 2, '12' => 1
);
一个简单的方法来选择一个概率与这些数组的值成正比(因此与基础模型一致)的随机密钥(索引)是创建另一个数组,其元素是原始数据的密钥重复多次如值所示,然后返回一个随机值。例如,对于dice
阵列:
$arr = array(2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, ...
这样做,我们有信心,每个按键都会有正确的相对概率有所回升。我们可以封装所有逻辑与它建立辅助阵列的返回使用mt_rand()随机索引的功能的建筑工类:
class RandomKeyMultiple {
private $pool = array();
private $max_range;
function __construct($source) {
// build the look-up array
foreach ($source as $key => $value) {
for ($i = 0; $i < $value; $i++) {
$this->pool[] = $key;
}
}
$this->max_range = count($this->pool) - 1;
}
function get_random_key() {
$x = mt_rand(0, $this->max_range);
return $this->pool[$x];
}
}
的使用简单,只是创建该类的一个对象使所述源数组,然后该函数的每个调用将返回一个随机密钥:
$test = new RandomKeyMultiple($dice);
echo $test->get_random_key();
的问题是,OP的数组包含大值,这导致了非常大的(但仍在可控范围内,即使没有除以100的所有值)数组。
2.步骤
通常,离散概率分布可以是更复杂的,条件是不能在重复数很容易地转换的浮点值。
另一种方式来解决这个问题是要考虑数组作为间隔的是把所有可能的值的全球范围内的misures中的值:
+---------------------------+-----------------+-------+----+
| | | | |
|<--- 265000 --->|<-- 190000 -->|<30000>|1300|
|<------- 455000 ------>| |
|<---------- 485000 --------->| |
|<---------------- 486300 -------------->|
然后,我们可以选择0到1之间的随机数486300(全球范围)并查找正确的指数(其可能性与其段的长度成正比,给出正确的概率分布)。类似:
$x = mt_rand(0, 486300);
if ($x < 265000)
return 0;
elseif ($x < 455000)
return 1;
elseif ($x < 485000)
return 2;
else
return 3;
我们可以概括该算法并封装在一个类中的所有逻辑(使用辅助数组来存储中的部分和):
class RandomKey {
private $steps = array();
private $last_key;
private $max_range;
function __construct($source) {
// sort in ascending order to partially avoid numerical issues
asort($source);
// calculate the partial sums. Considering OP's array:
//
// 1300 ----> 0
// 30000 ----> 1300
// 190000 ----> 31300
// 265000 ----> 221300 endind with $partial = 486300
//
$partial = 0;
$temp = 0;
foreach ($source as $k => &$v) {
$temp = $v;
$v = $partial;
$partial += $temp;
}
// scale the steps to cover the entire mt_rand() range
$factor = mt_getrandmax()/$partial;
foreach ($source as $k => &$v) {
$v *= $factor;
}
// Having the most probably outcomes first, minimizes the look-up of
// the correct index
$this->steps = array_reverse($source);
// remove last element (don't needed during checks) but save the key
end($this->steps);
$this->last_key = key($this->steps);
array_pop($this->steps);
}
function get_random_key() {
$x = mt_rand();
foreach ($this->steps as $key => $value) {
if ($x > $value) {
return $key;
}
}
return $this->last_key;
}
}
Here或here有现场演示用一些例子和帮助函数来检查密钥的概率分布。
对于较大的阵列,也可以考虑查找索引的二进制搜索。
什么是“基于区域”是什么意思?目前还不清楚你在这里做什么。 –
这些值是随机加权的吗?意思是你希望数组选择索引'0'265000次,每1300次它选择索引'3'? –
也许吧。感谢您的回应。 – user889349