由于工作中经常需要随机抽取一些数据做人工评测,所以就实现了一个随机工具,使用的是蓄水池随机抽样算法。
问题描述:
从一个很大的样本空间中随机选取出少量数据,并且每个数据被选取到的概率是一样的。
主要逻辑:
假设样本空间为n,随机选取k个数据,k<=n。
1)先选取前k个数据(0,1,2,...k-1,角标从0开始)
2)对于第i个数据(k<=i<n),随机生成区间[0, i]的一个数r,如果r<k,则将第i个数据替换第r个数据。
证明:
证明来自http://www.cnblogs.com/HappyAngel/archive/2011/02/07/1949762.html
附上实现代码:
#include <stdio.h>
#include <unistd.h>
typedef struct _line {
char line[1024];
}Line;
int Help(char *arg) {
printf("use : \n");
printf(" %s -h\n", arg);
printf(" %s -v\n", arg);
printf(" %s -f file -n number, random [n] data from [f]\n", arg);
printf("\n");
return 0;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
return Help(argv[0]);
}
char *file = NULL;
int num = 0;
char ch;
opterr = 0;
while ((ch=getopt(argc, argv, "hvf:n:")) != -1) {
switch(ch) {
case 'h':
return Help(argv[0]);
case 'v':
return 0;
case 'f':
file = optarg;
break;
case 'n':
num = atoi(optarg);
break;
}
}
if (num == 0) {
return fprintf(stderr, "[error] num=%d\n", num);
}
Line *line = (Line *)malloc(num*sizeof(Line));
if (line == NULL) {
return fprintf(stderr, "[error] malloc %d kb\n", num);
}
int l = 0;
Line one;
FILE *in = NULL;
if (file == NULL) {
in = stdin;
} else {
in = fopen(file, "r");
if (in == NULL) {
return fprintf(stderr, "[error] fopen file '%s'\n", file);
}
}
srand(time(NULL));
while (fgets(one.line, 1024, in)) {
if (l < num) {
memcpy(&line[l], &one, sizeof(Line));
} else {
int rd = Rand(0, l);
if (rd < num) {
memcpy(&line[rd], &one, sizeof(Line));
}
}
l++;
}
int min = l>num ? num : l;
int i;
for (i=0; i<min; i++) {
printf("%s", line[i].line);
}
free(line);
if (file != NULL) {
fclose(in);
}
return 0;
}
编译后可以这样使用:./random -f f -n 10或cat f | ./random -n 10
当然这个也可以通过sort -R -n来实现。