搜尋
首頁資料庫mysql教程海量数据处理算法之BloomFilter

海量数据处理算法之BloomFilter

Jun 07, 2016 pm 04:13 PM
bbloomfilter介紹資料處理海量演算法

算法介绍 Bloom Filter的中文名称叫做布隆过滤器,因为他最早的提出者叫做布隆(Bloom),因而而得此名。布隆过滤器简单的说就是为了检索一个元素是否存在于某个集合当中,以此实现数据的过滤。也许你会想,这还不简单,判断元素是否存在某集合中,遍历集合,

 

算法介绍

Bloom Filter的中文名称叫做布隆过滤器,因为他最早的提出者叫做布隆(Bloom),因而而得此名。布隆过滤器简单的说就是为了检索一个元素是否存在于某个集合当中,以此实现数据的过滤。也许你会想,这还不简单,判断元素是否存在某集合中,遍历集合,一个个去比较不就能得出结果,当然这没有任何的问题,但是当你面对的是海量数据的时候,在空间和时间上的代价是非常恐怖的,显然需要更好的办法来解决这个问题,而Bloom Filter就是一个不错的算法。具体怎么实现,接着往下看。

Bloom Filter

先来说说传统的元素检索的办法,比如说事先在内存中存储了一堆的url字符数组,然后给定一个指定的url,判断是否存在于之前的集合中,我们肯定是加载整个数组到内存中,然后一个个去比较,假设每条url字符平均所占的量只有几个字节,但是当数据变为海量的时候,也足以撑爆整个内存,这是空间上的一个局限。再者,逐次遍历的方式本身就是一种暴力搜索,搜索的时间将会随着集合容量的本身而线性扩展,一旦数据量变大,查询时间上的开销也是非常恐怖的。针对时间和空间上的问题,Bloom Filter都给出了完美的解决办法。首先第一个空间的问题,原本的数据占用的是字符,在这里我们用1个位占据,也就是说1个元素我用1/8的字节表示,不管你的url长度是10个字符,100字符,统统用一个位表示,所以在这里我们需要能够保证每个字符所代表的位不能冲突。因为用到了位的存储,我们需要对数据进行一个hash映射,以此得到他的位置,然后将此位置上的位置标为1(默认都是为0)。所以说白了,Bloom Filter就是由一个很长的位数组和一些随机的哈希函数构成。位数组你可以想象成下面的这种形式:

\

你可以想象这个长度非常长,反正1个单位就占据1个位,1k的空间就已经能够表示1024*8=8192位了。所以说内存空间得到了巨大的节约。现在一个问题来了,为什么我刚刚用了一些随机的哈希函数这个词而不是说一个呢,因为会有哈希碰撞,再好的哈希函数也不能保证不会发生哈希冲突,所以这里需要采用多个哈希函数,所以元素是否存在的判断条件就变为了只有所有的哈希函数映射的位置的值都是true的情况下,此元素才是存在于集合中的,这样判断的准确率就会大大提升了,哈希映射之后的效果图如下:

\

假设我们的程序采用了如上图所示的3个随机独立的哈希函数,1个元素需要进行3次不同的哈希函数的映射算法,对3个位置进行标记,对此元素的误判概率我们做个计算,要使此元素误判,就是说,他的这3个位置都有人占据了,就是说都与别的哈希函数有冲突,这最糟糕的情况就是他的3个映射位置与某个其他的元素通过哈希函数计算完全重叠,假设位空间长度1W位。每个位置被映射的概率就为1/1w,所以最糟糕的情况的冲突概率就是1/1w*1/1w*1/1w=1/10的12次方,如果最大的冲突概率的可能性呢,就是每个位置都与其中的某个哈希函数映射冲突,那误差概率就是叠加的情况1/1w+1/1w+1/1w=0.0003。结果已经非常明显了,通过3个哈希函数就已经能够保证足够低的误判率了,更别说当你用4个,5个哈希函数做映射的情况。下面问题又转移到了我们用什么方式去作为位数组呢,int数组,字符char数组,答案都不是。结果在下面。

BitSet

这个是java中的某个数据类型,C,C++我目前不清楚有没有这样的类,为什么选用这个而不是前面说的int,或char数组,首先int当然不行,1个int本身就有32位,占了4个字节,用他做出0,1的存储显然相当于没省下空间,自然我们就想到了用字符数组char[],在C语言中1个char占一个字节,而在java中由于编码方式的不同,一个char占2个字节,用char做存储也只是稍稍比int介绍了一半的空间,并没有真正的做到一个元素用一个位来表示,后来查了一下,java里面就有内置了BitSet专门就是做位存储的,还能够进行位相关的许多操作,他的操作其实就是和数组一样,也是从0开始的。不熟悉的同学可以自行上网查阅相关资料,其实int数组也可以实现类似的功能,不过自己要做转换,把int当成32位来算,之前我写过相关的文章,是关于位示图法存储大数据

