2009-01-15 67 views
47

我知道如何在PHP中生成一个随机数,但可以说我想要一个1-10之间的随机数,但我想要更多的3,4,5然后是8,9,10。这怎么可能?我会张贴我尝试过的,但老实说,我甚至不知道从哪里开始。在PHP中通过权重生成随机结果?

回答

11

对此的幼稚黑客将建立一个列表或阵列状

1,2,3,3,3,3,3,4,4,4,4,4,4,5, 5,5,5,6,6,7,7,7,8,8,9,9,10,10

然后从中随机选择。

+1

完全正确。创建一个列表,其中加权的数字以您想要的比例加权(如果可能,可以使用其他函数创建列表),然后从列表中随机选择一个项目。 – Ross 2009-01-15 00:51:59

+9

唯一的缺点是,如果你希望数字1的可能性比其他数字高1万亿倍,那么你需要一个1万亿以上的元素。 – 2009-01-15 00:53:17

+1

@Allain Lalonde哦相当。它不会扩展,但它非常简单,可能足以满足OP的需求。 – 2009-01-15 00:56:34

21

有一个pretty good tutorial for you

基本上是:所有的数字

  1. 总和的权重。
  2. 挑选一个小于此数的随机数
  3. 减去权重直到结果为负,如果是,则返回该数。
+0

此外,这没有先前的答案的内存开销(建立另一个阵列与所需的分布,并从中随机选择) – 2009-01-15 00:55:36

+0

这是非天真的黑客攻击。 :-) – 2009-01-15 01:06:56

27

为了有效的随机数一致偏向规模的一端:

  • 选择0..1
  • 抬起之间的功率γ的连续随机数,以将其偏置。图1是未加权的,低级提供了更多的数字较高,反之亦然
  • 缩放到期望的范围内和圆整数

例如。在PHP(未经测试):

function weightedrand($min, $max, $gamma) { 
    $offset= $max-$min+1; 
    return floor($min+pow(lcg_value(), $gamma)*$offset); 
} 
echo(weightedrand(1, 10, 1.5)); 
+0

我爱你的答案。请看看我的问题(下面的链接)。我很乐意听取您的意见,并将其扩展。 http://stackoverflow.com/questions/4030427/generate-random-weighted-value – 2010-10-27 06:25:52

4

This tutorial指导您完成它,在PHP中,有多个剪切和粘贴的解决方案。请注意,由于下面的注释,此例程与您在该页面上找到的内容略有不同。

从后拍摄的功能:

/** 
* weighted_random_simple() 
* Pick a random item based on weights. 
* 
* @param array $values Array of elements to choose from 
* @param array $weights An array of weights. Weight must be a positive number. 
* @return mixed Selected element. 
*/ 

function weighted_random_simple($values, $weights){ 
    $count = count($values); 
    $i = 0; 
    $n = 0; 
    $num = mt_rand(1, array_sum($weights)); 
    while($i < $count){ 
     $n += $weights[$i]; 
     if($n >= $num){ 
      break; 
     } 
     $i++; 
    } 
    return $values[$i]; 
} 
1

,因为我用IainMH的解决方案,我也不妨分享我的PHP代码:

<pre><?php 

// Set total number of iterations 
$total = 1716; 

// Set array of random number 
$arr = array(1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5); 
$arr2 = array(0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 5); 

// Print out random numbers 
for ($i=0; $i<$total; $i++){ 

    // Pick random array index 
    $rand = array_rand($arr); 
    $rand2 = array_rand($arr2); 

    // Print array values 
    print $arr[$rand] . "\t" . $arr2[$rand2] . "\r\n"; 

} 

?></pre> 
2

平原和公平。 只需复制/粘贴并测试它。

/** 
* Return weighted probability 
* @param (array) prob=>item 
* @return key 
*/ 
function weightedRand($stream) { 
    $pos = mt_rand(1,array_sum(array_keys($stream)));   
    $em = 0; 
    foreach ($stream as $k => $v) { 
     $em += $k; 
     if ($em >= $pos) 
      return $v; 
    } 

} 

$item['30'] = 'I have more chances than everybody :]'; 
$item['10'] = 'I have good chances'; 
$item['1'] = 'I\'m difficult to appear...'; 

for ($i = 1; $i <= 10; $i++) { 
    echo weightedRand($item).'<br />'; 
} 

编辑:在结尾处添加缺少的括号。

81

基于@ Allain的answer/link,我使用PHP编写了这个快速函数。如果你想使用非整数加权,你将不得不修改它。

/** 
    * getRandomWeightedElement() 
    * Utility function for getting random values with weighting. 
    * Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50) 
    * An array like this means that "A" has a 5% chance of being selected, "B" 45%, and "C" 50%. 
    * The return value is the array key, A, B, or C in this case. Note that the values assigned 
    * do not have to be percentages. The values are simply relative to each other. If one value 
    * weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66% 
    * chance of being selected. Also note that weights should be integers. 
    * 
    * @param array $weightedValues 
    */ 
    function getRandomWeightedElement(array $weightedValues) { 
    $rand = mt_rand(1, (int) array_sum($weightedValues)); 

    foreach ($weightedValues as $key => $value) { 
     $rand -= $value; 
     if ($rand <= 0) { 
     return $key; 
     } 
    } 
    } 
2

您可以使用weightedChoiceNon-standard PHP library。它接受配对列表(物品,重量),以便可以使用不能成为数组键的物品。您可以使用pairs函数将array(item => weight)转换为所需的格式。

