【位图布隆过滤器海量数据面试题】

news2024/11/18 19:54:20

文章目录

  • 1 位图
  • 2 布隆过滤器

1 位图

首先我们来看看一个腾讯的面试题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
分析:

40亿个不重复整形数据,大概有160亿字节,也就是16GB大小左右的数据,直接放在内存肯定是行不通的,一般内存没有那么大的空间,所以我们不能够用set/unoedered_set等容器来存放数据,那我们应该如何处理呢?

我们可以用哈希的思想来处理:将每一个数据映射一个比特位,比特位为1就表示该数据存在,为0就表示该数据不存在,这样40亿个数据就表示40亿个比特位,算下来就5亿字节左右的样子,也就是0.5GB,内存里面完全可以存下。
所谓位图,就是用每一个比特位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据是否存在。所以这种场景我们可以用位图来处理。

我们如何自己实现一个位图结构呢?大家可以参考下面这种方式:

template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_bits.resize(N/8+1, 0);
		}

		void set(size_t num)
		{
			size_t i = num / 8;
			size_t j = num % 8;

			_bits[i] |= (1 << j);
		}

		void reset(size_t num)
		{
			size_t i = num / 8;
			size_t j = num % 8;

			_bits[i] &= ~(1 << j);
		}

		bool test(size_t num)
		{
			size_t i = num / 8;
			size_t j = num % 8;

			return _bits[i] & (1 << j);
		}

	private:
		vector<char> _bits;
		
	};

注意:

  • 这里面给了一个非类型模板参数N,表示我们要开辟数据最大值(注意并不是数据有多少个);
  • 由于vector里面套用的是char,所以实际resize大小时/8,当然大家也可以用int,不过其他地方(像set/reset/test)也得做出相应的调整。

这个是我们自己实现的一份位图,不过STL里面也提供了一个位图结构:
【STL的bitset】
大家有兴趣可以下去看看这个结构,这里面提供的接口还是挺多的。

那位图还有哪些应用呢?
大家来看看下面这几道题,能不能找到些思路:

    1. 给定100亿个整数,设计算法找到只出现一次的整数?
    1. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
    1. 1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数?

我们首先来看看第一个问题:给定100亿个整数,设计算法找到只出现一次的整数?
分析:

100亿个整数,由于无符号整数最大才42亿多,所以里面肯定有大量重复数据,由于数据量过大,之前学过的红黑树/哈希表的方式根本行不通,所以只有通过位图来处理,那么我们究竟如何表示数据出现了一次不是出现了一次呢?我们不妨用两个位图来表示一个数据,00表示没有数据01表示只出现了一次10表示出现了2次及以上。这样我们就能够快速判断出是否只出现了一次。我们可以自己在像上面一样设计一个2个比特位表示一个数据的位图结构,只不过里面数据处理就是/4 %4了,只不过这样做又得重新实现一份,我们不妨复用刚才实现的bitset,用两个bitset来标识。

实例代码:

template<size_t N>
	class twobits
	{
	private:
		bitset<N> _bits1;
		bitset<N> _bits2;

	public:

		void set(size_t num)
		{
			//00->01
			if (_bits1.test(num) == false && _bits2.test(num) == false)
			{
				_bits2.set(num);
			}
			//01->10
			else if (_bits1.test(num) == false && _bits2.test(num) == true)
			{
				_bits1.set(num);
				_bits2.reset(num);
			}
			
		}

		void print_one_cnt()
		{
			for (size_t i = 0; i < N; ++i)
			{
				if (_bits1.test(i) == false && _bits2.test(i) == true)
					cout << i << " ";

			}
		}
	};

我们可以直接打印出来所有只出现一次的数据并且排好了序。

我们在来看看问题2:给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
分析:

思路一:两个文件中分别有100亿个整数,我们可以将其中一个文件放在位图结构中加载到内存,然后再从另外一个文件中一个一个读取数据在位图结构中test。

