由于疫情原因,软件行业也变得越来越不景气了,找工作的人越来越多。这不老板让告诉小王公司要招5名程序猿,才一上午的工夫就有100多份简历投来。老板告诉小王他只准备看20份简历,望着桌面上的简历和下午还不知道有多少的简历,小王有点头疼。该怎么样才能在保证公平的前提下随机选出20份简历呢?

问题分析

为了更好的帮助小王解决这个难题,我们把这个问题转化成一个数学问题。如何一串长度n未知的数据流中挑选出k个随机样本?这是一道经典的随机算法题。算法不知道总体n的大小,我们也无法回溯先前的数据。设想一下,如果我们知道流的大小n为N,则按照概率抽k/N抽取k个数据即可。那么现在n为止,抽取的概率也未知,该怎么办呢?

Reservoir sampling(R算法)

  1. 抽取前K个数据,如果n=K,则结束;

  2. 遇到第K+1数据,以神奇的R算法 - 图1的概率与K个数据中的一个替换;

代码的实现

  1. #include <iostream>
  2. #include <random>
  3. #include <vector>
  4. #include <random>
  5. template<class T>
  6. class Selector
  7. {
  8. public:
  9. Selector(int _k = 0) : k(_k), count(0) {}
  10. ~ Selector() {}
  11. void set(int _k) {
  12. this.k = _k;
  13. }
  14. void reset() {
  15. k = 0;
  16. count = 0;
  17. sampVec.clear();
  18. }
  19. void put(const T& _t) {
  20. ++count;
  21. if (count < k + 1) {
  22. sampVec.push_back(_t);
  23. } else {
  24. int random = rand() % count;
  25. if (random < k) {
  26. sampVec[random] = _t;
  27. }
  28. }
  29. }
  30. const std::vector<T>& getSample() const{
  31. return sampVec;
  32. }
  33. int getCount() const {
  34. return count;
  35. }
  36. int getK() const {
  37. return k;
  38. }
  39. private:
  40. std::vector<T> sampVec;
  41. int k;
  42. int count;
  43. };
  44. int main()
  45. {
  46. printf("请开始输入数字:\n");
  47. Selector<int> filter(3);
  48. int num = 0;
  49. srand(time(NULL));
  50. while (scanf("%d", &num) != EOF) {
  51. filter.put(num);
  52. printf("总数:%d, 抽取样本数:%d\n", filter.getCount(), filter.getK());
  53. printf("抽取样本集合[");
  54. for (const auto& i : filter.getSample()) {
  55. printf("%d ", i);
  56. }
  57. printf("]");
  58. }
  59. return 0;
  60. }

运行:输入[100-116], 结果如下:
0-0-1.png