【问题标题】:Picking random element by user defined weights [duplicate]通过用户定义的权重选择随机元素[重复]
【发布时间】:2011-01-18 16:13:10
【问题描述】:

可能重复:
Generating random results by weight in PHP?

我有一个 Web 应用程序,用户可以在其中添加 1-20 个文本字符串,并为他们分配显示频率的权重。然后系统将根据定义的权重选择一个随机字符串。解决此问题的最佳方法是什么?每个字符串的权重范围值是否重要?我可以让用户为每个字符串分配一个数字(0-100)吗?您将如何选择随机字符串? (每个选择都不担心之前选择了什么,每个字符串在每次调用开始时都有相同的几率(基于权重)被选中)。

【问题讨论】:

标签: php algorithm random


【解决方案1】:

我在几个PHP游戏引擎中使用了这个函数:

<?php
/**
 * @param array $values - just the weights
 * @return integer A number between 0 and count($values) - 1
 */
function getBucketFromWeights($values) {
    $total = $currentTotal = $bucket = 0;
    $firstRand = mt_rand(1, 100);

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

    $rand = ($firstRand / 100) * $total;

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

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

    return $bucket;
}

用法

假设我有一个关联数组中的用户权重,其中每个字符串都指向它的权重:

$weighted_strings = array(
    "important string" => 100,
    "terrible string" => 10,
    "never string" => 0,
    // etc
);

如果我想根据重量拉绳子,我会这样做:

$weights = array_values($weighted_strings);
$strings = array_keys($weighted_strings);
$index = getBucketFromWeights($weights);
$selectedString = $strings[$index];

【讨论】:

  • 如果你构建一个反向关联数组,其中键是权重的总和,值是字符串,这可以进一步优化,所以像这样:0 =&gt; "important string", 100 =&gt; "terrible string", 110=&gt; "never string",这使你使用二分搜索查找所选元素。当然,对于少数几个元素,这是不值得的。
  • @biziclop 如果您要进行二分搜索,那么使用关联数组代替排序列表确实没有任何好处。
  • 它工作的代码,但我无法理解逻辑。你能解释一下它是如何工作的吗?例如,我的意思是这条线的目标是什么: $rand = ($firstRand / 100) * $total;
  • @Anton_Sh:这个想法是将权重转换为百分比。首先,他生成一个 1-100(百分比)的随机数,然后用该行将其转换为权重。其次,通过for 循环,他找到随机数落在哪个部分(想想不同长度的条),并返回该部分的索引。但是,我认为$rand = ($firstRand / 100) * $total; 仍然是多余的。为什么他一开始不使用mt_rand(0, $total)?
【解决方案2】:

这是一个简单的实现:

function Probability($data, $number = 1)
{
    $result = array();

    if (is_array($data) === true)
    {
        $data = array_map('abs', $data);
        $number = min(max(1, abs($number)), count($data));

        while ($number-- > 0)
        {
            $chance = 0;
            $probability = mt_rand(1, array_sum($data));

            foreach ($data as $key => $value)
            {
                $chance += $value;

                if ($chance >= $probability)
                {
                    $result[] = $key; unset($data[$key]); break;
                }
            }
        }
    }

    return $result;
}

使用此功能,您可以指定需要多少个唯一加权随机元素 (IDEOne)。

【讨论】:

    【解决方案3】:

    这里提供了很好的答案,但有一种方法可以节省锁定循环。更快的方式到select random value from array。其实Idea也是一样的,只是作为一个简单的循环运行起来更快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-23
      • 2016-02-01
      • 2011-06-05
      • 2011-01-09
      • 2012-06-05
      • 2014-06-11
      相关资源
      最近更新 更多