根据" weight"随机选择行。那一行

时间:2018-02-16 12:52:48

标签: php mysql sql random

我有一张这样的表:

ID chance
1 1
2 2
3 4
4 1

现在我需要从这个表中选择一个rand()

SELECT * FROM table
ORDER BY RAND()
LIMIT 1

但ID#2与ID#1 AND 4相比,选择的机会增加了一倍。与ID#1 AND 4相比,ID#3的选择机会是其四倍。

与基于偶然性的彩票有点类似。

4 个答案:

答案 0 :(得分:2)

这是彩票在某些游戏中的运作方式。给出类似于您的示例的表(例如,我们还有1 + 2 + 4 + 1 = 8列表示获得此特定奖励的基于值的可能性),算法为:

  1. 计算总彩票值(在您的示例中为1..max)。
  2. 生成范围max(当前示例中85)的值。
  3. 对奖励列表中的所有项目进行迭代,找到所有先前机会总和超过生成数量但小于或等于
  4. 的项目

    说,我们已经生成了数字0 < 5 <= (0) + 1。我们的比较步骤是:

    1. 1 < 5 <= (1) + 2是假的,所以ID1不是我们得到的。左边是0,因为我们从0开始计算。
    2. 1 + 2 < 5 <= (1 + 2) + 4是假的,所以ID2不是我们得到的。
    3. var rewards = [ { id: 1, chance: 1 }, { id: 2, chance: 2 }, { id: 3, chance: 4 }, { id: 4, chance: 1 } ]; function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function generate() { var sum = 0; var next_sum = 0; var random = getRandomInt(1, rewards.reduce(function(pv, cv) { return pv + cv.chance; }, 0)); for (var i = 0; i < rewards.length; i++) { next_sum = sum + rewards[i].chance; if ((random > sum) && (random <= next_sum)) { return rewards[i].id; } sum += rewards[i].chance; } } var winnerCounts = {}, i, winner; for (i = 0; i < 8000; i++) { winner = generate(); winnerCounts[winner] = (winnerCounts[winner] || 0) + 1; } console.log("Number of times each id was selected after %d itrations", i); console.log(winnerCounts);是真的,所以我们得到了ID3。
    4. JavaScript中的示例:

      &#13;
      &#13;
      decode-hex-string ( a1 u1 a2 u2 -- a2 u )
      &#13;
      &#13;
      &#13;

答案 1 :(得分:2)

以下是SQL Fiddle,仅提供MySQL解决方案

select * from (
  select id, @running_total as previous_total, @running_total := @running_total + chance AS running_total, until.rand
  from (
    select round(rand() * init.max) as rand from (
      select sum(chance) - 1 as max from demo
    ) as init
  ) as until,
  demo,
  ( select @running_total := 0.00 ) as vars
) as results
where results.rand >= results.previous_total and results.rand < results.running_total

算法如下:

  1. 找出所有机会的总和,并将其存储在max
  2. 在区间[0, max)
  3. 中生成随机数
  4. 对于每一行,计算到目前为止遇到的机会previous_total (initially 0)current_total
  5. 仅保留生成的数字在[previous_total, current_total)
  6. 区间内的行

    因为我们甚至有机会在[0, sum_of_all_chances)区间内选择每个数字,所以我们可以在这个区间内为每个条目设置尽可能多的数字,因为它有机会被选中,确保均匀分布。

    @running_total只是一个MySQL变量,我只使用( select @running_total := 0.00 ) as vars作为给它初始值的方法。 另外,我使用( select round(rand() * init.max) as rand from ( select sum(chance) - 1 as max from demo ) as init ) as until作为总结机会并存储MySQL rand函数生成的随机数的方法。希望这使代码易于理解。

答案 2 :(得分:0)

您有一个概率列表: 1 / 8 2 / 8 4 / 8 1 / 8 ,您需要根据此选择项目。解决方案是选择1到8之间的数字,然后在这些范围[1,1] [2,3] [4,7] [8,8]内查找。

这是SQL查询,它根据机会为每行添加下限和上限:

SELECT
    testdata.id,
    testdata.chance,
    SUM(IFNULL(prevdata.chance, 0)) + 1 AS lb,
    SUM(IFNULL(prevdata.chance, 0)) + testdata.chance AS ub
FROM testdata
LEFT JOIN testdata AS prevdata ON testdata.ID > prevdata.ID
GROUP BY testdata.id, testdata.chance

输出:

+------+--------+------+------+
| id   | chance | lb   | ub   |
+------+--------+------+------+
|    1 |      1 |    1 |    1 |
|    2 |      2 |    2 |    3 |
|    3 |      4 |    4 |    7 |
|    4 |      1 |    8 |    8 |
+------+--------+------+------+

其余的是直截了当的。选择介于1和SUM_OF_ALL_CHANCES:

之间的随机数
SELECT @total := SUM(chance) FROM testdata;
SELECT @random := FLOOR(RAND() * @total) + 1;

在上表中查找该数字:

SELECT
    testdata.id,
    testdata.chance,
    SUM(IFNULL(prevdata.chance, 0)) + 1 AS lb,
    SUM(IFNULL(prevdata.chance, 0)) + testdata.chance AS ub
FROM testdata
LEFT JOIN testdata AS prevdata ON testdata.ID > prevdata.ID
GROUP BY testdata.id, testdata.chance
HAVING @random BETWEEN lb AND ub

答案 3 :(得分:0)

如果你需要清晰的MySQL解决方案,你可以使用它:

  

SELECT id FROM`table` ORDER BY -LOG(1-RAND())/ chance LIMIT 1

这是关于从指数分布中选择一个随机数 http://www.tushar-mehta.com/publish_train/xl_vba_cases/0806%20generate%20random%20numbers.shtml

简单代码&#34;仅用于测试&#34;

$sql = "SELECT id FROM `table` ORDER BY -LOG(1-RAND())/chance LIMIT 1";
$Res=array();
for ($i=0;$i<10000;$i++) {
    $result = mysqli_query($db,$sql);
    $row=mysqli_fetch_array($result, MYSQLI_ASSOC);
    if (isset($row['id'])) {
       echo "$i. => ".($row['id'])."\n";
       if (!isset($Res[$row['id']])) $Res[$row['id']]=0;
       $Res[$row['id']]++;
    } else {
        echo ' error.432 ';exit;
    }
}

print_r($Res);

你会看到&#34; 2&#34;两倍于#34; 4&#34;或&#34; 1&#34;。并且&#34; 3&#34;比&#34; 2&#34;

多两倍