与权值成正比的随机算法设计

背景:
我们的推荐服务里面有这么一个需求, 给一些帖子计算其得分, 然后随机从这些帖子里面取出几个展现给用户, 而且每个帖子被取出的概率和帖子的得分成正比

模型:
int score[5] = {15, 7, 36, 23, 19};
随机取出其中一个值, 该值被取到的概率和值的大小成正比. 该如何设计程序?

今天看到我们这块代码以前的同学是这么实现的:
vector<int> array;
第0个帖子是15分, 那么往array里面push进去15个0, 
第1个帖子是7分, 那么往array里面push进去7个1,
第2个帖子是36分, 那么往array里面push进去36个2,
... ...
最后在array数组里面是15个0,7个1,36个2,23个3,19个4, 最后在这个数组里面随机取, 结果就是根据得分的分布出来的了.

上面的算法, 在得分值如果是比较大的(比如1000), 那么vector要多大? 
这个算法的复杂度就是O(m), m是所有帖子的总得分. 这样的代码, 还会导致vector的反复重新分配内存, 性能不是很可取.

下面讲一下我的思路:
1) 按顺序对帖子得分做累加, sum[i] 等于 (score[0]+score[1]+...+score[i]), 比如 int sum[5] = {15,  15+7,  15+7+36,  15+7+36+23, ...};
2) 设max是所有帖子得分的总和, 在0~max的范围里获取一个随机数r
3) 取出lower_bound(sum, sum+5, r) 对应的下标, 就是本次取帖的下标

代码:
//score_rand.cc
#include <algorithm>
#include <stdio.h>
#include <vector>
#include <stdlib.h>
#include <ctime>
using namespace std;

int main () {   
  const int N = 5;  
  int score[N] = {15, 7, 36, 23, 19}; //和是100, 便于最后打印看统计分布

  //构造累加和数组
  vector<pair<int, int>> sum_idx;
  int sum = 0;
  for(size_t i = 0; i < N; ++i) {
    sum += score[i];
    sum_idx.push_back( make_pair(sum, i) );
  }

  //这个数组用于记录随机抽取的结果
  int res[N];
  for (int i = 0; i < N; ++i) res[i] = 0;

  //随机做M次抽取,统计结果
  const int M = 1000000;
  srand((int)time(0));
  for (int i = 0; i < M; ++i) {
    int r = rand() % sum;
    auto iter = std::lower_bound( sum_idx.begin(), sum_idx.end(), make_pair(r, sum) ); 
    res[iter->second]++;
  }

  //打印结果分布
  for (int i = 0; i < N; ++i) { 
    printf("%.2f%% ", (float)(res[i]*100)/M);
  }
}
g++ score_rand.cc -std=c++0x

某次执行的结果:
14.95% 6.97% 36.08% 23.01% 18.99%
和score[5] = {15, 7, 36, 23, 19};对比


转载请注明出处: http://blog.csdn.net/answer3y/article/details/21128519
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值