问题: 在给定的但未知其大小的数据集中,随机等概率的抽取一个元素。
分析: 如果知道数据集的大小,可以直接rand() % len得到一个确切的位置。
所以未知长度时,即可使用蓄水池采样。
算法思路: 总是选择第一个对象,以的概率选中第二个数据,依次类推... 以的概率选中第m个数据。此时可以保证被选中的概率为。
第m个被选中,那么就是说后面的都不被选中(第m个数据不被选中,前m - 1个随机选中一个)
证明如下:
以上是两个典型的问题(来源: leetcode):
问题1(leetcode 382):
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。进阶:如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//又是抽样问题???
ListNode* H;
/** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
Solution(ListNode* head){
H = head;
}
/** Returns a random node's value. */
int getRandom() { //随机返回一个数
ListNode* p = H->next;
int i = 1, res = H->val; //i 从第一个数开始
while(p){
int r = rand() % (i + 1);
//这一步是为什么? 求解r == 0 即记录结果 总是先选择第一个数
if(r < 1) res = p->val;
p = p->next;
i ++;
}
return res;
}
};
问题2(leetcode 398):
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。注意:数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。
class Solution {
public:
//随机+“大数组” --> 蓄水池采样
vector<int> v; //定义二维map 以及怎么使用
Solution(vector<int>& nums) {
v = nums;
}
int pick(int target) {
int res = 0, cnt = 0; //统计个数
for(int i = 0; i < v.size(); i ++){ //随机给出其目标值的下标
if(v[i] == target){
cnt ++; //统计目标值的个数
if(rand() % cnt == 0) res = i;
}
}
return res;
}
};
拓展: 如何从大数据中随机抽取k个数字。
若是将前k个数字放进“水库”中去,对于第k+1个数据元素,以的概率选取它,以的概率选取第k+2个数据元素...
依次类推,若第m个数据元素被选中 (说明:此时不被选中的概率为 = ,则随机替换水库中的一个对象。最终每个对象被选中的概率为k/n。
证明如下: 第m个数据元素被选中 = 选择m的概率 * (其后元素不被选择的概率 + 其后数据元素被选中的概率*不替换第m个的概率)
以上就是今天所有的内容,欢迎各位给出意见,谢谢!