PHP:如何随机选择最大概率值?

4

我有以下数组和代码

    $a = [
      149 => 55,
      130 => 10,
      131 => 5,
      132 => 5,
      133 => 10,
      134 => 10,
      135 => 5
    ];

   $rand = rand (0,(count($a)-1));

   echo array_values($a)[$rand];

这将主要返回5,10,而不是55

值的总和为100%的概率。值也可以是小数,如55.55、10.10等,但总体上将是100%。

我已经按照https://www.geeksforgeeks.org/how-to-get-random-value-out-of-an-array-in-php/的方法进行了操作,但结果并非完全符合预期。

因此,应该随机选择具有最高概率的选项。

因此,结果可能会像这样:55、55、10、10、10、55、5等。

我发现了一个有用的链接Generating random results by weight in PHP?其中概率=权重。


数组有值55、10、5,按比例1:3:3计算,因此最大结果只能为3 - Antony Jack
嗨@AntonyJack,那么假设它的值是44,10,9,21,36,对吗? - Jackson
现在它将接受任何一个值,而不需要比例。 - Antony Jack
从结果来看,它优先考虑最低值,而不是最高值。 - Jackson
@AnkitShah,你真的让悬赏过期了吗?太浪费了。另外,从你的个人资料中我看到你有Joomla的经验,但没有[joomla.se] Stack Exchange账户--欢迎加入该社区。 - mickmackusa
谢谢@mickmackusa。是的,当然会的,再次感谢。 - Jackson
5个回答

10

现在,你的数组是这样的:-

55, 10, 5, 5, 10, 10, 5

现在,你应该生成一个介于[0,100)之间的随机数,让我们称其为r

  • 如果r在[0,55)之间,则选择值55。
  • 否则,如果r在[55,55 + 10 = 65)之间,则选择值10。
  • 否则,如果r在[65,65 + 5 = 70)之间,则选择值5。
  • 否则,如果r在[70,70 + 5 = 75)之间,则选择值5。
  • 否则,如果r在[75,75 + 10 = 85)之间,则选择值10。
  • 否则,如果r在[85,85 + 10 = 95)之间,则选择值10。
  • 否则,如果r在[95,95 + 5 = 100)之间,则选择值5。

我相信你已经明白了...

因此,对于一般情况,如果你有一个名为'arr'的数组,这是伪代码: -

function SELECTPROB()
{
    $r = generateRandomNumber(0, 100);    //function to generate random number between 0 and 100, (100 exclusive)
    $sum = 0;
    foreach($arr as $i)
    {
        if($r >= $sum && $r < $sum + $i)
        {
            return $i
        }
        $sum = $sum + $i
    }
    return -1    //Should technically never reach upto this, but it can if your probability's sum is not 100
}

1
@AntonyJack 你在问题中提到了“因此,应该选择具有最高概率的主要和随机的选项。” 在这里,55被主要和随机地选择了。 - EReload
1
这个解决方案比较模糊。总体思路不错,但你只提供了这一个特定情况的伪代码。原帖中提到值可能不同,意味着数组不是静态的。如果你想改进你的答案,请考虑不同的值和数量变化的元素,并设计一个解决方案。 - El_Vanja
1
实际上@El_Vanja,我不同意,我认为这是一个完全好的答案。它解释了基本推理,并给出了伪代码“实现”,以明确实际代码应该是什么样子。这对于原始提问者来说应该已经足够创建特定情况下的解决方案了。发布可以解决“确切”提出的问题的代码示例(甚至可以复制和粘贴)将是喂食式的,并且是不必要的,或者甚至可能对答案有害,就我而言。 - David Z
@DavidZ 好的,说得对。但是你是在谈论答案的原始形式还是编辑后的形式?当我再次考虑时,确实,即使是原始形式也应该足够了。我在这方面改正了。但我仍然觉得添加的这个伪代码通用解决方案可以改善答案 - 正如你自己所说,增加了清晰度(在标记语言内)。我的评论没有明确表达,这是我的错误,但我从来没有想过回答者实际上应该编写一个解决此问题的具体代码示例。我旨在使用伪代码。 - El_Vanja
@El_Vanja 啊,我没有意识到它已经被编辑了,抱歉。我在谈论第二版。但是我也认为我所说的一些内容同样适用于原始版本,特别是原始版本是一个完全可以接受的答案。 - David Z
我喜欢代码的简洁、简单和优雅,仅使用一个循环也使其高效。 - santamanno

1
这里有一个与遗传算法中的轮盘赌选择类似的实现方式。这是EReload的答案版本,但是它被限制在总和上而不是100。
    $a = [
      149 => 55,
      130 => 10,
      131 => 5,
      132 => 5,
      133 => 10,
      134 => 10,
      135 => 5
    ];

   echo randSelect($a);

   function randSelect($a) {
        $values = array_values($a);
        $sum = array_sum($values);
        $rand = (rand(0,1000)/1000) * $sum;
        $partialSum = 0;

        for ($i=0; $i < count($values); $i++) {
            $partialSum += $values[$i];
            if($partialSum >= $rand){
                return $values[$i];
                // incase you are using something like array_count_values and are actually looking for the keys
                // return array_keys($a)[$i];
            }
        }
   }

0

据我理解,您想让较大的数字在 rand 方法中更频繁地出现,无论较小的数字出现多少次。您需要先对数组进行去重。