算法的实现

算法其实非常的简单,我这里用一组少量的数据进行模拟。

输入数据input.txt:

 

mike
study
day
get
last
exam
think
fish
he
然后是测试数据,用于查询操作testInput.txt:

 

 

play
mike
study
day
get
Axis
last
exam
think
fish
he
其实就是我随便组合的一些词语。

 

算法的工具类BloomFilterTool.java:

 

package BloomFilter;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;

/**
 * 布隆过滤器算法工具类
 * 
 * @author lyq
 * 
 */
public class BloomFilterTool {
	// 位数组设置为10w位的长度
	public static final int BIT_ARRAY_LENGTH = 100000;

	// 原始文档地址
	private String filePath;
	// 测试文档地址
	private String testFilePath;
	// 用于存储的位数组,一个单元用1个位存储
	private BitSet bitStore;
	// 原始数据
	private ArrayList<String> totalDatas;
	// 测试的查询数据
	private ArrayList<String> queryDatas;

	public BloomFilterTool(String filePath, String testFilePath) {
		this.filePath = filePath;
		this.testFilePath = testFilePath;

		this.totalDatas = readDataFile(this.filePath);
		this.queryDatas = readDataFile(this.testFilePath);
	}

	/**
	 * 从文件中读取数据
	 */
	public ArrayList<String> readDataFile(String path) {
		File file = new File(path);
		ArrayList<String> dataArray = new ArrayList<String>();

		try {
			BufferedReader in = new BufferedReader(new FileReader(file));
			String str;
			String[] tempArray;
			while ((str = in.readLine()) != null) {
				tempArray = str.split(" ");
				for(String word: tempArray){
					dataArray.add(word);
				}
			}
			in.close();
		} catch (IOException e) {
			e.getStackTrace();
		}

		return dataArray;
	}
	
	/**
	 * 获取查询总数据
	 * @return
	 */
	public ArrayList<String> getQueryDatas(){
		return this.queryDatas;
	}

	/**
	 * 用位存储数据
	 */
	private void bitStoreData() {
		long hashcode = 0;
		bitStore = new BitSet(BIT_ARRAY_LENGTH);

		for (String word : totalDatas) {
			// 对每个词进行3次哈希求值,减少哈希冲突的概率
			hashcode = BKDRHash(word);
			hashcode %= BIT_ARRAY_LENGTH;

			
			bitStore.set((int) hashcode, true);

			hashcode = SDBMHash(word);
			hashcode %= BIT_ARRAY_LENGTH;

			bitStore.set((int) hashcode, true);

			hashcode = DJBHash(word);
			hashcode %= BIT_ARRAY_LENGTH;

			bitStore.set((int) hashcode, true);
		}
	}

	/**
	 * 进行数据的查询,判断原数据中是否存在目标查询数据
	 */
	public Map<String, Boolean> queryDatasByBF() {
		boolean isExist;
		long hashcode;
		int pos1;
		int pos2;
		int pos3;
		// 查询词的所属情况图
		Map<String, Boolean> word2exist = new HashMap<String, Boolean>();

		hashcode = 0;
		isExist = false;
		bitStoreData();
		for (String word : queryDatas) {
			isExist = false;
			
			hashcode = BKDRHash(word);
			pos1 = (int) (hashcode % BIT_ARRAY_LENGTH);

			hashcode = SDBMHash(word);
			pos2 = (int) (hashcode % BIT_ARRAY_LENGTH);

			hashcode = DJBHash(word);
			pos3 = (int) (hashcode % BIT_ARRAY_LENGTH);

			// 只有在3个哈希位置都存在才算真的存在
			if (bitStore.get(pos1) && bitStore.get(pos2) && bitStore.get(pos3)) {
				isExist = true;
			}

			// 将结果存入map
			word2exist.put(word, isExist);
		}

		return word2exist;
	}

	/**
	 * 进行数据的查询采用普通的过滤器方式就是,逐个查询
	 */
	public Map<String, Boolean> queryDatasByNF() {
		boolean isExist = false;
		// 查询词的所属情况图
		Map<String, Boolean> word2exist = new HashMap<String, Boolean>();

		// 遍历的方式去查找
		for (String qWord : queryDatas) {
			isExist = false;
			for (String word : totalDatas) {
				if (qWord.equals(word)) {
					isExist = true;
					break;
				}
			}

			word2exist.put(qWord, isExist);
		}

		return word2exist;
	}

