背景:
我们的推荐服务里面有这么一个需求, 给一些帖子计算其得分, 然后随机从这些帖子里面取出几个展现给用户, 而且每个帖子被取出的概率和帖子的得分成正比
模型:
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};对比
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