但是这样处理会有一个问题,大家想想是什么?这样处理的话非位图结构中的重复数据也就被保留了下来,也就是找到的文件交集会包含重复数据,那这样肯定是不行的。我们可以在找的时候第一次找到了就将位图结构的对应映射位置置空,这样下次找的时候就不会找到重复数据了。

思路二:将两个文件都放进位图中,然后在遍历整张位图看是否数据存在。

注意这种方式我们是要遍历到数据最大值的也就是0XFFFFFFFF,我们也可以将-1转换为size_t类型。因为我们并不知道最大数据是多少,所以必须开这么大(0XFFFFFFFF)的空间。

这里由于是有100亿的数据,所以我们选用第二种方式是可行的,假如数据量不是很大,比如10亿左右的话,我们就可以选用第一种方式,因为我们并不需要遍历整张位图,而是只遍历文件中数据即可。

再来看看问题3:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数?
分析:

相信解决了问题一后问题三的处理也会变得很简单了。00表示没有数据01表示只出现了一次10表示出现了2次11表示出现了2次以上,这样我们就能够统计出不超过两次的所有整数。

我们可以浅浅总结下位图的应用:

1. 快速查找某个数据是否在一个集合中
2. 排序 + 去重
3. 求两个集合的交集、并集等
4. 操作系统中磁盘块标记

位图的优点是啥捏?

速度快,节省空间

那有啥缺点呢?

只能够映射整形,像浮点型,字符串等类型不能够映射。

像我们常用的字符串类型那应该怎么办呢?只是单纯的位图结构肯定是处理不了了,所以就引出了我们下面要讲的内容:布隆过滤器.


2 布隆过滤器

字符串不能够直接转化为整形,但是字符串可以间接的转换为整形,这就是字符串哈希,大家可以去看看下面的文章,介绍了比较常用的字符串哈希算法:【字符串哈希算法】

那我们可以将字符串通过字符串哈希算法转换为整形再来映射,但是这样做也引出了另外的问题,就是可能存在冲突:
假设通过某种哈希函数,出现了下面的这种情况:
在这里插入图片描述
我们发现了阿里和腾讯通过某种哈希算法映射到了同一个位置,那不就冲突了吗?有啥办法可以降低冲突吗?(注意:哈希冲突是无法避免,只能通过某种手段来降低冲突的概率)
我们不妨多给集中哈希算法,让每一个字符串映射多个位置,比如这里让每一个字符串用3中不同的哈希算法映射:
在这里插入图片描述这样的话当我们判断时如果有其中一个哈希函数映射的位置不存在,那就说明该字符串一定是不存在的,而3个哈希函数映射的位置都存在的话是不能够一定确定该字符串一定是存在的,有可能存在误判(由于其他的字符串可能通过哈希函数映射到相同的位置)
所以这种方式我们可以得出结论:判断不在一定是准确的,判断在可能会存在误判

此外布隆过滤器的长度也会直接影响误判的概率,一般来说,布隆过滤器越长,冲突的概率就越小,但是布隆过滤器也不能够太长,否则节省空间的优势就没有了,那么我们究竟如何权衡布隆过滤器的长度和哈希函数的个数呢?
有人专门做了这样的统计:参考自这篇文章【详解布隆过滤器的原理,使用场景和注意事项】
在这里插入图片描述其中里面贴了一个公式:
在这里插入图片描述
其中k为哈希函数的个数,m为布隆过滤器的长度,n为插入元素的个数。
所以m=knln2
假设我们给了3个哈希函数,算下来m大概是5*n左右
我们可以自己来实现一个简易版的布隆过滤器:

