1. 程式人生 > >資料流基本問題--獨立元素計數(一)

資料流基本問題--獨立元素計數(一)

下面我們討論如何計算獨立元素數目。

一、問題定義

如果一個數據流,其中m為資料流的大小,。我們可以定義每個元素

出現的次數,其中為第i個元素出現的次數。假設,容易得知d為在

資料流中出現的不同元素數目,也就是獨立元素數目。

對於這個問題,可以在記憶體中使用高效的搜尋結構(比如平衡BST等)保留當前已經出現的元素。但是如果元素數目實在太

多以致搜尋結構無法訪問記憶體時,我們可以使用更多的機器或者將資料結構的一部分放入到外存中。

上述做法是計算流中獨立元素的精確解。如果我們僅僅需要對獨立元素數目進行估計,則方法要簡單的多,空間消耗也很少

(一般確定性演算法空間複雜度需要)。

二、具體演算法

通過將流中元素雜湊到一個足夠長的位串,就可以實現獨立元素數目的估計。這裡要求雜湊函式屬於2-universal hash family。要求位串必須要足夠長,以致雜湊函式的可能結果數目要遠大於流中獨立元素個數。如果在流中看到的不同元素越多,我們看到的不同雜湊值也就越多。對於一個元素雜湊後的結果p,我們定義zero(p)為p的二進位制表示尾部中連續0的個數。也就是如下定義:


如果我們記錄流中所有元素zero()的最大值設為z,從直觀上理解,如果流中獨立元素數目越多,那麼z的取值就會越大。演算法的基本思想就是如果我們從d個不同的元素中希望有一個使得。舉例來說,如果流中有8個獨立元素,我們希望其中有一個滿足雜湊後的結果尾部有3個0。所以zero(h(j))的最大值(也就是下面演算法中的z)理論上應該是log d的一個較好的近似。

基於上述想法,演算法的步驟如下:

int get_distinct_elements_num(vector<int>&nums) {
	z=0;
	//h(i)為雜湊函式
	//zero(i)是求i二進位制表示尾部中連續0的數目
	for(int i=0;i<nums.size();i++){
		if(zero(h(nums[j]))>z){
			z=zero(h(nums[j]));
		}
	}
	return 2<<(z+1.0/2);
}
三、演算法的評估

假設為一個取值0或1的量,表示,t表示第二節中演算法執行結束後z的取值。很明顯,我們有:

或者

因為h(j)是取值是隨機的,所以:


由於之間獨立,我們得到的期望和方差:

(Var(x)表示x的方差,該步驟利用了)      

分別由馬爾科夫不等式和切比雪夫不等式可得:



是演算法對d的估計,有。設a是滿足的整數,b是滿足的最大整數。則有


通過上面兩個式子,可以發現,只是d的同階的估計,並不是一個任意好的估計。另外,過大或者過小的概率並不是很大,只有

四、Median trick

所謂median trick就是我們執行這個演算法k次,取k次結果的中位數即可。通過切爾諾夫界可以證明,median trick可以使

過大或者過小的概率降低到足夠低。直觀上理解,取中位數的話就不會受到偶然極大值或者極小值的影響,從而是一個更好的估計。在後續的部落格也會多次提及median trick。

五、附相關不等式。

1.馬爾科夫不等式


2.切比雪夫不等式