小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

快速URL排重的方法

 Ralf_Jones 2008-10-31
http://blog.csdn.net/oyd/archive/2007/07/19/1699237.aspx
我這里介紹一個極適合大量URL快速排重的方法 ,這個算法被稱為Bloom filter,基本上,它也只適合這樣的場合。

這里的大量是指有5000萬至1億的URL,更大的數(shù)據(jù)量可能也不合適了。

一開始我使用了一個最復(fù)雜的做法,是有一個單獨的daemon程序負責排重,數(shù)據(jù)和排重結(jié)果通過socket傳輸。
后來發(fā)現(xiàn)不行,僅僅幾百萬數(shù)據(jù)要做好幾個小時,5000萬不把人都急瘋了?至于daemon中具體用什么算法就次要了,因為一涉及到網(wǎng)絡(luò)通訊,速度再快也被拉下來(這里針對的是發(fā)送一條記錄/返回一條結(jié)果的模式,一次傳送一批數(shù)據(jù)則與網(wǎng)絡(luò)狀況有關(guān)了)

所以,把目標鎖定在單機排重,一開始,試驗了perl中的hash,非常簡單的代碼

use DB_File;
my %db;
#tie %db, ‘DB_File‘, "createdb.dat", or die "Can‘t initialize db:: $! ";
while(<>) {
        
chomp $_;
        
$db{$_= 1;# add code here
}
#untie %db;

從標準輸入或文件中每行一個URL讀入,插入到perl內(nèi)置的hash表中,這就成了,需要輸出結(jié)果則預(yù)先判斷一下插入的key是否存在。

這個方法速度很快,可惜的是,它占用內(nèi)存太大,假設(shè)1個URL平均50字節(jié),5000萬個URL需要2.5G內(nèi)存。

于是又想到一個方法,把部分數(shù)據(jù)放入硬盤空間,perl中也提供一個現(xiàn)成的模塊DB_File,把上面代碼中的注釋去掉,就可使用DB_File了,用法與hash一樣,只是內(nèi)部用數(shù)據(jù)庫實現(xiàn)的。

測試了一下,速度明顯下降了一個檔次,僅40萬的數(shù)據(jù)就要1分鐘,關(guān)鍵還在于隨著數(shù)據(jù)量的增加,速度下降加快,兩者不呈線性關(guān)系。

數(shù)據(jù)量大的時候,有可能用MySQL的性能會比DB_File好,但是總體上應(yīng)該是一丘之貉,我已經(jīng)不抱期望了。

也許DB_File可以優(yōu)化一下,使用更多的內(nèi)存和少量的硬盤空間,不過這個方案還是太復(fù)雜,留給專家解決吧,一般來說,我認為簡單的方法才有可能做到高效。

下面我們的重點對象隆重登場:Bloom filter。簡單的說是這樣一種方法:在內(nèi)存中開辟一塊區(qū)域,對其中所有位置0,然后對數(shù)據(jù)做10種不同的hash,每個hash值對內(nèi)存bit數(shù)求模,求模得到的數(shù)在內(nèi)存對應(yīng)的位上置1。置位之前會先判斷是否已經(jīng)置位,每次插入一個URL,只有當全部10個位都已經(jīng)置1了才認為是重復(fù)的。

如果對上面這段話不太理解,可以換個簡單的比喻:有10個桶,一個桶只能容納1個球,每次往這些桶中扔兩個球,如果兩個桶都已經(jīng)有球,才認為是重復(fù),問問為了不重復(fù)總共能扔多少次球?

10次,是這個答案吧?每次扔1個球的話,也是10次。表面上看一次扔幾個球沒有區(qū)別,事實上一次兩個球的情況下,重復(fù)概率比一次一個球要低。Bloom filter算法正式借助這一點,僅僅用少量的空間就可以進行大量URL的排重,并且使誤判率極低。

有人宣稱為每個URL分配兩個字節(jié)就可以達到0沖突,我比較保守,為每個URL分配了4個字節(jié),對于5000萬的數(shù)量級,它只占用了100多M的空間,并且排重速度超快,一遍下來不到兩分鐘,極大得滿足了我的欲望。
 

接上篇,起初我為了輸入輸出方便,是用perl去實現(xiàn)的,后來發(fā)現(xiàn)perl中求模速度太慢,就改用C了 

常量定義:SPACE指你要分配多大的內(nèi)存空間,我這里是為5000萬數(shù)據(jù)的每一條分配4字節(jié)

const int SPACE = 50000000 * 4;
const int MAXNUM = SPACE * 8;
#define LINE_MAX 2048
int bits[] = {0x10x20x40x8163264128};
char* db = NULL;

主程序:這里循環(huán)讀入標準輸入的每一行,進行排重。

int main(int argc, char* argv[])
{
        db 
= new char[SPACE];
        memset(db, 
0, SPACE);
        
char line[LINE_MAX];
        
while (fgets(line, LINE_MAX, stdin) != NULL) {
                
int len = strlen(line);
                len
--;
                
if (len <= 0continue;
                
if (!is_exist(line, len)) {
                        
// add code here
                }
        }
        
return 0;
}

判定函數(shù):我沒有做Bloom filter算法中描述的10次hash,而是做了一個MD5,一個SHA1,然后折合成9次hash。

bool is_exist(const char* str, int len) {
        unsigned 
int hashs[9];
        unsigned 
char buf[20];
        MD5(str, len, buf);
        memcpy(hashs, buf, 
16);
        SHA1(str, len, buf);
        memcpy(hashs 
+ 4, buf, 20);
        
int k = 0;
        
for (int j = 0; j < sizeof(hashs) / sizeof(hashs[0]); j++) {
                
int bitnum = hashs[j] % MAXNUM;
                
int d = bitnum / 8;
                
int b = bitnum % 8;
                
char byte = db[d];
                
if (byte & bits[b] == bits[b]) {
                } 
else {
                        
byte |= bits[b];
                        db[d] 
= byte;
                        k
++;
                }
        }
        
return (k == 0);
}

 

主要算法就在這里了,實際應(yīng)用的話可以采用循環(huán)監(jiān)視磁盤文件的方法來讀入排重數(shù)據(jù),那些代碼就與操作系統(tǒng)相關(guān),沒必要在這寫了
 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多