选择随机行但有赔率

时间:2010-08-03 12:53:01

标签: php mysql random probability

我有一个行数据集,每个行的“赔率”数字在1到100之间。我希望以最有效的方式做到这一点。赔率不一定达到100。

我有一些想法。

A) 选择整个数据集,然后添加所有可能性并生成1到该数字之间的随机数。然后遍历数据集,从数字中扣除赔率,直到为0。

我希望尽量减少对数据库的影响,所以我考虑过是否只能选择我需要的行。

b)中

SELECT * FROM table WHERE (100*RAND()) < odds

我考虑过LIMIT 0,1

但是如果项目具有相同的概率,则只返回其中一项

或者选择整个数据集并从中选择一个随机的...但随后赔率受到影响,因为它随机变为随机赔率然后随机而没有赔率因此赔率倾向于支持更高的赔率(甚至更多)。

我想我可以order by odds ASC然后获取整个数据集,然后使用PHP随机输出与第一条记录相同的几率(最低)。

似乎是一个笨拙的解决方案。

有没有人有优秀的解决方案?如果不是以上哪一个最好?

7 个答案:

答案 0 :(得分:3)

做一些前期工作,在表格中添加一些有助于选择的列。例如,假设您有这些行

 X  2  
 Y  3
 Z  1

我们添加一些累积值

 Key Odds Start  End 
 X    2     0     1      // range 0->1, 2 values == odds
 Y    3     2     4      // range 2->4, 3 values == odds
 Z    1     5     5      // range 5->5, 1 value == odds

开始和结束选择如下。第一行的起点为零。后续行的开头比前一行多一个。结束是(开始+赔率-1)。

现在选择0到Max(结束)范围内的随机数R

Select * from T where R >= T.Start and R <= T.End

如果数据库足够聪明,我们可以使用

 Select * from T where R >= T.Start and R <= (T.Start + T.Odds - 1)

我猜测让一个带有索引的End列可以提供更好的性能。此外,Max(End)可能会被隐藏在某处,并在必要时通过触发器进行更新。

显然,更新Start / End会有一些麻烦。这可能不是太糟糕,如果

  • 表格内容稳定
  • 或插入在某种程度上是自然排序的,因此每个新行只是从旧的最高位继续。

答案 1 :(得分:0)

我没有尝试过,但也许是这样的(带有从0到SUM(odds) - 1的随机数)?

SET @prob := 0;

SELECT
  T.*,
  (@prob := @prob + T.odds) AS prob
FROM table T
WHERE prob > ?
LIMIT 1

这与你的想法a)基本相同,但完全在一个(好的,技术上是两个,如果算上变量设置)SQL命令。

答案 2 :(得分:0)

如果您在赔率列和主键上有索引,这将非常有效:

SELECT id, odds FROM table WHERE odds > 0

数据库甚至不必从表中读取,它将从赔率索引中获得所需的一切。

然后,您将选择介于1和返回行数之间的随机值。

然后从返回的行数组中选择该行。

然后,最后,选择整个目标行:

SELECT * FROM table WHERE id = ?

这确保所有行之间的均匀分布具有赔率值。


或者,将赔率放在另一个表中,并使用自动增量主键。

Odds
ID     odds
1      4
2      9
3      56
4      12

将ID外键存储在主表中而不是赔率值,并将其编入索引。

首先,获取最大值。这永远不会触及数据库。它使用索引:

SELECT MAX(ID) FROM Odds

获取1到最大值之间的随机值

然后选择记录。

SELECT * FROM table
JOIN Odds ON Odds.ID = table.ID
WHERE Odds.ID >= ?
LIMIT 1

如果您倾向于删除赔率值或回滚插入以保持分布均匀,则需要进行一些维护。

书中SQL Antipatterns有一整章随机选择。

答案 3 :(得分:0)

如果您使用了代码,并添加了ORDER BY RAND()LIMIT 1

,该怎么办?
SELECT * FROM table WHERE (100*RAND()) < odds ORDER BY RAND() LIMIT 1

这样,即使你有相同概率的倍数,它总会随机排序,然后你只需要第一个条目。

答案 4 :(得分:0)

select * from table 
where id between 1 and 100 and ((id % 2) <> 0) 
order by NewId() 

答案 5 :(得分:0)

嗯。不完全清楚你想要什么结果,所以如果这有点疯狂,请耐心等待。话虽如此,怎么样:

制作一张新桌子。该表是一个固定的数据表,如下所示:

Odds
====
   1
   2
   2
   3
   3
   3
   4
   4
   4
   4
etc, 
etc.

然后从您的数据集加入到赔率列上的该表。对于表中的每一行,您将获得与该行的给定几率一样多的行。

然后随机选择其中一个。

答案 6 :(得分:0)

适用于O(log(n))更新的通用解决方案是这样的:

  • 将对象存储为(平衡)树的叶子。
  • 在每个分支节点处,存储其下所有对象的权重。
  • 添加,删除或修改节点时,请更新其父级的权重。

然后选择一个介于0和(总重量 - 1)之间的数字,然后向下导航,直到找到合适的物体。

由于您不关心树中的事物顺序,您可以将它们存储为N个指针和N-1个数字的数组。