最近修改了网站的抽奖算法,使得抽奖看起来更加『公平』,为此我整理了下,谈谈在抽奖系统设计中的『坑』。

抽奖分为两种:

知道总人数

不知道总人数

举栗子

1. 已知人数

14 个奖品分给 500 个人:

奖品分为一等奖、二等奖、三等奖;

总人数 500 人。

奖品

数量

一等奖

1

二等奖

3

三等奖

10

设计思路:

为 500 人设计序号,1 - 500;

生成中奖序列(伪代码)

// 中奖序号, 大奖为最后一个

awardIds = List()

// random 出序号

while (awardIds.size() < 14) {

rand = Math.ceil(random() * 500); // 取整

// 限不限制都可以,概率太低

awardIds.contains(rand) ? awardIds.push(rand);

}

这种设计下,每个人的中间概率都是 1/500 之一。

这样就选出了中奖列表了,然而有『坑』,后面再说这个问题。

2. 开放人数

需求栗子

设计一个简单的开放人数抽奖系统。

奖品分为一等奖、二等奖、三等奖;

保证每个人的抽奖概率一致。

奖品

数量

概率

一等奖

1

0.001

二等奖

3

0.005

三等奖

10

0.01

设计思路

利用程序自带的 random 函数,很容易生成一个 0 ~ 1 之间的随机数,可以直接这样设计:

奖品

中奖区间

一等奖

[0, 0.001)

二等奖

[0.001, 0.006)

三等奖

[0.006, 0.016)

只要 random() 落在了哪个区间,就中了几等奖。

这种情况下因为是对区间取值,所以每次抽奖的概率都是一样的。

说完栗子,下面来说说 random 函数。

random 函数

大部分语言都自带 random 函数,然而都只是伪随机,是由可确定的函数(常用线性同余),通过一个种子( 常用当前时间),产生的伪随机数。

这意味着:如果知道了种子(seed),或者已经产生的随机数,都可能获得接下来随机数序列的信息。即可以预测。

种子

由于各个语言实现 random 的方式不一样,所以栗子 1、2 会有不同的问题存在。

栗子 1 中可能会出现种子没有更改的情况,这种情况下生成的序列可以预测,设计没有问题,而结果变成了『坑』。而如果种子时间相关就可以避免这个问题。

栗子 2 中如果同一时间有多个用户同时开始抽奖,这个时候时间相关的种子又会带来问题,即:只要这几个用户有一个中奖,就意味着这个时间点的用户都中奖了。

真随机

程序并不能产生真正的随机数,但是可以产生理论上的真随机数。

如 UNIX 中的 /dev/random 可以看成是一个真随机的生成器。

具体来讲就是生成器有一个容纳噪声数的熵池,在读取时,/dev/random 设备会返回小于熵池噪声总数的随机字节,比如任何硬件的状态变化,IO 响应时间、磁盘读写速度、中断、网络变化等等,都会作为熵反馈给生成器,所以理论上就产生了不可预测性。

如果程序中 random 并不能满足随机要求,可以为种子设计一个熵,将不可预知的事件收集起来,作为种子产生随机数。

重新设计栗子 2

现在我们有了真随机函数,然后栗子 2 依然不是一个可用的抽奖系统,依然有一些问题。

问题

重复中奖

同一个用户重复中一种奖品?

必然要限制,不然很容易被认为有 py 交易。

同一个用户重复中多个奖品?

限制,虚拟奖品(积分等)无所谓,实体奖品限制。

奖品放出

抽奖开始就放出所有奖品?

由于无法预估人数,概率很难调整。

抽奖开始后奖品一段时间一段时间的放出。

防止大量的人都在放出的点抽取,而送光奖品。

区间清理

如果某个奖品被抽光了,是否剔除该奖品的区间?

剔除不剔除都没关系,为了简化处理(偷懒),实际上是可以不用理会的。

重新设计

在保持核心中奖逻辑不变(概率区间)的情况下,为栗子 2 添加限制。

唯一中奖

设计了奖品(award)之后,再添加一张 具体奖品(cdkey),使用 user 表示中奖用户,默认值 null。

字段

类型

默认值

...

...

...

user

int、UNIQUE

null

如果使用 MySQL ,MySQL 的 UNIQUE 限制并不校验 null 值,所以在限制唯一中奖上并不用单独处理。

如果不做 UNIQUE 限制,那么就需要手动查询了:

# SQL 示例, 一次选出两条,检查用户是否中奖过

SELECT * FROM ckdey WHERE award = ? AND (user = ? OR user = null) LIMIT 2;

奖品放出时间

为了限制奖品的放出时间,可以在 cdkey 上再添加 ready 表示放出时间:

奖品

ready

一等奖 1

08:00:00

二等奖 1

08:00:00

二等奖 2

12:00:00

二等奖 3

16:00:00

三等奖 1

08:00:00

...

...

这种方法在奖品数少的时候特别好使,而且还可以指定开奖时间,然而当奖品数很多的时候,就没那么好使了。

当不需要使用指定时间时,完全可以利用 random 的特性来随机生成时间,不需要写入数据库。

在 award 上增加 count(总数) 和 remain (剩余数量)

以 remain 的线性函数作为随机数种子,则每次随机数的序列都是一样的。

# 等分抽奖时间

average = Math.ceil((expirationTime - startTime) / count);

# 相同的种子得到生成器

rand = seedrand(transform(remain))

# 获取奖品放出时间

readyTime = expirationTime + Math.ceil(average * rand()) - average * remain;

这样对于每个用户,每次放出的时间都是相同的(不看源码也不知道是什么时候),每次放出一个奖品,第一段时间没送出去,就累积到第二段时间,依次后推。

PS: 这种设计下,中奖的人真的是运气爆表,万年非洲人表示很忧伤!!

python 抽奖 完全公平的随机数算法_抽奖 随机数相关推荐

  1. java红包金额随机数算法_实时随机数算法(微信红包分配算法)

    微信红包算法在知乎上面有个专题讨论,其实红包的发放的随机算法,有两种作法:java 一.预生产: 算法 无外乎是在发红包的时候,随机去把金额生成到某个容器当中,而后要用的时候,一个一个的POP:微信 ...

  2. java中奖率算法_抽奖概率-三种算法

    一.逢"几"中奖 逢"几"中奖,即通过预估抽奖人数和奖品数来判断,"几"=(抽奖人数/奖品数)*N.这是一种最简单抽奖算法,适合抽奖人数众多 ...

  3. python卷积神经网络cnn的训练算法_【深度学习系列】卷积神经网络CNN原理详解(一)——基本原理...

    上篇文章我们给出了用paddlepaddle来做手写数字识别的示例,并对网络结构进行到了调整,提高了识别的精度.有的同学表示不是很理解原理,为什么传统的机器学习算法,简单的神经网络(如多层感知机)都可 ...

  4. python猜随机数代码_猜随机数游戏Python

    我的程序应该要求用户猜测一个0到100之间的数字,但我似乎无法得到正确的输出.此时,如果用户数大于随机数,它会打印出无限量的"你的数字太高了".同样,如果第一个用户猜测值很低,那么 ...

  5. python实现循环赛日程表问题的算法_循环赛日程表的分治算法实现实验报告gxl.doc...

    循环赛日程表的分治算法实现实验报告gxl PAGE PAGE 2 深 圳 大 学 实 验 报 告 课程名称: 算法设计与分析 实验项目名称: 分治算法 --矩阵相乘的Strassen算法及时间复杂性分 ...

  6. python实现循环赛日程表问题的算法_循环赛日程表的分治算法实现实验报告_gxl.doc...

    循环赛日程表的分治算法实现实验报告_gxl 深 圳 大 学 实 验 报 告 课程名称: 算法设计与分析 实验项目名称: 分治算法 --矩阵相乘的Strassen算法及时间复杂性分析 或--循环赛日程表 ...

  7. python概率随机抽奖源码_抽奖算法-指定概率的随机

    抽奖模型 普通概率模型 普通概率模型是最常用的一种模型,但是在游戏运营过程中的确发现很多小白玩家不能正确理解--他们认为中奖率 10% 的设定等同于抽 10 次肯定会中一次.这显然是错误的,普通概率模 ...

  8. python序列模式的关联算法_关联算法

    以下内容来自刘建平Pinard-博客园的学习笔记,总结如下: 1 Apriori算法原理总结 Apriori算法是常用的用于挖掘出数据关联规则的算法,它用来找出数据值中频繁出现的数据集合,找出这些集合 ...

  9. 用python做算法_自己用python写的螺旋矩阵生成算法

    自己用python写的螺旋矩阵生成算法 如果输入6,可以生成如下矩阵: 1 20 19 18 17 16 2 21 32 31 30 15 3 22 33 36 29 14 4 23 34 35 28 ...

最新文章

  1. Java的四种引用,强弱软虚,用到的场景(转+补充)
  2. 趣挨踢 | 用大数据扒一扒蔡徐坤的真假流量粉
  3. 互联网晚报 | 1月26日 星期三 | 春晚正式入驻视频号;小红书合并社区与电商业务;中国电信5G消息正式商用...
  4. 素材干货|UI设计师不会插画?不难搞!有了这些模板,作品安了!
  5. AI 专业人才缺口上百万,年薪 80 万远超同行
  6. Tensorflow2.0数据和部署(四)——Tensorflow高级模型部署
  7. ajax跨域请求jsonp
  8. 教室录播系统方案_全自动录播教室系统设计方案
  9. 黑客文化与介绍:黑客精英轶事
  10. 最通俗易懂的JUC多线程并发编程
  11. esp32触摸touch功能使用过程详述arduino
  12. 如何用 css 画一个正方体
  13. angular 万年历_jQuery实现的简单日历组件定义与用法示例
  14. lpk劫持方式粘滞键后门后门T00ls Lpk Sethc v3.0 正式版下载
  15. Android Manifest内容解析
  16. MySQL-InnoDB锁
  17. WHQL认证(徽标认证)步骤介绍
  18. Flask表单提交的方法
  19. mount 挂载 img
  20. 【Unity3D 灵巧小知识点】☀️ | Unity 四元数、欧拉角 与 方向向量 之间转换

热门文章

  1. 《禅与摩托车维修艺术》
  2. noip2003 侦探推理 (字符串处理)
  3. 看完这篇招聘方法论,90%CEO会心痛
  4. python中的函数
  5. OTN告警测试1:LOS
  6. 三十二、http与www服务介绍
  7. golang 将kafka的offset置为最新
  8. Python_pgzero小球抛物线运动
  9. Arduino与Proteus仿真实例-MPX4250压力传感器驱动仿真
  10. CAD得到所有图层名(网页版)