SQL中的随机偏差?

时间:2010-10-31 15:38:06

标签: sql mysql random statistics

我的数据库中有一些条目,在我的案例中有评级和受欢迎程度的视频以及其他因素。在所有这些因素中,我计算一个可能因子或更多来说一个提升因子。

所以我基本上有字段ID和BOOST。提升的计算方式是结果是一个整数,表示相应的条目应该被命中的百分比。

ID  Boost
1   1
2   2
3   7

因此,如果我无限期地运行我的随机函数,我应该在ID 1上获得X点击,在ID 2上获得两倍,在ID 3上获得7倍。

所以每次击中都应该是随机的,但概率为(boost / sum of boosts)。因此,此示例中ID 3的概率应为0.7(因为总和为10.为简单起见,我选择这些值。)

我想到了类似以下问题的内容:

SELECT id FROM table WHERE CEIL(RAND() * MAX(boost)) >= boost ORDER BY rand();

不幸的是,在考虑了表格中的以下条目之后,这不起作用:

ID  Boost
1   1
2   2

它有50/50的机会,只有第二个或两个元素可以随机选择。

所以0.5点击进入第二个元素 0.5点击进入(第二个和第一个)元素,它是随机选择的,所以每个0.25。 所以我们最终得到0.25 / 0.75比率,但应该是0.33 / 0.66

我需要一些修改或新方法才能以良好的性能完成此任务。

我还想过累积存储boost字段,所以我只是从(0-sum())做一个范围查询,但是如果我更改它或开发一些交换,我将不得不重新索引一个项目之后的所有内容算法或其他东西......但那真的不是优雅和东西。

插入/更新和选择都应该快!

你有解决这个问题的方法吗?

最好的用例可能是广告投放。 “请选择一个具有给定概率的随机广告”......但是我需要它用于其他目的,但只是为了给你最后的图片它应该做什么。

编辑:

感谢kens回答我想到了以下方法:

  1. 从0-sum(不同提升)计算随机值

    SET @randval =(从测试中选择ceil(rand()* sum(DISTINCT boost));

  2. 从所有不同的提升因子中选择提升因子,其中加起来超过随机值

  3. 然后我们在第一个例子中有一个0.1,2,一个0.2和7,概率为0.7。

    1. 现在从具有此提升因子的所有条目中选择一个随机条目
    2. 问题:因为具有一次提升的条目数总是不同的。例如,如果只有1个提升的条目,我会在10个呼叫中的1个中获得它,但如果有1个有7个,则每个都很难返回... 所以这并没有成功:(试图改进它。

      我必须以某种方式包含具有此提升因子的条目数...但我不知何故坚持...

3 个答案:

答案 0 :(得分:3)

您需要为每行生成一个随机数并对其进行加权。

在这种情况下,RAND(CHECKSUM(NEWID()))绕过RAND的“每个查询”评估。然后简单地将它乘以boost和ORDER BY结果DESC。 SUM..OVER为您提供全面提升

DECLARE @sample TABLE (id int, boost int)

INSERT @sample VALUES (1, 1), (2, 2), (3, 7)

SELECT
    RAND(CHECKSUM(NEWID())) * boost  AS weighted,
    SUM(boost) OVER () AS boostcount,
    id
FROM
    @sample
GROUP BY
    id, boost
ORDER BY
    weighted DESC

如果你有不同的提升值(我认为你提到过),我也会考虑使用LOG(基础e)来平滑分布。

最后,ORDER BY NEWID()是一个不考虑提升的随机性。种子兰德很有用,但不能单独播种。

此示例在SQL Server 2008上放在一起,BTW

答案 1 :(得分:2)

我敢用两个查询建议直接解决方案,使用累积增量计算。

首先,选择增强之和,并在0和增加和之间生成一些数字:

select ceil(rand() * sum(boost)) from table;

该值应存储为变量,我们称之为{random_number}

然后,选择表格行,计算增加的​​累积总和,并找到第一行,累积提升大于{随机数}:

SET @cumulative_boost=0;
SELECT
  id,
  @cumulative_boost:=(@cumulative_boost + boost) AS cumulative_boost,
FROM
  table
WHERE
  cumulative_boost >= {random_number}
ORDER BY id
LIMIT 1;

答案 2 :(得分:0)

我的问题很相似:每个人在最终抽奖中都有计算出的门票数量。如果你有更多的门票,那么你将有更高的机会赢得彩票"。

由于我不信任任何找到的结果rand() * multiplier或网络上-log(rand()),我想实现我自己的直接解决方案。

我做了什么,在你的情况下看起来有点像这样:

(SELECT id, boost FROM foo) AS values
INNER JOIN (
    SELECT id % 100 + 1 AS counter 
    FROM user 
    GROUP BY counter) AS numbers ON numbers.counter <= values.boost
ORDER BY RAND()

由于我不经常使用它,所以我并不关心未来的表现,而且目前对我来说速度很快。

在我使用此查询之前,我检查了两件事:

  1. boost的最大数量小于数字查询
  2. 中返回的最大数量
  3. 内部查询返回介于1..100之间的所有数字。它可能不依赖于你的桌子!
  4. 由于我有1到100之间的所有不同数字,然后加入numbers.counter <= values.boost意味着如果一行增加2,它最终会在最终结果中重复。如果一行的增益为100,那么它最终会在最后一次增加100次。或者换句话说。如果提升的总和是4212,那么在我的情况下,你将在最后一组中有4212行。

    最后我让MySql随机排序。

    修改:要使内部查询正常工作,请确保使用较大的表格,或确保ID不会跳过任何数字。更好,甚至可能更快一点,你甚至可以创建一个临时表,只需要所有数字在1..n之间。然后你可以简单地使用INNER JOIN numbers ON numbers.id <= values.boost