蓄水池随机抽样算法

由于工作中经常需要随机抽取一些数据做人工评测,所以就实现了一个随机工具,使用的是蓄水池随机抽样算法。

问题描述

从一个很大的样本空间中随机选取出少量数据,并且每个数据被选取到的概率是一样的。

主要逻辑

假设样本空间为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来实现。

猜你喜欢

转载自blog.csdn.net/hbuxiaoshe/article/details/38657459