	/**
	 * BKDR字符哈希算法
	 * 
	 * @param str
	 * @return
	 */
	private long BKDRHash(String str) {
		int seed = 31; /* 31 131 1313 13131 131313 etc.. */
		long hash = 0;
		int i = 0;

		for (i = 0; i < str.length(); i++) {
			hash = (hash * seed) + (str.charAt(i));
		}

		hash = Math.abs(hash);
		return hash;
	}

	/**
	 * SDB字符哈希算法
	 * 
	 * @param str
	 * @return
	 */
	private long SDBMHash(String str) {
		long hash = 0;
		int i = 0;
		
		for (i = 0; i < str.length(); i++) {
			hash = (str.charAt(i)) + (hash << 6) + (hash << 16) - hash;
		}

		hash = Math.abs(hash);
		return hash;
	}

	/**
	 * DJB字符哈希算法
	 * 
	 * @param str
	 * @return
	 */
	private long DJBHash(String str) {
		long hash = 5381;
		int i = 0;

		for (i = 0; i < str.length(); i++) {
			hash = ((hash << 5) + hash) + (str.charAt(i));
		}

		hash = Math.abs(hash);
		return hash;
	}

}
场景测试类Client.java:

 

 

package BloomFilter;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Map;

/**
 * BloomFileter布隆过滤器测试类
 * 
 * @author lyq
 * 
 */
public class Client {
	public static void main(String[] args) {
		String filePath = "C:\\Users\\lyq\\Desktop\\icon\\input.txt";
		String testFilePath = "C:\\Users\\lyq\\Desktop\\icon\\testInput.txt";
		// 总的查询词数
		int totalCount;
		// 正确的结果数
		int rightCount;
		long startTime = 0;
		long endTime = 0;
		// 布隆过滤器查询结果
		Map<String, Boolean> bfMap;
		// 普通过滤器查询结果
		Map<String, Boolean> nfMap;
		//查询总数据
		ArrayList<String> queryDatas;

		BloomFilterTool tool = new BloomFilterTool(filePath, testFilePath);

		// 采用布隆过滤器的方式进行词的查询
		startTime = System.currentTimeMillis();
		bfMap = tool.queryDatasByBF();
		endTime = System.currentTimeMillis();
		System.out.println("BloomFilter算法耗时" + (endTime - startTime) + "ms");

		// 采用普通过滤器的方式进行词的查询
		startTime = System.currentTimeMillis();
		nfMap = tool.queryDatasByNF();
		endTime = System.currentTimeMillis();
		System.out.println("普通遍历查询操作耗时" + (endTime - startTime) + "ms");

		boolean isExist;
		boolean isExist2;

		rightCount = 0;
		queryDatas = tool.getQueryDatas();
		totalCount = queryDatas.size();
		for (String qWord: queryDatas) {
			// 以遍历的查询的结果作为标准结果
			isExist = nfMap.get(qWord);
			isExist2 = bfMap.get(qWord);

			if (isExist == isExist2) {
				rightCount++;
			}else{
				System.out.println("预判错误的词语:" + qWord);
			}
		}
		System.out.println(MessageFormat.format(
				"Bloom Filter的正确个数为{0},总查询数为{1}个,正确率{2}", rightCount,
				totalCount, 1.0 * rightCount / totalCount));
	}
}
在算法的测试类中我对于Bloom Filter和普通的遍历搜索方式进行了时间上的性能比较,当数据量比较小的时候,其实是看不出什么差距,甚至有可能布隆过滤器所花的时间可能更长比如我下面的某次测试结果:

 

 

