JavaTips之分页随机


2020年02月22日

1、背景

比如 你有一个抽样的需求,即 从一堆数据中抽样出样本数据,而且最好要 随机,考虑这样一个场景:

A 是一个接收业务数据的 服务端,B 源源不断的一批一批数据的发送给 A,而且 这些数据都是按照时间先后顺序发送的, 如何从 B 发给 A 的全部数据中 “随机” 抽样(譬如 按照 百分比)出样本数据呢?

2、解决方法

2.1、内存随机

将 全部数据 存于内存中,等全部接收完毕后,再整体 按照百分比随机

问题:

  • 怎么知道 全部已经接收完成?约定结束标志
  • 数据太大,内存并不合适

2.2、内存随机,唯一标志映射

将全部数据存于 表中,且 将 每条数据的 唯一标志,如 主键ID,与 1~N 做 映射 存于 内存, 然后 从 1~N 随机,然后 取对应的 唯一标志,从表中取完整数据

  • 数据太大,也并不合适

2.3、表中随机

将数据全部存于 表中,利用 数据库随机函数等随机出样本

  • 一般不建议使用数据库随机函数等,且数据过大不合适

2.4、表中分页随机

将 全部数据存于表中,分页取数据,每页 按照百分比 随机取出 n 条

  • 如果 每页取出的 n 过多,则 不用 遍历完 所有页数,即可 取完数据
  • 如果 每页取出的 n 过少,则 可能遍历完 所有页数,最后 数据总和 不够 百分比条件 概率 如:在 将 全部数据存到表中后,设 数据总条数为 t,每页存储 n 条,需要 按照 p 的概率抽取数据(p是百分比), 那么 每页 取多少条合适呢?

由以上可知:

  • 假设 全部数据 共存了 x 页, 则 满足 x * n >= t
  • 假设 每页取 n’ 条合适
  • 按照 p 的概率需要取出的样本数据大小为 tp (暂不考虑小数等) 条
  • 满足条件: tp - (x - 1) * n’ <= t - (x - 1) * n,即 每页取n’条时,只要满足 最后一页的数据大小 小于 每页取 n条时的最后一页数据,这样就不会出现(每页取出的 n’ 过少,则 可能遍历完 所有页数,最后 数据总和 不够 百分比条件)

由 等式 tp - (x - 1) * n’ <= t - (x - 1) * n 推出:

n’ >= n + t * (p - 1)/(x - 1)

且 n’ <= n

综合 n >= n’ >= n + t * (p - 1)/(x - 1)

即 n’ 最多取 n条,最少取 n + t * (p - 1)/(x - 1) 条

举例: t = 25, n = 6,则 x = 5(每页存6条,最后一页存了 1 条),p = 60%,则 抽样样本需要抽取 t * p = 15条

且 n + t * (p - 1)/(x - 1) = 3.5 即 6 >= n’ >= 3.5 即 n’最多取 6条,最少需要取 4条(取整)才能满足 60% 的抽样

如果 每页取 少于 4条,如 3,那么 前 4页能取 12条,最后一页 只有 1条,只能总共取 13条数据,无法满足 15条数据的抽样。

如果你想每页都可以 取到(譬如 按照时间分页 可以雨露均沾),那么就取 最小值,即 4条。

当然 也可以把 每批次发送的大小当做 每页存储的 大小,就不用存表了,有其他额外需要再存。