struct BKDRHash
	{
		size_t operator()(const string& s)
		{
			// BKDR
			size_t value = 0;
			for (auto ch : s)
			{
				value *= 31;
				value += ch;
			}
			return value;
		}
	};


	struct APHash
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (long i = 0; i < s.size(); i++)
			{
				if ((i & 1) == 0)
				{
					hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3));
				}
				else
				{
					hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5)));
				}
			}
			return hash;
		}
	};


	struct DJBHash
	{
		size_t operator()(const string& s)
		{
			size_t hash = 5381;
			for (auto ch : s)
			{
				hash += (hash << 5) + ch;
			}
			return hash;
		}
	};


	template<size_t N,class K=string,class Hash1= BKDRHash,class Hash2= APHash,class Hash3= DJBHash>
	class bloomfilter
	{
	private:
		static const int m = 5;
		bitset<N*m> _bit;
	public:
		void set(const K& key)
		{
			size_t len = N * m;
			size_t hashi1 = Hash1()(key) % len;
			size_t hashi2 = Hash2()(key) % len; 
			size_t hashi3 = Hash3()(key) % len;

			_bit.set(hashi1);
			_bit.set(hashi2);
			_bit.set(hashi3);

		}

		bool test(const K& key)
		{
			size_t len = N * m;
			size_t hashi1 = Hash1()(key) % len;
			size_t hashi2 = Hash2()(key) % len;
			size_t hashi3 = Hash3()(key) % len;

			if (!_bit.test(hashi1))
				return false;
			if (!_bit.test(hashi2))
				return false;
			if (!_bit.test(hashi3))
				return false;

			return true;//存在误判

		}
	};

注意布隆过滤器的模板参数N表示的是数据个数,因为不管数据有多大我们取模后的数据都是小于len的长度的。
为了测试我们可以写一个测试程序来测试一下误判率:

void test_bloomfilter1()
	{
		srand(time(0));
		const size_t N = 10000;
		bloomfilter<N> bf;
		std::vector<std::string> v1;
		std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";

		for (size_t i = 0; i < N; ++i)
		{
			v1.push_back(url + std::to_string(i));
		}

		for (auto& str : v1)
		{
			bf.set(str);
		}

		// v2跟v1是相似字符串集,但是不一样
		std::vector<std::string> v2;
		for (size_t i = 0; i < N; ++i)
		{
			std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
			url += std::to_string(999999 + i);
			v2.push_back(url);
		}

		size_t n2 = 0;
		for (auto& str : v2)
		{
			if (bf.test(str))
			{
				++n2;
			}
		}
		cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;

		// 不相似字符串集
		std::vector<std::string> v3;
		for (size_t i = 0; i < N; ++i)
		{
			string url = "zhihu.com";
			//string url = "https://www.cctalk.com/m/statistics/live/16845432622875";
			url += std::to_string(i + rand());
			v3.push_back(url);
		}

		size_t n3 = 0;
		for (auto& str : v3)
		{
			if (bf.test(str))
			{
				++n3;
			}
		}
		cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;
	}

我们主要测试了一下相似字符和不相似字符的误判率:
我们可以运行下结果:
在这里插入图片描述哈希函数为3个,布隆过滤器的长度为5倍左右的样子大概是上面这个样子,当然,你布隆过滤器的长度越长理论上冲突的概率也会越低,大家可以自行下去测验。

顺便问一下布隆过滤器支持删除操作吗?
如果只是我们上面这种设计思路的话就应该不能够支持删除,因为可能存在误删的情况,有什么方法可以支持删除呢?
我们可以用引用计数的思想方法来处理:我们标识每一个字符串时用多个比特位来标识(具体多少大家可根据数据重复量来选择),当我们要选择删除时就用我们上面的思想:这里我以2个比特位来举例,01->00,10->01,11->10
这样我们就可以避免误删的情况。

那布隆过滤器的应用有哪些呢?
我们举一个栗子,我们玩游戏时,游戏呢称的查重我们希望玩家只要输入名字后立马就能够识别是否该名字已经出现过,所以我们可以用布隆过滤器来处理,并且我们发现名字查重是可以容忍误判的,因为系统判定不在一定是准确的,而就算误判了在,那么玩家是不知道该游戏名字是否存在的,所以以玩家的视角这个是没问题的。但是假如玩家想要注册绑定手机号时如果玩家并没有绑定手机号,但是系统误判了,玩家是能够直接感知到的。但是我们可以提前用布隆过滤器过滤出不在,因为不在一定是准确的,而判定为在时在从数据库中查找是否真正存在,这样就提高了查找的效率。