BloomFilter算法耗时2ms
普通遍历查询操作耗时0ms
Bloom Filter的正确个数为11,总查询数为11个,【本文来自鸿网互联 (http://www.68idc.cn)】正确率1
但是当我用真实的测试数据进行测试,我把原始数据缓存了一篇标准的文档,然后把查询的结果词语数量进行了翻倍,然后执行同样的程序结果变为了下面这个样子:

 

 

BloomFilter算法耗时16ms
普通遍历查询操作耗时47ms
Bloom Filter的正确个数为2,743,总查询数为2,743个,正确率1
其实这还不足以模拟海量数据的场景,对于这个结果也不难理解,普通的暴力搜寻,是和原始数据的总量相关,时间复杂度为O(n)的,而Bloom Filter,则是常量级别,做一个哈希映射就OK 了,时间复杂度O(l),

 

算法小结

算法在实现的过程中遇到了一些小问题,第一就是在使用哈希函数的时候,因为我是随机的选了3个字符哈希函数,后来发现老是会越界,一越界数值就会变为负的再通过BitSet就会报错,原本在C语言中可以用unsigned int来解决,java中没有这个概念,于是就直接取hash绝对值了。Bloom Filter算法的一个特点是数据可能会出现误判,但是绝对不会漏判,误判就是把不是存在集合中的元素判定成有,理由是哈希冲突可能造成此结果,而漏判指的是存在的元素判定成了不存在集合中,这个是绝对不可能的,因为如果你存在,你所代表的位置就一定会有被哈希映射到,一旦映射到了,在你再去查找就不会漏掉。算法的应用范围其实挺多的,典型的比如垃圾邮箱地址的过滤。

 

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
如何在MySQL中刪除或修改現有視圖?如何在MySQL中刪除或修改現有視圖?May 16, 2025 am 12:11 AM

todropaviewInmySQL,使用“ dropviewifexistsview_name;” andTomodifyAview,使用“ createOrreplaceViewViewViewview_nameAsSelect ...”。 whendroppingaview,asew dectivectenciesanduse和showcreateateviewViewview_name;“ tounderStanditSsstructure.whenModifying

MySQL視圖:我可以使用哪些設計模式?MySQL視圖:我可以使用哪些設計模式?May 16, 2025 am 12:10 AM

mySqlViewScaneFectectialized unizedesignpatternslikeadapter,Decorator,Factory,andObserver.1)adapterPatternadaptSdataForomDifferentTablesIntoAunifiendView.2)decoratorPatternenhancateDataWithCalcalcualdCalcalculenfields.3)fieldfields.3)

在MySQL中使用視圖的優點是什麼?在MySQL中使用視圖的優點是什麼?May 16, 2025 am 12:09 AM

查看InMysqlareBeneForsImplifyingComplexqueries,增強安全性,確保dataConsistency,andOptimizingPerformance.1)他們simimplifycomplexqueriesbleiesbyEncapsbyEnculatingThemintoreusableviews.2)viewsEnenenhancesecuritybyControllityByControllingDataAcces.3)

如何在MySQL中創建一個簡單的視圖?如何在MySQL中創建一個簡單的視圖?May 16, 2025 am 12:08 AM

toCreateAsimpleViewInmySQL,USEthecReateaTeviewStatement.1)defitEtheetEtheTeViewWithCreatEaTeviewView_nameas.2)指定usethectstatementTorivedesireddata.3)usethectStatementTorivedesireddata.3)usetheviewlikeatlikeatlikeatlikeatlikeatlikeatable.views.viewssimplplifefifydataaccessandenenanceberity but consisterfort,butconserfort,consoncontorfinft

MySQL創建用戶語句:示例和常見錯誤MySQL創建用戶語句:示例和常見錯誤May 16, 2025 am 12:04 AM

1)foralocaluser:createUser'localuser'@'@'localhost'Indidendify'securepassword'; 2)foraremoteuser:creationuser's creationuser'Remoteer'Remoteer'Remoteer'Remoteer'Remoteer'Remoteer'Remoteer'Remoteer'Rocaluser'@'localhost'Indidendify'seceledify'Securepassword'; 2)

在MySQL中使用視圖的局限性是什麼?在MySQL中使用視圖的局限性是什麼?May 14, 2025 am 12:10 AM

mysqlviewshavelimitations:1)他們不使用Supportallsqloperations,限制DatamanipulationThroughViewSwithJoinsOrsubqueries.2)他們canimpactperformance,尤其是withcomplexcomplexclexeriesorlargedatasets.3)

確保您的MySQL數據庫:添加用戶並授予特權確保您的MySQL數據庫:添加用戶並授予特權May 14, 2025 am 12:09 AM

porthusermanagementinmysqliscialforenhancingsEcurityAndsingsmenting效率databaseoperation.1)usecReateusertoAddusers,指定connectionsourcewith@'localhost'or@'%'。

哪些因素會影響我可以在MySQL中使用的觸發器數量?哪些因素會影響我可以在MySQL中使用的觸發器數量?May 14, 2025 am 12:08 AM

mysqldoes notimposeahardlimitontriggers,butacticalfactorsdeterminetheireffactective:1)serverConfiguration impactactStriggerGermanagement; 2)複雜的TriggerSincreaseSySystemsystem load; 3)largertablesslowtriggerperfermance; 4)highConconcConcrencerCancancancancanceTigrignecentign; 5); 5)

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

北端:融合系統,解釋
1 個月前By尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
4 週前By尊渡假赌尊渡假赌尊渡假赌
<🎜>掩蓋:探險33-如何獲得完美的色度催化劑
2 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用