[读书笔记]随机取样

1. 随机样本的产生:

       给定两个整数m和n,其中m < n,要求从0 ~ n - 1范围内随机选取m个数并按序输出, 网络上流传的很多技术类题目中都有这个问题的身影,《编程珠玑》更是专门用一个章节(第12章)来阐述取样问题;书中首先提及的是Knuth的半数值算法的解决方案(算法S),但我至今还没有一个透彻的理解。

       一个比较明了的解法则是利用C++的标准模板库,利用类模板set的特性,可以这样来实现:

 

void gensets(const int m,const int n)
{
	set<int> iset;

	srand((unsigned)time(0)); 
	while(iset.size() < m)
	{
		iset.insert(bigrand() % n);
	}

	set<int>::iterator it;
	for(it = iset.begin();it != iset.end();++it)
	{
		cout << *it << '\t';
	}
}

 

       其中的bigrand()函数的功能是一个产生范围在[0,RAND_MAX)的随机数

 

int bigrand(void)
{
	return RAND_MAX * rand() + rand();
}


       另外,Ashley Shepherd和Alex Woronow发现,在这个问题中我们只需要打乱数组的前m个元素,然后再取前m个元素,就可以达到随机性。然而,当m接近于n时,基于集合的算法生成的很多随机数都要丢掉,因为它们之前已存在于集合中了!为此,R. W. Floyd提出了另外一个具体的方案:

 

void genfloyd(const int m,const int n)
{
	set<int> iset;

	srand((unsigned)time(0)); 
	for(int i = n - m;i < n;++i)
	{
		int t = bigrand() % (i + 1);
		if(iset.end() == iset.find(t))
		{
			iset.insert(t);
		}
		else
		{
			iset.insert(i);
		}
	}

	set<int>::iterator it;
	for(it = iset.begin();it != iset.end();++it)
	{
		cout << *it << '\t';
	}
}

 

       再有一个问题:如何在事先不知道文本文件行数(n)的情况下读取该文件并随机输出一行?

       现有的解答是:我们以概率1/i来选取第i行,即:总是选择第一行,然后,如果后面还有第2行,那就以1/2的概率用第2行去覆盖之前的选择,如果还有第3行,则以1/3的概率用第3行去覆盖在这之前的选择……如此下去,直到读完文件的所有行。

       以上的解答被人称作蓄水池抽样模型,对应的问题要求我们以概率1/n来选取每一行(当然,可以扩展到m行,然后以概率m/n来选定每一行)。

       对于第i行来说,前面的i – 1行中的已被正确地以1/(i- 1)的概率选定了某一行,我们以1/i的概率用i去置换已选定了那一行,然后第i行获得了正确的被选定概率,之前已被选定的那一行还能被选定的概率就等于1/(i – 1) * (i – 1)/i = 1/i ,从而,这个方案的正确性得到证明。

 

2. 分析模式的随机性

       如何测试一个生成样本的程序,以确保其输出确实是随机的?这个问题同样出自《编程珠玑》,来自微软MSDN的技术讨论文档提出了如下的建议:

============================ 我是分隔线 =================================

       有几个统计方法可用于实现此目的,但最简单的是 Wald-Wolfowitz 检验。该检验有时称为单样本游程检验(其中,“游程”为一连串相同数字或字符)。Wald-Wolfowitz 检验适用于由两个符号组成的序列,例如:

A B B A A A B BB A B B

       其中的原理是,对于模式中指定数量的每个符号类型(共两个符号类型),如果符号为随机生成(在本例中,意味着模式中每个位置出现 A 或 B 的几率各为 50%),则您可计算该模式中的预期游程数。模式游程是一个由相同符号类型组成的序列。因此,在刚才所示的模式中,共有六个游程:A、BB、AAA、BBB、A、BB。如果模式中的实际游程数过大或过小,则证明该模式不是随机生成。

 

       尽管Wald-Wolfowitz 检验仅适用于只包含两种符号类型的模式,但您通常可以将任意模式映射为 Wald-Wolfowitz 形式。例如,如果您有一个 {7, 9, 3, 4, 1, 6} 之类的整数序列,则可以算出其平均值为 (5.0),然后将该序列映射到符号 H 和 L,其中 H 代表大于该平均值的数,而 L 代表小于该平均值的数:H H L L L H。如果您需要分析更复杂的模式,则可以使用 Chi-Square 检验或 Kolmogorov 检验之类的一些检验方法。

============================== 分隔线 ==================================

       这就更深入地涉及到数学知识了,但我却缺乏这方面的储备,所以只能跟着这个这个建议去尝试着理解,到目前,对上面说的,还是一知半不解的状态。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值