我们可以来总结下布隆过滤器的优点:

  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关;
  2. 哈希函数相互之间没有关系,方便硬件并行运算;
  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势;
  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势;
  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能;
  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算.

布隆过滤器的缺点:

  1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据);
  2. 不能获取元素本身;
  3. 一般情况下不能从布隆过滤器中删除元素;
  4. 如果采用计数方式删除,可能会存在计数回绕问题.

我们再来看一个面试题:给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法。
分析:

有两个文件分别有100亿次query,我们可以认为一次查询就是一次sql语句,假设一次查询30字节,那么老规矩,100亿次查询放在内存肯定行不通的(大概300GB)。我们可以用布隆过滤器作为近似算法:思想还是与前面一样,我们可以将其中一个文件的query放进布隆过滤器中,然后在从另外一个文件读取数据,但是这种方式也要处理去重,由于布隆过滤器是不太方便处理去重的,所以我们可以将文件交集找到了后在进行去重(前提要保证文件交集大小不会超过1GB)。

那么如何精确的找到文件交集呢?

我们可以利用哈希切割的思想,将大文件切分成一个一个小的文件。但是怎样切分是关键。问一下,我们能够平均切分文件吗?这种方式未免也太低效了吧,切分后,其中一个大文件的所有小文件还要与另外一个大文件的所有小文件一 一比较,效率太低下了。所以我们采用哈希切分的思想来进行切割,用某种哈希函数将query转换,这里我们不妨将文件分成600份,怎样处理呢?

我们可以采用下面这种方式:
Hashi=HashFunc(query)%600
这样通过哈希函数以及取模运算,每个query都会有一个对应的映射下标,下标是几,就被分到对应下标的小文件。

那么相同查询一定会被分到同一下标的小文件,所以我们只需要比较相同下标文件的数据即可:
在这里插入图片描述这样做就极大提高了效率,我们可以将相同下标的小文件放到set/unoedered_set中去重找交集,最后将所有的小交集并在一起即可。但是这种方式处理还是可能存在着一些问题,大家想想是什么?
我们理想状态下是将大文件分成了内存可以容纳的下的小文件,但是实际上万一有大量的query进入到了某个小文件从而造成了小文件中大小分布不均衡的问题,可能其中一个小文件的大小只有几MB,而另外一个小文件中的数据大小有几GB,这种情况应该怎么处理?
我们可以分成两种情况来处理:

  • 一种是小文件中有大量重复数据
  • 另外一种是小文件中没有大量重复数据

小文件中如果有大量重复数据时就算我们更换哈希函数也是徒劳的,那我们应该怎样处理呢?
我们可以这样处理,将小文件中的数据一个一个放进内存的set中,如果里面有大量重复数据的话,那么set可以帮助我们去重,去重了后可能就能够将数据全部放进内存,如果我们在将数据放进set中抛了异常,那就说明该文件中大概率是没有大量重复数据的,那么我们重新更换哈希函数将小文件再次划分为更小的文件继续处理即可。如果set没有抛异常那么我们就直接将数据加载到了set中,直接进行两个文件找交集即可。

这样我们就用了精确和近似算法找到了两个文件的交集。

趁热打铁,我们再来看一个相似的面试题:给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?与上题条件相同,如何找到top K的IP?
分析:

基本思路与上面讲解的思路一致,我们仍然采用哈希切割的思想将大文件分成小文件,然后将小文件中的数据加载到内存用map/unoedered_map进行数据次数的统计,最后找到最大出现的次数即可。这里我们的数据要保证切割了后小文件的数据内存放得下,至于topK问题我们可以建立一个小堆,一次取小文件中的数据比较即可。

好了,今天的讲解就到这里了,如果你感觉该文章对你有所收获的话能不能3连支持一下博主。
💘💘💘


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/641615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux---vim的使用

专栏&#xff1a;Linux 个人主页&#xff1a;HaiFan. 本章为大家带来Linux工具—vim Linux工具 关于rzszyumvim的基本概念vim的基本操作vim正常模式命令集vim末行模式命令集简单vim配置配置文件的位置常用配置选项 关于rzsz 这个工具用于windows机器和Linux机器通过Xshell传输…

