代码之家  ›  专栏  ›  技术社区  ›  The Surrican

SQL中的有偏随机?

  •  1
  • The Surrican  · 技术社区  · 15 年前

    所以我基本上有ID和BOOST两个字段,BOOST的计算方式是,它是一个整数,表示这个条目在比较中应该被命中的百分比。

    ID  Boost
    1   1
    2   2
    3   7
    

    所以,如果我无限期地运行随机函数,我应该在ID 1上得到X的点击,在ID 2上是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和中计算一个随机值(不同的boost)

    2. 从所有累计超过随机值的不同激励因子中选择激励因子

    在我们的第一个例子中,1的概率为0.1,2的概率为0.2,7的概率为0.7。

    1. 现在从所有具有此提升因子的条目中选择一个随机条目

    问题: 所以这不可行:(试图完善它。

    我得把条目的数量加上这个助推因子。。。但我不知怎的被困在那。。。

    3 回复  |  直到 15 年前
        1
  •  4
  •   gbn    15 年前

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

    在这种情况下, RAND(CHECKSUM(NEWID())) 绕过的“每个查询”计算 RAND . 然后简单地乘以boost和ORDER,再乘以结果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
    

    如果你有非常不同的boost值(我想你提到过),我也会考虑使用LOG(以e为基数)来平滑分布。

    这个示例放在SQL Server 2008上,顺便说一句

        2
  •  2
  •   Kel    15 年前

    我敢建议用两个查询直接解决问题,使用累积boost计算。

    首先,选择sum of boosts,并生成介于0和boost sum之间的数:

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

    这个值应该作为变量存储,我们称它为{随机数}

    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;
    
        3
  •  0
  •   Artur K.    10 年前

    我的问题是相似的:在最后的抽签中每个人都有一个计算好的票数。如果你有更多的彩票,那么你将有更高的机会赢得“彩票”。

    因为我不相信任何发现的结果 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. 内部查询返回1..100之间的所有数字。这可能不取决于你的桌子!

    因为我所有的数字都在1到100之间 numbers.counter <= values.boost 也就是说,如果一行的升幅为2,那么最终结果将是重复的。如果一排有一个100倍的助推,它将在最后一盘结束100次。或者换句话说。如果助推的总和是4212,在我的例子中,你在最后一盘有4212行。

    编辑: INNER JOIN numbers ON numbers.id <= values.boost

    推荐文章