按权重随机是一种简单的随机方法,但您可以通过求幂而不是自身来更自由地控制权重。

$a = [
      149 => 55,
      130 => 10,
      131 => 5,
      132 => 5,
      133 => 10,
      134 => 10,
      135 => 5
    ];

$val_arr = array_unique(array_values($a));

function rand_by_sum($arr, $power=1){
        $sum = 0;
        $f_val = function($f)use($power){
                return pow($f, $power);
        };
        foreach($arr as $f){
                $sum += $f_val($f);
        }
        $rand = mt_rand(0, $sum);

        $tmp_sum = 0;
        foreach($arr as $f){
                $tmp_sum += $f_val($f);
                if($tmp_sum >= $rand) return $f;
        }
}

for($i=0; $i< 10; $i++){
        echo rand_by_sum($val_arr, $argv[1]) . " ";
}

echo "\n";

这里是使用不同 pow 函数的一些测试结果

php test.php 0.5
55 5 10 55 5 55 55 5 55 55 

php test.php 2
55 55 10 55 55 55 55 55 55 55 

php test.php 1
55 10 55 55 55 55 55 55 55 10

为了获取值,你需要将数组倒序,得到 55 => [149],然后从随机数中获取结果,在倒序的数组值中再次随机。

0

我认为您可以对数组进行洗牌并弹出一个元素,再次洗牌并弹出该元素,这将是随机的,并且那些具有更大概率的数字将首先出现。

您可以创建另一个具有100个数字的数组,表示总概率,并在其中插入与其值相等的数量的数字,最后将其洗牌以稍后随机选择索引。然后,您将获得一个包含100个数字的数组,其中最重复的数字是最有可能的。最后,您只需选择一个随机索引并创建您的数组即可。

您能告诉我是否正在寻找这样的东西或者我是否误解了问题

function getProb($array, $elements)
{
    $myNewArray = [];
    $myProbabilisticArray = $this->getProbabilisticArray($array);
    for ($i=0; $i < $elements; $i++) {
        $myNewArray[] = $myProbabilisticArray[array_rand($myProbabilisticArray)];
    }
    return $myNewArray;
}

function getProbabilisticArray($array) {
    $myNewArray = [];
    rsort($array);

    $currentProbability = 0;
    $accumulatedProbability = $array[0];
    $currentPosition = 0;

    while ($currentProbability < 100) {
        if ($currentProbability > $accumulatedProbability) {
            $currentPosition++;
            $accumulatedProbability += $array[$currentPosition];
        }
        array_push($myNewArray, $array[$currentPosition]);
        $currentProbability++;
    }
    shuffle($myNewArray);
    return $myNewArray;
}

0
寻找适用于所有情况或任何数字的答案。
值也可以是十进制的,例如55.55、10.10等,但总体上将是100%。
尽管您将总重量限制为100,但您希望在该范围内容纳小数值的事实意味着您不能假设最多有100个单位可供选择。如果您的粒度是十分之一,则每个潜在选择的单位将为0.1。如果指定到百分之一(如55.55),则您需要以每次0.01的相对基本单位。
因为我不想通过浮点值进行迭代,所以建议您通过消除权重和随机数生成器中的所有浮点数的因子来扩大所有值——只需乘以10/100/1000等您需要将所有权重转换为整数的因子即可。
现在让我们尽可能地缩短迭代过程:
  1. 遍历输入数组一次,以确定最长的小数精度。
  2. 在0到(所有权重之和减1)乘以10的“最长表示小数长度”的幂之间选择一个随机整数。
  3. 再次遍历输入数组,并简单地检查随机整数是否小于当前权重加上任何先前的权重;如果不是,则中断循环,因为已经找到了所选的加权随机数。

代码:(演示)--演示进行了10次迭代,以帮助揭示加权效果

$valueWeights = [
    149 => 55.555,
    130 => 10.0050,
    131 => 5,
    132 => 5.2,
    133 => 10,
    134 => 10.24,
    135 => 5
];

$mostDecimals = 0;
// not bothering to validate against infinite and extremely fringe case floats
foreach ($valueWeights as $value => $weight) {
    $tempDecimals = 0;
    while ((string)$weight !== (string)floor($weight)) {
        $weight *= 10;  // this is not permanently mutating the weight
        ++$tempDecimals;
    }
    $mostDecimals = max($mostDecimals, $tempDecimals);
}
echo "Most Decimals: {$mostDecimals}\n";
$factor = pow(10, $mostDecimals);
echo "Factor: " , $factor , "\n";
$totalWeight = (array_sum($valueWeights) - 1) * $factor;


for ($i = 0; $i < 10; ++$i) {
    $rand = mt_rand(0, $totalWeight);
    echo "\nRand: " , $rand , "\n";
    $cumulativeScaledWeight = 0;
    foreach ($valueWeights as $value => $weight) {
        $cumulativeScaledWeight += $weight * $factor;
        if ($rand < $cumulativeScaledWeight) {
            echo "Value: {$value}\n";
            break;
        }
    }
}

输出:

Most Decimals: 3
Factor: 1000

Rand: 52197
Value: 149

Rand: 33785
Value: 149

Rand: 4783
Value: 149

Rand: 24994
Value: 149

Rand: 76588
Value: 133

Rand: 77417
Value: 133

Rand: 40541
Value: 149

Rand: 80009
Value: 133

Rand: 14826
Value: 149

Rand: 52691
Value: 149

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接