字符串--字符指针、字符串的访问和输入/输出(字符串空格问题,输入带双引号的字符串)

一、字符指针 字符指针&#xff08;Character Pointers&#xff09;是指向字符型数据的指针变量。 每个字符串在内存中都占用一段连续的存储空间&#xff0c;并有唯一确定的首地址。因此&#xff0c;只要将字符串的首地址赋值给字符指针&#xff0c;即可让字符指针指向一个字符…

安卓主板/开发板定制开发生产,MTK/高通/紫光展锐安卓开发板

智物通讯是一家致力于行业安卓主板定制开发的公司&#xff0c;提供包括MTK四核/八核方案、MTK、高通、紫光展锐系列行业主板方案定制等多样化的服务。 用户可以根据实际需求选择各种不同的模块类型&#xff0c;包括4G模块和5G模块。其中4G模块方案有MT6761、MT6762、MT6765、M…

爬虫 python 正则匹配 保存网页图片

目录 1. 简介1.1 爬虫1.2 爬虫语言1.3 python库1.4 我的步骤 2. 导入包2.1 代码2.2 requests库 3. 写入文件函数4. 获取图片5. 主函数5.1 代码5.2 说明一下webbrowser 6. 所有代码7. 其他&#xff08;可以忽略&#xff09;8. 总结 在这里我只提供的是一种方法&#xff0c;有很多…

webpack生产模式配置

一、生产模式和开发模式介绍 生成模式&#xff08;production mode&#xff09;是指在开发完成后将代码部署到生产环境中运行的模式&#xff0c;通常需要进行代码压缩、优化、合并&#xff0c;以减少文件大小和请求次数&#xff0c;提高页面加载速度和运行效率。 开发模式&am…

Android12 系统开发记录-迅为RK3588使用ADB工具

ADB 英文名叫 Android debug bridge &#xff0c;是 Android SDK 里面的一个工具&#xff0c;用这个工具可以 操作管理 Android 模拟器或者真实的 Android 设备&#xff0c;主要的功能如下所示&#xff1a;  在 Android 设备上运行 shell 终端&#xff0c;用命令行操作 …

How to fix the NHS 如何改革英国的国民医疗保险制度 | 经济学人20230527版社论双语精翻

他山之石&#xff1a;2023年5月27日《经济学人》社论&#xff08;Leaders&#xff09;精选&#xff1a;《如何改革英国的国民医疗保险制度》&#xff08;“How to fix the NHS”&#xff09; Leaders | The sick factor 社论 | 致病因素 How to fix the NHS 如何改革英国的国民…

软件安装mysql

1系统约定 安装文件下载目录&#xff1a;/data/softwareMysql目录安装位置&#xff1a;/usr/local/mysql数据库保存位置&#xff1a;/data/mysql日志保存位置&#xff1a;/data/log/mysql 2下载mysql 在官网&#xff1a;MySQL :: Download MySQL Community Server 中&#x…

Guitar Pro8.0.1最新中文版本吉他谱下载及使用教程

许多人都对吉他这个乐器很感兴趣&#xff0c;因为吉他的学习成本较低&#xff0c;学习难度较小&#xff0c;即便是零基础的小白通过短期的学习也能掌握基本的技巧。但实际上&#xff0c;学习吉他还需要认识吉他谱&#xff0c;识谱是每个吉他爱好者都必须掌握的技能&#xff0c;…

数字孪生世界建设核心能力:物理世界感知能力

中国信通院在《数字孪生城市白皮书&#xff08;2022年&#xff09;》中指出&#xff0c;数字孪生城市技术集成性高&#xff0c;核心板块日渐清晰&#xff0c;当前已逐步深入到城市全要素表达、业务预警预测、场景仿真推演、态势感知只能决策等多个环节。数字孪生技术的向前发展…

Yum update和upgrade的区别