use function \nspl\a\pairs; 
use function \nspl\rnd\weightedChoice; 

$weights = pairs(array(
    1 => 10, 
    2 => 15, 
    3 => 15, 
    4 => 15, 
    5 => 15, 
    6 => 10, 
    7 => 5, 
    8 => 5, 
    9 => 5, 
    10 => 5 
)); 

$number = weightedChoice($weights); 

在这个例子中,2-5会比7-10多3倍。

0

我刚刚发布class to perform weighted sorting很容易。

它基于与Brad'sAllain's中提到的算法相同的算法,并针对速度进行了优化,单元测试均匀分布,并支持任何PHP类型的元素。

使用它很简单。实例吧:

$picker = new Brick\Random\RandomPicker(); 

然后添加元素的加权值的数组(仅当您的元素是字符串或整数):

$picker->addElements([ 
    'foo' => 25, 
    'bar' => 50, 
    'baz' => 100 
]); 

或者使用个人来电addElement()。此方法支持任何种类的PHP值作为元素(字符串,数字,对象,...),而不是在阵列的方法:

$picker->addElement($object1, $weight1); 
$picker->addElement($object2, $weight2); 

然后得到一个随机元素:

$element = $picker->getRandomElement(); 

的获取其中一个元素的可能性取决于其相关的权重。唯一的限制是权重必须是整数。

1
/** 
* @param array $weightedValues 
* @return string 
*/ 
function getRandomWeightedElement(array $weightedValues) 
{ 
    $array = array(); 

    foreach ($weightedValues as $key => $weight) { 
     $array = array_merge(array_fill(0, $weight, $key), $array); 
    } 

    return $array[array_rand($array)]; 
} 

getRandomWeightedElement(array('A'=>10, 'B'=>90));

这是非常简单的方法。如何获得随机加权元素。我填充数组变量$ key。我得到$ key给数组$ weight x。之后,使用array_rand到数组。我有随机值;)。

0

function getBucketFromWeights($ values){ $ total = $ currentTotal = $ bucket = 0;

foreach ($values as $amount) { 
    $total += $amount; 
} 

$rand = mt_rand(0, $total-1); 

foreach ($values as $amount) { 
    $currentTotal += $amount; 

    if ($rand => $currentTotal) { 
     $bucket++; 
    } 
    else { 
     break; 
    } 
} 

return $bucket; 

}

我哎从这里Picking random element by user defined weights

答案我写了这个之后,我看到别人有一个更优雅的回答修改此。他他是他。

0

本页面上的许多答案似乎都使​​用数组膨胀,过度迭代,库或难以阅读的过程。当然,每个人都认为自己的宝宝是最可爱的,但我真的认为我的方法是精简,简单易读/修改...

根据OP,我将创建一个值数组(声明为键)从1到10,其中3,4和5的权重是其他值的两倍(声明为值)。

$values_and_weights=array(
    1=>1, 
    2=>1, 
    3=>2, 
    4=>2, 
    5=>2, 
    6=>1, 
    7=>1, 
    8=>1, 
    9=>1, 
    10=>1 
); 

如果你只打算做一个随机选择和/或您的阵列相对较小*(做你自己的基准测试,以确保),这可能是你最好的选择:

$pick=mt_rand(1,array_sum($values_and_weights)); 
$x=0; 
foreach($values_and_weights as $val=>$wgt){ 
    if(($x+=$wgt)>=$pick){ 
     echo "$val"; 
     break; 
    } 
} 

这种方法不涉及数组修改,可能不需要迭代整个数组(但可能)。


在另一方面,如果你要使阵列上的多个随机选择和/或您的阵列足够大*(做你自己的基准测试,以确保),重组的阵列可能更好。

用于生成新的阵列将越来越对齐在存储器中的成本:

  1. 阵列尺寸的增加和
  2. 数随机选择的增加。

新阵列要求用每个值的“限制”替换“重量”,方法是将前一个元素的权重添加到当前元素的权重。

然后翻转数组,使极限是数组键,值是数组值。 逻辑是:所选值将具有> = $ pick的最低限制。

// Declare new array using array_walk one-liner: 
array_walk($values_and_weights,function($v,$k)use(&$limits_and_values,&$x){$limits_and_values[$x+=$v]=$k;}); 

//Alternative declaration method - 4-liner, foreach() loop: 
/*$x=0; 
foreach($values_and_weights as $val=>$wgt){ 
    $limits_and_values[$x+=$wgt]=$val; 
}*/ 
var_export($limits_and_values); 

创建此数组:

array (
    1 => 1, 
    2 => 2, 
    4 => 3, 
    6 => 4, 
    8 => 5, 
    9 => 6, 
    10 => 7, 
    11 => 8, 
    12 => 9, 
    13 => 10, 
) 

我们生成随机$pick和选择值:

// $x (from walk/loop) is the same as writing: end($limits_and_values); $x=key($limits_and_values); 
$pick=mt_rand(1,$x); // pull random integer between 1 and highest limit/key 
while(!isset($limits_and_values[$pick])){++$pick;} // smallest possible loop to find key 
echo $limits_and_values[$pick]; // this is your random (weighted) value 

这种做法是辉煌的,因为isset()非常快,最大数量isset() while循环中的调用只能与阵列中的最大权重(不要与极限混淆)一样多。对于这种情况,最大迭代次数= 2!

这种方法从来不需要遍历整个数组