前言 蓄水池抽样原理
问题抽象为:从n个数中随机采样k个,每个数被采样的概率是k/n。
思路一:
将n个数按顺序编号1,2,3,...,k,k+1,k+2,...k+n-k
选前k个数放到蓄水池里,
对于i = 1...n-k
按k/(k+i)的概率采样第k+i个数,然后随机跟蓄水池里的一个数替换。
思路二:
主要思想就是保持一个集合(这个集合中的每个数字出现),作为蓄水池,依次遍历所有数据的时候以一定概率替换这个蓄水池中的数字。
其伪代码如下:
Init : a reservoir with the size: k
for i= k+1 to N
M=random(1, i);
if( M <= k)
SWAP the Mth value and ith value
end for
解释一下:程序的开始就是把前k个元素都放到水库中,然后对之后的第i个元素,以k/i的概率替换掉这个水库中的某一个元素,所以每个元素被替换的概率是1/i
————————————————
原文链接:https://blog.youkuaiyun.com/sharelearner/article/details/10917075
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-random-node
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。重点补充:随机数没通过的同学们,前面加几个空跑的rand试试,让程序的随机数序列与评测器一致。
首先是我的代码:
这里采用了两种思路。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
#include <cstdlib> //rand() 库
class Solution {
public:
ListNode* root;
Solution(ListNode* head) {
root=head;
}
/** Returns a random node's value. */
int getRandom() {
if(!root) return 0;
int val = root->val;
int itr=2;
ListNode* p = root;
// 思路一的实现
int val = root->val;
ListNode* p = root;
// ListNode* res = root;
int iter=2;
while(p->next){
p=p->next;
double rd=1.0/iter;
double rr =rand()%100/100.0;
if(rd-rr>=0.000001)
//res=p;
val = p->val;
iter++;
}
//val = res->val;
// 思路一的实现
/*****
//思路二的实现
while(p->next){
p=p->next;
int i=rand()%itr;
if(i==0) val=p->val;
itr++;
}
//思路二的实现
******/
return val;
}
};
其中思路一结果:

思路二结果:

下面是来自网友的实现:
#include <cstdlib>
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
private:
ListNode *list;
public:
/** @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): list(head) {
}
/** Returns a random node's value. */
int getRandom() {
ListNode *p = list->next;
int val = list->val;
int n = 2;
// 这4个rand很关键
rand();
rand();
rand();
rand();
// 4个rand让该程序的随机数序列与题目一致,别问我怎么知道的
while(p) {
if ((rand()%n)==0) {
val = p->val;
}
p = p->next;
n++;
}
return val;
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(head);
* int param_1 = obj->getRandom();
*/
作者:armstrongcn
链接:https://leetcode-cn.com/problems/linked-list-random-node/solution/xu-shui-chi-chou-yang-sui-ji-shu-mei-tong-guo-de-y/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网友二:
//蓄水池抽样,感觉更像流水线抽样,不知道多少个不知道什么时候结束,参考https://www.cnblogs.com/snowInPluto/p/5996269.html
//要求:1.不知道链表长度,常数空间复杂度,就是不能把链表存起来然后遍历计算len,所以没办法知道len
//如果可以使用的话,直接选择rand()/len就行了
//2. 等概率,证明请看以上链接
ListNode* nod;
Solution(ListNode* head) {
this->nod = head;
}
/** Returns a random node's value. */
int getRandom() {
if(nod==NULL)
return 0;
int res = nod->val;//第一步选中1个数
ListNode* cur=nod;
cout<<rand()%2<<" "<<endl;
int ind=1;//当前位置
while(cur->next!=NULL){
cur=cur->next;
if((rand()%(ind+1))==ind)
res=cur->val;
ind++;
}
return res;
}
作者:shou-zai-jian-pan
链接:https://leetcode-cn.com/problems/linked-list-random-node/solution/liu-shui-xian-chou-yang-wei-zhi-chang-du-qie-gai-l/
来源:力扣(LeetCode)
本文介绍了蓄水池抽样算法,用于从n个数中以k/n的概率随机选取k个数。算法包括两种思路:一是预先选择前k个数,二是遍历过程中以概率替换蓄水池中的数。此外,文章还讨论了LeetCode的链表随机节点问题,要求保证每个节点被选概率相等,并提供了不同实现方案。
2199

被折叠的 条评论
为什么被折叠?



