繁体   English   中英

如何在 PostgreSQL 中动态执行加权随机行选择?

[英]How to dynamically perform a weighted random row selection in PostgreSQL?

我有一个应用程序的下表,其中学生被分配任务来玩教育游戏。

学生{id,last_played_datetime,total_play_duration,total_points_earned}

该应用程序随机选择一名学生并分配任务。 学生只需玩游戏即可获得一分。 该应用程序记录玩游戏的日期和时间以及持续时间。 我想随机 select 一个学生并分配任务。 一次只能为一名学生分配任务。 为了给所有学生平等的机会,我正在使用学生上次玩游戏的日期和时间、总游戏时间和学生获得的总积分动态计算学生的体重。 然后将随机选择一个受体重影响的学生。

我如何在 PostgreSQL 中根据动态计算的行权重从表中随机 select 行?

每个学生的权重计算如下:(分钟(current_datetime - last_played_datetime) * 0.75 + total_play_duration * 0.5 + total_points_earned * 0.25) / 1.5

样本数据:

+====+======================+=====================+=====================+
| Id | last_played_datetime | total_play_duration | total_points_earned |
+====+======================+=====================+=====================+
| 1  | 01/02/2011           | 300 mins            |  7                  |
+----+----------------------+---------------------+---------------------+
| 2  | 06/02/2011           | 400 mins            |  6                  |
+----+----------------------+---------------------+---------------------+
| 3  | 01/03/2011           | 350 mins            |  8                  |
+----+----------------------+---------------------+---------------------+
| 4  | 22/03/2011           | 550 mins            |  9                  |
+----+----------------------+---------------------+---------------------+
| 5  | 01/03/2011           | 350 mins            |  8                  |
+----+----------------------+---------------------+---------------------+
| 6  | 10/01/2011           | 130 mins            |  2                  |
+----+----------------------+---------------------+---------------------+
| 7  | 03/01/2011           |  30 mins            |  1                  |
+----+----------------------+---------------------+---------------------+
| 8  | 07/10/2011           |   0 mins            |  0                  |
+----+----------------------+---------------------+---------------------+

这是一个工作原理如下的解决方案:

  • 首先计算每个学生的体重
  • 将所有学生的权重相加并乘以随机种子
  • 然后选择高于该目标的第一个学生,随机,权重

询问:

with 
    student_with_weight as (
        select 
            id,
            (
                extract(epoch from (now() - last_played_datetime)) / 60 * 0.75
                + total_play_duration * 0.5
                + total_points_earned * 0.25
            ) / 1.5 weight
        from student
    ),
    random_weight as (
        select random() * (select sum(weight) weight from student_with_weight ) weight
    )
select id 
from 
    student_with_weight s
    inner join random_weight r on s.weight >= r.weight
order by id
limit 1;

您可以对权重使用累积总和并与rand()进行比较。 它看起来像这样:

with s as (
      select s.*, 
             <your expression> as weight
      from s
     )
select s.*
from (select s.*,
             sum(weight) over (order by weight) as running_weight,
             sum(weight) over () as total_weight
      from s
     ) s cross join
     (values (random())) r(rand)
where r.rand * total_weight >= running_weight - weight and
      r.rand * total_weight < running_weight;

values()子句确保随机值只为查询计算一次。 如果将random()放在where子句中,可能会发生一些奇怪的事情,因为每次比较都会重新计算它。

基本上,您可以将累积总和视为将总数划分为离散区域。 然后rand()只是选择其中之一。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM