隨著資訊的高速發展,越來越多的資料資訊等待處理,如何快速的從這些海量資料中找到你所需要的資料呢。這就是大資料的處理問題,下面我對幾個經典的大資料問題進行分析~~~~
一. 設計演算法找到每日訪問百度出現次數最多的IP地址。
分析:將所有的IP逐個寫入到一個大檔案中,因為當IP地址採用點分十進位的方式表示的時候是32位的,所以最多存在2^32個IP。可以採用映射的方式,比如模1000,將這個較大的檔案對應為1000個小檔案,再將每個小檔案載入到記憶體中找到每個小檔案中出現頻率最大的IP(可以使用hash_map的思想進行頻率統計);然後在這1000個最大的IP中找出那個出現頻率最大的IP,就是出現次數最多的IP了。
演算法思想如下:(分而治之+hash)
1).IP地址最多有2^32=4G個,所以不能直接將所有的IP地址載入到記憶體中
2).可以考慮採用"分而治之"的思想,就是將IP地址Hash(IP)%1024值,將海量IP分別儲存到1024個小檔案中,這樣每個小檔案最多包含(2^32)/(2^10)=4M個IP地址
3).對於每一個小檔案,可以構建一個IP值為key,出現次數為vaue的hash_map,通過value的比較找到每個檔案中出現次數最多的那個IP地址
4).經過上述步驟已經得到1024個出現次數最多的IP地址,再選擇一定的排序演算法找出這1024個IP中出現次數最多的那個IP地址
二.給兩個檔案,分別有100億個整數,我們只有1G記憶體,如何找到兩個檔案的交集。
分析:我們知道對於整形資料來說,不管是有符號的還是無符號的,總共有2^32=4G個資料(100億個資料中肯定存在重複的資料),我們可以採用位元影像的方式來解決,假如我們用一個位來代表一個整形資料,那仫4G個數共佔512M記憶體。我們的做法是將第一個檔案裡的資料對應到位元影像中,再拿第二個檔案中的資料和第一個檔案中的資料做對比,有相同的資料就是存在交集(重複的資料,交集中只會出現一次).
三.假定一個檔案有100億個整形資料,1G記憶體,如何找到出現次數不超過兩次的數字。
分析:要解決這個問題同樣需要用到位元影像的思想,在問題二中已經瞭解到採用位元影像的一個位可以判斷資料是否存在,那仫找到出現次數不超過兩次的數字使用一個位是無法解決的,在這裡可以考慮採用兩個位的位元影像來解決.
根據上述分析我們可以藉助兩個位,來表示數位存在狀態和存在次數,比如:00表示不存在,01表示存在一次,10表示存在兩次,11表示存在超過兩次;類似問題二的計算過程:如果一個數字佔一位,需要512M記憶體即可,但是如果一個數字佔兩位,則需要(2^32)/(2^2)=2^30=1G記憶體;將所有資料對應到位元影像中尋找不是11的所對應的數字就解決上述問題了。
題目擴充:其他條件不變,假如只給定512M記憶體該如何找到出現次數不超過兩次的數字。
分析:將資料分批處理,假若給定的是有符號數,則先解決正數,再解決負數,此時512M正好解決上述問題.
四.給兩個檔案,分別有100億個query,我們只有1G記憶體,如何找到兩檔案交集。分別給出精確演算法和近似演算法!
分析:看到字串首先應該反應過來的就是布隆過濾器,而問題四的近似演算法就是採用布隆過濾器的方法,之所以說布隆過濾器是近似的演算法,因為它存在一定 的誤判(不存在是肯定的,存在是不肯定的);而要想精確判斷字串檔案的交集,我們可以採用分而治之的方法:將大檔案切分為一個一個的小檔案,將一個又一個的小檔案拿到記憶體中做對比,找到對應的交集。
1.布隆過濾器的近似解決辦法:
根據不同的字串雜湊演算法,可以計算出不同的key值,然後進行映射,此時可以映射到不同的位置,只有當這幾個位全部為1的時候這個字串才有可能存在(因為當字串過多的時候可能映射出相同的位),只有一個位為0,那仫該串一定是不存在的,所以說布隆過濾器是一種近似的解決辦法。將第一個檔案對應到布隆過濾器中,然後拿第二個檔案中的每個串進行對比(計算出特定串的key,通過不同的雜湊演算法映射出不同的位,如果全為1則認為該串是兩個檔案的交集;如果有一位為0那仫該串一定不是交集).
2.雜湊切分的精確解決辦法:
既然叫做切分,顧名思義就是將大檔案切分為小檔案,那仫如何切分。切分的依據是什仫呢。如果我們在切分的時候可以將相似或者相同的檔案切分到同一個檔案中那仫是不是就加快了尋找交集的速度呢。答案是肯定的。
知道了雜湊切分的依據我們應該如何處理呢。我們可以根據字串的某個雜湊演算法得到該字串的key,然後將key模要分割的檔案數(假設為1000個檔案,檔案編號為0~999),我們將結果相同的字串放到同一個檔案中(兩個檔案中的字串通過相同的雜湊演算法就會被分到下標相同的檔案中),此時我們只需要將下標相同的檔案進行比對就可以了。。。
雜湊切分明顯比布隆過濾器的方法效率要高,時間複雜度為O(N).
具有刪除功能的BloomFilter:
struct __HashFunc1{size_t BKDRHash(const char *str) { register size_t hash = 0; while (size_t ch = (size_t)*str++) { hash = hash * 131 + ch;// 也可以乘以31、131、1313、13131、131313.. } return hash;} size_t operator()(const string& str){return BKDRHash(str.c_str());}};struct __HashFunc2{size_t SDBMHash(const char *str) { register size_t hash = 0; while (size_t ch = (size_t)*str++) { hash = 65599 * hash + ch; //hash = (size_t)ch + (hash << 6) + (hash << 16) - hash; } return hash; } size_t operator()(const string& str){return SDBMHash(str.c_str());}};struct __HashFunc3{size_t RSHash(const char *str) { register size_t hash = 0; size_t magic = 63689; while (size_t ch = (size_t)*str++) { hash = hash * magic + ch; magic *= 378551; } return hash; } size_t operator()(const string& str){return RSHash(str.c_str());}};struct __HashFunc4{size_t APHash(const char *str) { register size_t hash = 0; size_t ch; for (long i = 0; ch = (size_t)*str++; i++) { if ((i & 1) == 0) { hash ^= ((hash << 7) ^ ch ^ (hash >> 3)); } else { hash ^= (~((hash << 11) ^ ch ^ (hash >> 5))); } } return hash; } size_t operator()(const string& str){return APHash(str.c_str());}};struct __HashFunc5{size_t JSHash(const char *str) { if(!*str) // 這是由本人添加,以保證Null 字元串返回雜湊值0 return 0; register size_t hash = 1315423911; while (size_t ch = (size_t)*str++) { hash ^= ((hash << 5) + ch + (hash >> 2)); } return hash; } size_t operator()(const string& str){return JSHash(str.c_str());}};template<class K=string,class HashFunc1=__HashFunc1,class HashFunc2=__HashFunc2,class HashFunc3=__HashFunc3,class HashFunc4=__HashFunc4,class HashFunc5=__HashFunc5>class BloomFilter{public:BloomFilter(size_t num):_bitMap(num*5),_range(num*5){}void Set(const K& key){size_t hash1=HashFunc1()(key)%_range;size_t hash2=HashFunc2()(key)%_range;size_t hash3=HashFunc3()(key)%_range;size_t hash4=HashFunc4()(key)%_range;size_t hash5=HashFunc5()(key)%_range;_bitMap.Set(hash1);_bitMap.Set(hash2);_bitMap.Set(hash3);_bitMap.Set(hash4);_bitMap.Set(hash5);cout<<hash1<<endl;cout<<hash2<<endl;cout<<hash3<<endl;cout<<hash4<<endl;cout<<hash5<<endl<<endl;}//void Reset(const K& key);bool Test(const K& key){size_t hash1=HashFunc1()(key)%_range;if(_bitMap.Test(hash1) == false)return false;size_t hash2=HashFunc2()(key)%_range;if(_bitMap.Test(hash2) == false)return false;size_t hash3=HashFunc3()(key)%_range;if(_bitMap.Test(hash3) == false)return false;size_t hash4=HashFunc4()(key)%_range;if(_bitMap.Test(hash4) == false)return false;size_t hash5=HashFunc5()(key)%_range;if(_bitMap.Test(hash5) == false)return false;return true;}protected:BitMap _bitMap;size_t _range;};//具有刪除功能的布隆過濾器(引用計數)template<class K=string,class HashFunc1=__HashFunc1,class HashFunc2=__HashFunc2,class HashFunc3=__HashFunc3,class HashFunc4=__HashFunc4,class HashFunc5=__HashFunc5>class RefBoolFilter{public:RefBoolFilter(size_t num){_range=5*num;_refbitMap.resize(_range);}void Set(const K& key){size_t hash1=HashFunc1()(key)%_range;size_t hash2=HashFunc2()(key)%_range;size_t hash3=HashFunc3()(key)%_range;size_t hash4=HashFunc4()(key)%_range;size_t hash5=HashFunc5()(key)%_range;_refbitMap[hash1]++;_refbitMap[hash2]++;_refbitMap[hash3]++;_refbitMap[hash4]++;_refbitMap[hash5]++;}bool Reset(const K& key){size_t hash1=HashFunc1()(key)%_range;size_t hash2=HashFunc2()(key)%_range;size_t hash3=HashFunc3()(key)%_range;size_t hash4=HashFunc4()(key)%_range;size_t hash5=HashFunc5()(key)%_range;if (_refbitMap[hash1] == 0||_refbitMap[hash2] == 0||_refbitMap[hash3] == 0||_refbitMap[hash4] == 0||_refbitMap[hash5] == 0){return false;}_refbitMap[hash1]--;_refbitMap[hash2]--;_refbitMap[hash3]--;_refbitMap[hash4]--;_refbitMap[hash5]--;return true;}protected:vector<size_t> _refbitMap;size_t _range;};
在上面實現的布隆過濾器中引用了不同的雜湊演算法,有想研究雜湊演算法的的童鞋可參考下列連結:
http://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html
五.有一個詞典,包含N個英文單詞,現在任意給一個字串,設計演算法找出包含這個字串的所有英文單詞!
分析:字典樹,要找到滿足題意的單詞我們需要一層一層遍曆字典樹,在每層中找一次與給定單詞首字母相同的那個位置,進行遍曆尋找。
以上大資料問題的來源參考九章演算法第七講~~~~