Yum update和upgrade的区别 Linux yum中package升级命令有两个分别是 yum upgrade 和 yum update 1、区别 默认情况下&#xff0c;yum update和yum upgrade的功能是完全一样的&#xff0c;都是将需要更新的package(这里的包包括常规的包、软件、系统版本、系统内核)更新至软件…

如何使用ArcGIS加载历史影像

历史影像对研究地物的变化可以产生很直观的效果&#xff0c;Esri提供了在线浏览的历史影像&#xff0c;这里给大家介绍一下如何将这个历史影像加载到ArcGIS&#xff0c;希望能对你有所帮助。 获取地图链接 打开地图网站&#xff08;https://livingatlas.arcgis.com/wayback/&a…

【MySQL】复合查询(重点)

&#x1f3e0; 大家好&#xff0c;我是 兔7 &#xff0c;一位努力学习C的博主~&#x1f4ac; &#x1f351; 如果文章知识点有错误的地方&#xff0c;请指正&#xff01;和大家一起学习&#xff0c;一起进步&#x1f440; &#x1f680; 如有不懂&#xff0c;可以随时向我提问&…

实验篇(7.2) 13. 创建点对点安全隧道 (二)(FortiGate-IPsec) ❀ 远程访问

【简介】上一篇实验发现&#xff0c;两端都是可以远程的公网IP的话&#xff0c;两端防火墙都可以发出连接请求&#xff0c;并且都能够连通。这样的好处是安全隧道不用随时在线&#xff0c;只在有需求时才由发起方进行连接。但是现实中很多情况下只有一端公网IP可以远程&#xf…

番外篇 离线服务器环境配置与安装

&#xff08;离线远程服务器的Anaconda安装与卸载torch的安装与卸载&#xff09; 我参考或百度一些博主发的经验贴关于Anaconda的安装与卸载等教程&#xff0c;但实际情况是每一个服务器遇到的问题多多少少总有不一样的地方&#xff0c;虽然可以借鉴&#xff0c;但不能完全照搬…

常见Visual Studio Code 快捷键

一&#xff0c;文件与窗口快捷键&#xff1a; 1.打开一个新窗口&#xff1a; CtrlShiftN 2.关闭窗口&#xff1a; CtrlShiftW 3.文件切换&#xff1a;CtrlTab 4.快速打开文件&#xff1a;CtrlP 5.新建文件&#xff1a; CtrlN 6.切换侧边栏&#xff1a;CtrlB 7.选中单个文…

JWT代码实现

什么是 JWT&#xff1f; JSON Web Token&#xff0c;通过数字签名的方式&#xff0c;以 JSON 对象为载体&#xff0c;在不同的服务终端之间安全的传输信 息。(将信息进行封装&#xff0c;以 JSON 的形式传递) JWT 有什么用? JWT 最常见的场景就是授权认证&#xff0c;一旦用户…

web3.0 爆红是炒作还是真有赚头?

前言 最近两年虽然疫情肆虐全球&#xff0c;虽然困得住人们的脚步&#xff0c;但是困不住科技的发展趋势&#xff0c;前有元宇宙&#xff0c;后有 web3.0&#xff0c;新的热点一个接着一个的出现&#xff0c;技术革新也是越来越快&#xff0c;之前只能在科幻电影、科幻小说中出…

SIFT算法

文章目录 1. SIFT算法简介1.1 SIFT特征检测步骤1.2 SIFT算法的特点 2. SIFT算法原理2.1 尺度空间2.1.1 多分辨率金字塔2.1.2 高斯金字塔2.1.3 高斯尺度空间&#xff08;使用不同的参数&#xff09; 2.2 DoG空间极值检测&#xff08;查找关键点&#xff09;2.3 删除不好的极值点…

二、Kafka生产与消费全流程

Kafka生产与消费全流程 Kafka是一款消息中间件&#xff0c;消息中间件本质就是收消息与发消息&#xff0c;所以这节课我们会从一条消息开始生产出发&#xff0c;去了解生产端的运行流程&#xff0c;然后简单的了解一下broker的存储流程&#xff0c;最后这条消息是如何被消费者…