簡體   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