[讀書筆記]隨機取樣

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 檢驗之類的一些檢驗方法。

============================== 分隔線 ==================================

       這就更深入地涉及到數學知識了,但我卻缺乏這方面的儲備,所以只能跟着這個這個建議去嘗試着理解,到目前,對上面說的,還是一知半不解的狀態。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章