随机选取算法 (有权重的记录中选取)
2016-01-08 17:21
429 查看
三类随机问题
1. 已有n条记录,从中选取m条记录,选取出来的记录前后顺序不管。
实现思路:按行遍历所有记录,约隔n/m条取一个数据即可
2. 在1类情况下,还要求选取出来的m条记录是随机排序的
实现思路: 给n条记录,分别增加一列标记,值为随机选取的1至n之间的不重复数据,
实现参考博文 将文件内容按行随机排列
3. 区别于1,2类问题, 如果记录是有权重的,如何结合权重去随机选取。 比如A的权重为10, B的权重股为5, C的权重为1, 则随机选取4个时可能应该出现AABB。
这第三类问题是本文重点,下面开始解决。
实现思路: 以 A:10, B:5, C:1 三条记录上随机选取4条为例,(是否以权重排序这个无所谓)
对于
A 10
B 5
C 1
首先,将第n行的数值赋为第n行加第n-1行的,递归执行,如下:
A 10
B 15
C 16
然后每次从[1,16]随机选取一个数,如果落在[1,10]之间,则选取A,如果落在(10,15]之间则选B,如果落在(16,16]之间则选取C, 图示如下,谁占的区间大(权重高),被选上的概率更大。
![](http://my.csdn.net/uploads/201205/25/1337936317_6357.jpg)
知道了思路,实现起来就比较方便了, 需要考虑的一点可能就是我随即选了一个数值,比如12,我怎么跟B对应上? 其实也比较简单,用二分法查找即可。
下面附上实现代码:
[cpp] view
plaincopy
#include <string>
#include <cstdlib>
#include <vector>
using namespace std;
const int LEN = 4098;
const int MAX_QUERY_LEN = 2048;
//返回属于[p,q)的随机数
int rand(int p, int q)
{
int size = q-p+1;
return p+ rand()%size;
}
//删除行尾换行符
int chomp(char *str)
{
int len = strlen(str);
while(len > 0 && (str[len - 1] == '\n' || str[len - 1] == '\r'))
{
str[len - 1] = 0;
len--;
}
return len;
}
//获取一个随机数会落在哪个区间
int get_pos(vector<int> vec_freq, int begin, int end, int rand_num)
{
if(begin >= end)
{
return begin;
}
int mid = (begin + end)/2;
if( vec_freq[mid] >= rand_num )
{
return get_pos(vec_freq, begin, mid, rand_num );
}
else
{
return get_pos(vec_freq, mid+1, end, rand_num );
}
}
//主函数
int main(int argc, char *argv[])
{
//输入记录文件,两列,第一列为记录,第二列为热度值
FILE* infile = fopen(argv[1], "r");
if( infile == NULL )
{
printf("Cann't open file %s.", argv[1]);
return -1;
}
FILE* outfile = fopen(argv[2], "w");
if( outfile == NULL)
{
printf("Cann't open file %s to write.", argv[2]);
return -1;
}
//要获取的随机记录个数
int num = atoi(argv[3]);
if( num <= 0)
{
printf("num [%s] <= 0.");
return -1;
}
//这两个数组用下标关联
vector<string> vec_query;
vector<int> vec_freq;
vec_query.clear();
vec_freq.clear();
int freq = 0;
char line[MAX_QUERY_LEN]={0};
while( !feof(infile) )
{
if( !fgets(line, sizeof(line),infile))
{
break;
}
line[sizeof(line)-1] = 0;
chomp(line);
char* p_tab = strchr(line, '\t');
if( NULL == p_tab )
{
printf("line format error. [%s]\n");
continue;
}
*p_tab = 0;
string query(line);
freq += atoi(p_tab+1);
vec_query.push_back(query);
vec_freq.push_back(freq);
//printf("%s\t%d\n", line, freq);
}
for(int i=0; i < num; ++i)
{
int rand_num = rand(1, freq+1);
int pos = get_pos(vec_freq, 0, vec_freq.size(), rand_num);
fprintf(outfile, "%s\t%d\t%d\n", vec_query[pos].c_str(), vec_freq[pos], rand_num);
}
fclose(infile);
fclose(outfile);
return 0;
}
1. 已有n条记录,从中选取m条记录,选取出来的记录前后顺序不管。
实现思路:按行遍历所有记录,约隔n/m条取一个数据即可
2. 在1类情况下,还要求选取出来的m条记录是随机排序的
实现思路: 给n条记录,分别增加一列标记,值为随机选取的1至n之间的不重复数据,
实现参考博文 将文件内容按行随机排列
3. 区别于1,2类问题, 如果记录是有权重的,如何结合权重去随机选取。 比如A的权重为10, B的权重股为5, C的权重为1, 则随机选取4个时可能应该出现AABB。
这第三类问题是本文重点,下面开始解决。
实现思路: 以 A:10, B:5, C:1 三条记录上随机选取4条为例,(是否以权重排序这个无所谓)
对于
A 10
B 5
C 1
首先,将第n行的数值赋为第n行加第n-1行的,递归执行,如下:
A 10
B 15
C 16
然后每次从[1,16]随机选取一个数,如果落在[1,10]之间,则选取A,如果落在(10,15]之间则选B,如果落在(16,16]之间则选取C, 图示如下,谁占的区间大(权重高),被选上的概率更大。
![](http://my.csdn.net/uploads/201205/25/1337936317_6357.jpg)
知道了思路,实现起来就比较方便了, 需要考虑的一点可能就是我随即选了一个数值,比如12,我怎么跟B对应上? 其实也比较简单,用二分法查找即可。
下面附上实现代码:
[cpp] view
plaincopy
#include <string>
#include <cstdlib>
#include <vector>
using namespace std;
const int LEN = 4098;
const int MAX_QUERY_LEN = 2048;
//返回属于[p,q)的随机数
int rand(int p, int q)
{
int size = q-p+1;
return p+ rand()%size;
}
//删除行尾换行符
int chomp(char *str)
{
int len = strlen(str);
while(len > 0 && (str[len - 1] == '\n' || str[len - 1] == '\r'))
{
str[len - 1] = 0;
len--;
}
return len;
}
//获取一个随机数会落在哪个区间
int get_pos(vector<int> vec_freq, int begin, int end, int rand_num)
{
if(begin >= end)
{
return begin;
}
int mid = (begin + end)/2;
if( vec_freq[mid] >= rand_num )
{
return get_pos(vec_freq, begin, mid, rand_num );
}
else
{
return get_pos(vec_freq, mid+1, end, rand_num );
}
}
//主函数
int main(int argc, char *argv[])
{
//输入记录文件,两列,第一列为记录,第二列为热度值
FILE* infile = fopen(argv[1], "r");
if( infile == NULL )
{
printf("Cann't open file %s.", argv[1]);
return -1;
}
FILE* outfile = fopen(argv[2], "w");
if( outfile == NULL)
{
printf("Cann't open file %s to write.", argv[2]);
return -1;
}
//要获取的随机记录个数
int num = atoi(argv[3]);
if( num <= 0)
{
printf("num [%s] <= 0.");
return -1;
}
//这两个数组用下标关联
vector<string> vec_query;
vector<int> vec_freq;
vec_query.clear();
vec_freq.clear();
int freq = 0;
char line[MAX_QUERY_LEN]={0};
while( !feof(infile) )
{
if( !fgets(line, sizeof(line),infile))
{
break;
}
line[sizeof(line)-1] = 0;
chomp(line);
char* p_tab = strchr(line, '\t');
if( NULL == p_tab )
{
printf("line format error. [%s]\n");
continue;
}
*p_tab = 0;
string query(line);
freq += atoi(p_tab+1);
vec_query.push_back(query);
vec_freq.push_back(freq);
//printf("%s\t%d\n", line, freq);
}
for(int i=0; i < num; ++i)
{
int rand_num = rand(1, freq+1);
int pos = get_pos(vec_freq, 0, vec_freq.size(), rand_num);
fprintf(outfile, "%s\t%d\t%d\n", vec_query[pos].c_str(), vec_freq[pos], rand_num);
}
fclose(infile);
fclose(outfile);
return 0;
}
相关文章推荐
- Android学习笔记--服务(Service)
- iOS开发----Xcode7升级之后插件无法使用与不小心点击Skipbundle的解决办法
- 点击图片 , 获取图片上对应的坐标
- 课程设计--学生成绩管理系统
- php实现rar文件的读取和解压
- 好的博客
- Ubuntu 搜狗输入法 一直全角问题
- 【Visual C++】游戏开发笔记十七 游戏基础算法(一) 游戏随机系统初步
- Debug Java applications remotely with Eclipse
- 去除HTML标签
- AsyncTask多线程
- 给聊天记录加上日志
- viewpager和fragment实现预加载和fragment的单一加载
- 【SSH进阶之路】Hibernate映射——一对一单向关联映射(五)
- Zoho People-公式让人事工作更简单
- 对Ansible二次开发来检查服务器的Java程序占用端口数量
- 网络请求库Volley详解
- 百度地图开发问题随手记
- datagrid 删除要选择的项
- android TextView图文混排