382. Linked List Random Node 蓄水池抽样法
2017-09-08 04:44
513 查看
382. Linked List Random Node
题目链接题意
给你一个(很长)的链表,你需要从中随机地取出一个结点的值,并且保证取每个结点的概率是相同的。解法
蓄水池抽样法(resevoir sampling)的特例。在这种情况下,蓄水池抽样法总是选择第一个对象,以1/2的概率选择第二个,以1/3的概率选择第三个,以此类推,以1/m的概率选择第m个对象。当该过程结束时,每一个对象具有相同的选中概率,即1/n,证明如下。
证明:第m个对象最终被选中的概率P=选择m的概率*其后面所有对象不被选择的概率,即
P=1m×(mm+1×m+1m+2×…n−1n)=1n
代码
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { 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) { this->head = head; } /** Returns a random node's value. */ int getRandom() { int ans = head->val, i = 2; ListNode* cur = head->next; while(cur) { int j = rand() % i; if(!j) ans = cur->val; i++; cur = cur->next; } return ans; } private: ListNode* head = nullptr; }; /** * Your Solution object will be instantiated and called as such: * Solution obj = new Solution(head); * int param_1 = obj.getRandom(); */
我还看到了一种解法
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { 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):cur(head) { } /** Returns a random node's value. */ int getRandom() { int val=cur->val; ListNode *temp=cur; for(int i=0;temp!=nullptr;temp=temp->next,++i) { uniform_int_distribution<unsigned> u(0,i); default_random_engine e(rand());//真正随机的种子 unsigned int m=u(e); if(m<1) { val=temp->val; } } return val; } private: ListNode *cur; }; /** * Your Solution object will be instantiated and called as such: * Solution obj = new Solution(head); * int param_1 = obj.getRandom(); */
蓄水池抽样法
真正的蓄水池抽样问题是,即从一个包含n个对象的列表S中随机选取k个对象,n为一个非常大或者不知道的值。通常情况下,n是一个非常大的值,大到无法一次性把所有列表S中的对象都放到内存中。我们先把读到的前k个对象放入“水库”,对于第k+1个对象开始,以k(k+1)的概率选择该对象,以k(k+2)的概率选择第k+2个对象,以此类推,以km的概率选择第m个对象(m>k)。如果m被选中,则随机替换水库中的一个对象。最终每个对象被选中的概率均为kn,证明如下。
证明:第m个对象被选中的概率=选择m的概率(其后元素不被选择的概率+其后元素被选择的概率不替换第m个对象的概率),即
P=km×[(m+1−km+1+km+1×k−1k)×(m+2−km+2+km+2×k−1k×⋯×(n−kn+kn×k−1k)]=km×mn=kn
#include <iostream> #include <cstdlib> #include <ctime> #include <vector> using namespace std; typedef vector<int> vi; typedef typename vi::iterator it; typedef typename vi::const_iterator cit; // generate a random number between i and k, // both i and k are inclusive. int randint(int i, int k) { if(i > k) { int t = i; i = k; k = t; // swap } int ret = i + rand() % (k - i + 1); return ret; } // take m samples to result from input of n items. bool reservoir_sampling(const vi &input, vi &result, int m) { srand(time(NULL)); if(input.size() < m) return false; result.resize(m); cit it = input.begin(); for(int i = 0; i != m; i++) result[i] = *it++; for(int i = m; it != input.end(); i++, it++) { int j = randint(0, i); if (j < m) result[j] = *it; } return true; } int main() { const int n = 100; const int m = 10; vi input(n), result(m); for(int i = 0; i < n; i++) input[i] = i; if(reservoir_sampling(input, result, m)) for (int i = 0; i < m; ++i) cout << result[i] << " "; cout << endl; return 0; }
相关文章推荐
- 蓄水池抽样
- Reservoir Sampling 蓄水池抽样 海量数据不知道总数只能遍历一次随机抽样问题
- 随机抽样——蓄水池抽样算法(Reservoir Sampling)
- Reservoir Sampling - 蓄水池抽样
- 蓄水池抽样-Reservoir Sampling
- Reservoir Sampling - 蓄水池抽样
- 蓄水池抽样-Random Pick Index
- Reservoir Sampling - 蓄水池抽样
- 蓄水池抽样
- 蓄水池抽样
- 蓄水池抽样 海量数据不知道总数只能遍历一次随机抽样问题
- 蓄水池抽样问题(随机抽样问题)
- 面试题 从很长的数据流等概率随机采样 蓄水池抽样 Reservoir Sampling
- 面试题80:海量数据等概论抽样(蓄水池问题)
- Reservoir Sampling - 蓄水池抽样
- 大数据算法MOOC笔记3:水库抽样Reservoir Sampling(蓄水池问题)
- 数据工程师必知算法:蓄水池抽样
- 蓄水池抽样算法 (Reservoir Sampling Algorithm)
- 有关蓄水池抽样(Reservoir Sampling)
- 蓄水池抽样