1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
<?php /*Bloom Filter算法來去重過濾。 介紹下Bloom Filter的基本處理思路:申請(qǐng)一批空間用于保存0 1信息,再根據(jù)一批哈希函數(shù)確定元素對(duì)應(yīng)的位置,如果每個(gè)哈希函數(shù)對(duì)應(yīng)位置的值為全部1,說明此元素存在。相反,如果為0,則要把對(duì)應(yīng)位置的值設(shè)置為1。由于不同的元素可能會(huì)有相同的哈希值,即同一個(gè)位置有可能保存了多個(gè)元素的信息,從而導(dǎo)致存在一定的誤判率。 如果申請(qǐng)空間太小,隨著元素的增多,1會(huì)越來越多,各個(gè)元素沖突的機(jī)會(huì)越來越來大,導(dǎo)致誤判率會(huì)越來越大。另外哈希函數(shù)的選擇及個(gè)數(shù)上也要平衡好,多個(gè)哈希函數(shù)雖然可以提供判斷的準(zhǔn)確性,但是會(huì)降低程序的處理速度,而哈希函數(shù)的增加又要求有更多的空間來存儲(chǔ)位置信息。 Bloom-Filter的應(yīng)用。 Bloom-Filter一般用于在大數(shù)據(jù)量的集合中判定某元素是否存在。例如郵件服務(wù)器中的垃圾郵件過濾器。在搜索引擎領(lǐng)域,Bloom-Filter最常用于網(wǎng)絡(luò)蜘蛛(Spider)的URL過濾,網(wǎng)絡(luò)蜘蛛通常有一個(gè) URL列表,保存著將要下載和已經(jīng)下載的網(wǎng)頁(yè)的URL,網(wǎng)絡(luò)蜘蛛下載了一個(gè)網(wǎng)頁(yè),從網(wǎng)頁(yè)中提取到新的URL后,需要判斷該URL是否已經(jīng)存在于列表中。此時(shí),Bloom-Filter算法是最好的選擇。 比如說,一個(gè)象 Yahoo,Hotmail 和 Gmai 那樣的公眾電子郵件(email)提供商,總是需要過濾來自發(fā)送垃圾郵件的人(spamer)的垃圾郵件。一個(gè)辦法就是記錄下那些發(fā)垃圾郵件的 email 地址。由于那些發(fā)送者不停地在注冊(cè)新的地址,全世界少說也有幾十億個(gè)發(fā)垃圾郵件的地址,將他們都存起來則需要大量的網(wǎng)絡(luò)服務(wù)器。 布隆過濾器是由巴頓.布隆于一九七零年提出的。它實(shí)際上是一個(gè)很長(zhǎng)的二進(jìn)制向量和一系列隨機(jī)映射函數(shù)。我們通過上面的例子來說明起工作原理。 假定我們存儲(chǔ)一億個(gè)電子郵件地址,我們先建立一個(gè)十六億二進(jìn)制(比特),即兩億字節(jié)的向量,然后將這十六億個(gè)二進(jìn)制位全部設(shè)置為零。對(duì)于每一個(gè)電子郵件地址 X,我們用八個(gè)不同的隨機(jī)數(shù)產(chǎn)生器(F1,F2, ...,F8) 產(chǎn)生八個(gè)信息指紋(f1, f2, ..., f8)。再用一個(gè)隨機(jī)數(shù)產(chǎn)生器 G 把這八個(gè)信息指紋映射到 1 到十六億中的八個(gè)自然數(shù) g1, g2, ...,g8。現(xiàn)在我們把這八個(gè)位置的二進(jìn)制位全部設(shè)置為一。當(dāng)我們對(duì)這一億個(gè) email 地址都進(jìn)行這樣的處理后。一個(gè)針對(duì)這些 email 地址的布隆過濾器就建成了。(見下圖) 現(xiàn)在,讓我們看看如何用布隆過濾器來檢測(cè)一個(gè)可疑的電子郵件地址 Y 是否在黑名單中。我們用相同的八個(gè)隨機(jī)數(shù)產(chǎn)生器(F1, F2, ..., F8)對(duì)這個(gè)地址產(chǎn)生八個(gè)信息指紋 s1,s2,...,s8,然后將這八個(gè)指紋對(duì)應(yīng)到布隆過濾器的八個(gè)二進(jìn)制位,分別是 t1,t2,...,t8。如果 Y 在黑名單中,顯然,t1,t2,..,t8 對(duì)應(yīng)的八個(gè)二進(jìn)制一定是一。這樣在遇到任何在黑名單中的電子郵件地址,我們都能準(zhǔn)確地發(fā)現(xiàn)。 布隆過濾器決不會(huì)漏掉任何一個(gè)在黑名單中的可疑地址。但是,它有一條不足之處。也就是它有極小的可能將一個(gè)不在黑名單中的電子郵件地址判定為在黑名單中,因?yàn)橛锌赡苣硞€(gè)好的郵件地址正巧對(duì)應(yīng)八個(gè)都被設(shè)置成一的二進(jìn)制位。好在這種可能性很小。我們把它稱為誤識(shí)概率。在上面的例子中,誤識(shí)概率在萬(wàn)分之一以下。 布隆過濾器的好處在于快速,省空間。但是有一定的誤識(shí)別率。常見的補(bǔ)救辦法是在建立一個(gè)小的白名單,存儲(chǔ)那些可能別誤判的郵件地址。 */ // 使用php程序來描述上面的算法 $set = array (1,2,3,4,5,6); // 判斷5是否在$set 中 $bloomFiter = array (0,0,0,0,0,0,0,0,0,0); // 通過某種算法改變$bloomFiter 中位數(shù)組表示集合,這里我們使用簡(jiǎn)單的算法,把集合中對(duì)應(yīng)的value 對(duì)應(yīng)到bloom中的位置變成1 // 算法如下 foreach ( $set as $key ){ $bloomFiter [ $key ] = 1 ; } var_dump( $bloomFiter ) ; //此時(shí) $bloomFiter = array(1,1,1,1,1,1); //判斷是否在集合中 if ( $bloomFiter [9] ==1){ echo '在set 中' ; } else { echo '不在set 中' ; } // 上面只是一個(gè)簡(jiǎn)單的例子,實(shí)際上哈希算法需要好幾個(gè),但另一方面,如果哈希函數(shù)的個(gè)數(shù)少,那么位數(shù)組中的0就多 class bloom_filter { function __construct( $hash_func_num =1, $space_group_num =1) { $max_length = pow(2, 25); $binary = pack( 'C' , 0); //1字節(jié)占用8位 $this ->one_num = 8; //默認(rèn)32m*1 $this ->space_group_num = $space_group_num ; $this ->hash_space_assoc = array (); //分配空間 for ( $i =0; $i < $this ->space_group_num; $i ++){ $this ->hash_space_assoc[ $i ] = str_repeat ( $binary , $max_length ); } $this ->pow_array = array ( 0 => 1, 1 => 2, 2 => 4, 3 => 8, 4 => 16, 5 => 32, 6 => 64, 7 => 128, ); $this ->chr_array = array (); $this ->ord_array = array (); for ( $i =0; $i <256; $i ++){ $chr = chr ( $i ); $this ->chr_array[ $i ] = $chr ; $this ->ord_array[ $chr ] = $i ; } $this ->hash_func_pos = array ( 0 => array (0, 7, 1), 1 => array (7, 7, 1), 2 => array (14, 7, 1), 3 => array (21, 7, 1), 4 => array (28, 7, 1), 5 => array (33, 7, 1), 6 => array (17, 7, 1), ); $this ->write_num = 0; $this ->ext_num = 0; if (! $hash_func_num ){ $this ->hash_func_num = count ( $this ->hash_func_pos); } else { $this ->hash_func_num = $hash_func_num ; } } function add( $key ) { $hash_bit_set_num = 0; // 離散key $hash_basic = sha1( $key ); // 截取前4位,然后十六進(jìn)制轉(zhuǎn)換為十進(jìn)制 $hash_space = hexdec( substr ( $hash_basic , 0, 4)); // 取模 $hash_space = $hash_space % $this ->space_group_num; for ( $hash_i =0; $hash_i < $this ->hash_func_num; $hash_i ++){ $hash = hexdec( substr ( $hash_basic , $this ->hash_func_pos[ $hash_i ][0], $this ->hash_func_pos[ $hash_i ][1])); $bit_pos = $hash >> 3; $max = $this ->ord_array[ $this ->hash_space_assoc[ $hash_space ][ $bit_pos ]]; $num = $hash - $bit_pos * $this ->one_num; $bit_pos_value = ( $max >> $num ) & 0x01; if (! $bit_pos_value ){ $max = $max | $this ->pow_array[ $num ]; $this ->hash_space_assoc[ $hash_space ][ $bit_pos ] = $this ->chr_array[ $max ]; $this ->write_num++; } else { $hash_bit_set_num ++; } } if ( $hash_bit_set_num == $this ->hash_func_num){ $this ->ext_num++; return true; } return false; } function get_stat() { return array ( 'ext_num' => $this ->ext_num, 'write_num' => $this ->write_num, ); } } //test //取6個(gè)哈希值,目前是最多7個(gè) $hash_func_num = 6; //分配1個(gè)存儲(chǔ)空間,每個(gè)空間為32M,理論上是空間越大誤判率越低,注意php.ini中可使用的內(nèi)存限制 $space_group_num = 1; $bf = new bloom_filter( $hash_func_num , $space_group_num ); $list = array ( 'http://test/1' , 'http://test/2' , 'http://test/3' , 'http://test/4' , 'http://test/5' , 'http://test/6' , 'http://test/1' , 'http://test/2' , ); foreach ( $list as $k => $v ){ if ( $bf ->add( $v )){ echo $v , "\n" ; } } print_r( $bf ->get_stat()); |
PHP中實(shí)現(xiàn)Bloom Filter算法
2020-09-15 16:13PHP中文網(wǎng) PHP教程
這篇文章主要介紹了PHP中實(shí)現(xiàn)Bloom Filter算法,本文直接給出實(shí)現(xiàn)代碼,代碼中給出詳細(xì)注釋,Bloom Filter算法介紹等內(nèi)容,需要的朋友可以參考下
延伸 · 閱讀
- 2022-03-11Java十大經(jīng)典排序算法圖解
- 2022-03-11java實(shí)現(xiàn)LRU緩存淘汰算法的方法
- 2022-03-10簡(jiǎn)單談?wù)刢entos7中配置php
- 2022-03-09python機(jī)器學(xué)習(xí)Sklearn實(shí)戰(zhàn)adaboost算法示例詳解
- 2022-03-08C++趣味算法之偵探推理
- 2022-03-07C++ STL中五個(gè)常用算法使用教程及實(shí)例講解
- PHP教程
Laravel框架使用Redis的方法詳解
這篇文章主要介紹了Laravel框架使用Redis的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Laravel框架中Redis數(shù)據(jù)庫(kù)配置、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以...
- PHP教程
PHP基于php_imagick_st-Q8.dll實(shí)現(xiàn)JPG合成GIF圖片的方法
這篇文章主要介紹了PHP基于php_imagick_st-Q8.dll實(shí)現(xiàn)JPG合成GIF圖片的方法,需要的朋友可以參考下 ...
- PHP教程
php 數(shù)組元素快速去重
本篇文章主要介紹了php數(shù)組元素快速去重的方法,具有很好的參考價(jià)值。下面跟著小編一起來看下吧...
- PHP教程
eclipse php wamp配置教程
這篇文章主要為大家詳細(xì)介紹了eclipse php wamp配置教程,極其簡(jiǎn)單的php入門教程,感興趣的小伙伴們可以參考一下...
- PHP教程
PHP里8個(gè)鮮為人知的安全函數(shù)分析
這篇文章主要介紹了PHP里8個(gè)鮮為人知的安全函數(shù),較為詳細(xì)的分析了addslashes、htmlentities、htmlspecialchars及md5等函數(shù)在PHP程序設(shè)計(jì)安全中所起到重要作用,非常...
- PHP教程
PHP之使用swoole統(tǒng)計(jì)在線人數(shù)和ID案例講解
這篇文章主要介紹了PHP之使用swoole統(tǒng)計(jì)在線人數(shù)和ID案例講解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參...
- PHP教程
PHP設(shè)計(jì)模式之迭代器模式Iterator實(shí)例分析【對(duì)象行為型】
這篇文章主要介紹了PHP設(shè)計(jì)模式之迭代器模式Iterator,結(jié)合實(shí)例形式分析了PHP迭代器模式Iterator相關(guān)概念、原理、實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參...
- PHP教程
PHP中使用匿名函數(shù)操作數(shù)據(jù)庫(kù)的例子
這篇文章主要介紹了PHP中使用匿名函數(shù)操作數(shù)據(jù)庫(kù)的例子,本文直接給出類的代碼、使用例子、繼承例子,需要的朋友可以參考下...