【问题标题】:Algorithm/Query For Determine Weighted Reward确定加权奖励的算法/查询
【发布时间】:2020-02-16 07:46:42
【问题描述】:

我正在开发一款游戏,其中向用户提供随机奖励,每个奖励的交付机会由“分享”系统加权。为了说明这个问题,我们来看一个例子:

假设我要给出一个纯粹的随机奖励,超出集合:[A, B, C]。就像获取随机索引并从数组中返回任何奖励一样简单。

然而,在我的用例中,奖励集是带有“分享”系统的权重。所以我们有如下情况:

  • A - 重量为 2。
  • B - 重量为 3。
  • C - 重量为 5。

所以集合看起来像这样:[A, A, B, B, B, C, C, C, C, C]。

现在,我可以构建该数组并获得这样的随机结果,但我担心性能影响。这些奖励存储在数据库中(如果考虑在内,则使用 Django 和 PSQL 作为后端),并且可能有超过 100 个潜在奖励,每个奖励的权重为 1-100(或更多)。

所以我试图找出一种有效的方法来根据这样的权重提取随机奖励。


在我写这篇文章时,我想到了一件事(但我想听听其他想法):

  • 更新奖励集后,构建一次数组,然后为每个奖励分配一个百分比机会。所以计算一次百分比。
  • 在上面,我们最终会得到如下结果:[A: 0.2, B: 0.3, C: 0.5]。
  • 那么我们就可以得到一个0到1之间的随机数,然后拉出最接近的奖励而不用过去? (所以随机掷出 0.45 会得到 B,因为 0.3 最接近 0.45 而不会高于 0.45)。
    • 我正在努力思考如何为此编写高性能查询。也许查询所有百分比低于掷骰的奖励(上例中为 0.45),并从这些结果中返回百分比最高的奖励。

我想我可能只是回答了我自己的问题,但我希望能从第三方的角度来看。谢谢大家!

【问题讨论】:

  • 我认为我提出的答案存在一些逻辑问题。如果我们总是选择比掷骰子低的东西,我们就会错误地偏爱较低的结果。所以在这个例子中,B 有 30% 的机会被选中(在纸面上),但 0.45 的卷可能应该选择 C(我们有 50% 的机会选中)。
  • 呃,有点像。我太专注于我的问题的特定于 Django 的性质,而忽略了链接的问题(我已经看到了)。我标记为最佳的答案同样是直接的 PSQL 查询(而不是特定于 Django 的代码),这无济于事。

标签: python django postgresql algorithm random


【解决方案1】:

您应该使用累积百分比。示例设置:

create table weights(label text, weight int, cumulative_percentage float);
insert into weights (label, weight) values
('A', 2),
('B', 3),
('C', 5);

update weights w
set cumulative_percentage = cumulative_sum::float/ total
from (
    select 
        label,
        sum(weight) over (order by label) as cumulative_sum,
        sum(weight) over () as total
    from weights
    ) s
where s.label = w.label;

所以表格是这样的:

select *
from weights

 label | weight | cumulative_percentage 
-------+--------+-----------------------
 A     |      2 |                   0.2
 B     |      3 |                   0.5
 C     |      5 |                     1
(3 rows)

使用公用表表达式循环获取单个随机数:

with seed(r) as (select random())
select min(label)
from weights, seed
where r <= cumulative_percentage

或者创建一个函数:

create or replace function get_percentageed_reward(r float)
returns text language sql as $$
    select min(label)
    from weights
    where r <= cumulative_percentage
$$; 

检查算法的结果:

select get_percentageed_reward(random()), count(*)
from generate_series(1, 100000)
group by 1
order by 1

 get_percentageed_reward | count 
-------------------------+-------
 A                       | 20008
 B                       | 30165
 C                       | 49827
(3 rows)    

【讨论】:

  • 就是这样。 “累积”部分是缺失的成分。所以不是[A:0.2,B:0.3,C:0.5]的百分比集合,而是[A:0.2,B:0.5,C:1.0]。我们会选择最接近但高于滚动的奖励。魔术,我的好先生,还有一个有据可查的答案!
  • 并在我的项目中实施,效果非常好。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-29
相关资源
最近更新 更多