网站首页 > 精选教程 正文
前几天字节跳动的朋友发现一个很有趣的问题,在这里和读者们分享一下。
题目描述贼简单:设计一个随机抽样的系统,这个系统需要实时的从数据流中读取数据,并提供一个查询接口,这个接口需要从已读取的数据中等概率的选取K个数并返回。
一开始我以为是个设计随机算法的题目,所以给出了一个保存所有已读取数据的思路,然后刚说完“保存全部数据”就被面试官无情打断 —— 数据流很大很大,大到无法保存所有数据。
这样的话,空间复杂度应该不能超过O(K),只能是从已保存的K份数据和新读入的数据上做文章了。然后和面试官沟通了一些细节:
- O(K)的空间复杂度是否可以? (可以)
- 数据流中的数据总量会超过 int64 吗?(不会超过)
Tips:前面被打断就是因为在思考解决方案前没有和面试官沟通好数据规模。所以应该及时调整心态,多和面试官沟通一下细节。毕竟面试的时候时间和精力都很宝贵,尽量不要犯同样的错误啊老铁。
得到面试官的答案后继续思考。假设刚刚读取到第 N 个数据:
- 如果 N <= K,那么该数据被保留下的概率肯定为 1
- 如果 N > K,那么该数据被保留下的概率应为 K/N
那该如何实现呢?当 N<=K的时候,显而易见直接保留下来就可以了。
接着考虑 N = K+1 时,该元素被保留下的概率为 K/(K+1)。该元素被保留就意味着前K个元素中肯定有一个被淘汰。
那被淘汰的概率是多少呢?有K/(K+1)的概率触发淘汰操作,如果我们随机地选择一个元素,那么每个元素都有1/K的概率被选中。
综上,被淘汰的概率为 K/(K+1)*1/K,即 1/(K+1)。
因为一个元素要么保留下来,要么被淘汰,所以其保留下的概率亦为 K/(K+1)。
下面数学归纳法证明上述操作使得当输入N个元素时每个元素被保留的概率均为K/N (觉得不妥的老铁可以点击“阅读原文”一起讨论)
当 N = K 时,概率为 K/N = 1,显然成立。
当 N = K+1时,概率 为 K/N = K/(K+1),如上所述也成立。
那么当 N’ = N+1时,第N’个元素以K/N’的概率决定是否保留,此时前N元素中每个元素被保留下来的概率均为 1 - K/N * K/N’ * 1/K = K/N’。
综上可得,当已读取N个元素时,这种操作可使每个元素被保留下的概率均为 K/N。
总结一下,当读取到第N个元素时,先随机一下是否应该保留,被保留的概率为K/N。如果随机到保留,再随机一下要淘汰掉哪个元素即可。
template <typename DataType>
class RandomK {
uint64_t count;
uint32_t k;
vector<DataType> data_list;
public:
RandomK(uint32_t k_) : k(k_) {}
void input(const DataType &data) {
count += 1;
// 直接保留
if (count <= k) {
data_list.push_back(data);
return ;
}
// 随机产生一个 [0, count-1] 内的数字 x
// 如果 x ∈ [0, k-1],那么该数据应该被保留
if(random(count) < k) {
// 每个元素都有 1/K 的概率被淘汰
int pos = random(k);
data_list[pos] = data;
}
}
const vector<DataType>& getDataList() const {
return data_list;
}
};
来源:https://mp.weixin.qq.com/s/dxZc9opSInsirjFK5L2qMg
猜你喜欢
- 2024-11-16 Java编程从零开始07 数组的基本算法(查找和排序)
- 2024-11-16 Java练习:一个猜数游戏(java猜数游戏程序)
- 2024-11-16 Java中生成唯一ID的方法(java 生成id)
- 2024-11-16 617、java类,对象,集合的介绍(java中对象是什么)
- 2024-11-16 Java实现7种常见密码算法(java密码加密哪种方式最安全)
- 2024-11-16 JAVA入门:零基础实现幸运抽奖功能
- 2024-11-16 Hive 自定UDF函数,生成 32 位随机数
- 2024-11-16 四十三、Java常用类-Random类:深入理解与实践指南
- 2024-11-16 在程序中用的随机数,足够随机吗?
- 2024-11-16 用java实现跳表SkipList(跳表 java实现)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)