C++:位图和布隆过滤器

一,位图

1.1 位图的概念

      究竟什么是位图呢??我们用一道问题来引入

问题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
这40亿个数中。【腾讯】


根据这个问题,我们可以用什么方法来解决呢???我们可以很迅速地想到一下方法:

方法1:排序+二分查找

方法2:将整数丢进哈希表或者红黑树中

          看似好像都能解决这个问题,但是我们来审视一下这个问题的关键——内存

         1G=1024MB=1024*1024KB=1024*1024*1024Byte 约等于10亿Byte,也就是说40亿个数大约需要16G内存!!没有办法将数据一次性放到内存去处理。

  (1)分析方法1:如果我们将数据分在不同的文件里,我们可以用归并排序去完成文件之间的排序,但是无法使用二分查找法,因为没有办法通过下标去直接访问元素!!!

  (2)分析方法2:问题就是所需要的内存太大了,光是数据就16G了,更何况红黑树和哈希表的底层的封装还需要一定的损耗,我们如果要使用的话也只能是一部分一部分丢进容器,然后再删掉丢下一部分,这样一方面的问题是我们其实在一开始丢进容器的时候就可以去做比对了,没有必要丢到容器里,另一方面的问题就是不适应需要多次查找比对的场景。

      因此上面两种方法都是不太现实的,但是有一种数据结构可以帮助我们解决这个问题,那就是位图——通过哈希函数中的直接定址法确定整型在不在的模型(就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

    所以方法3:用位图去解决  

       数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:

       你可能会有这样的疑惑,为什么上面的排布是反着来的,即从7—>1,但其实这样才是符合我们的人为的认知的,比如1234,右边的是低位,左边才是高位。在计算机中具体是怎么排布的我们不得而知,因为那属于内存层,是我们看不到的,我们能看到的只是计算机给我们展现的虚拟层,比如说我们的监视窗口和内存窗口,每个bit位都是左高右低的排布,符合我们人的一个惯性。  而我们的左移运算符 其实就是从低位->高位  而右移运算符 其实是从高位->低位。

 1.2 位图的实现

      首先,我们的STL容器中是有位图这个容器的,名字叫做bitset。

1.2.1 基本结构

template<size_t N> //非类型模板参数   N表示需要开的位图的大小
class bitset
{
public:

private:
	vector<char> _bits;
};

      我们需要去控制比特位,所以选择char作为我们的类型是最合适的,其中N是非类型模板参数,表明位图具体开多大。

1.2.2 构造函数

       我们具体应该开多大,自然是取决于我们的元素数量的,既然我们一个char有8个bit位,自然就可以表示8个整数,但是需要注意的一个原则是:宁可开多不可开少。因为假设是10,除以8之后余数会被干掉,因此我们最后一定还要加上一个1

bitset()
{
	_bits.resize(N / 8 + 1, 0); //只能开多不能开少
}

1.2.3 set

       set的作用,就是将某一位设置成1

void set(size_t x) //设置成1
{
	size_t i = x / 8;//计算x映射的位char在第i个数组的位置
	size_t j = x % 8;//计算x映射的位在这个char的第j个比特位
	//按位或   <<左移是低->高
	_bits[i] |= (1 << j);
}

size_t i = x / 8;//计算x映射的位char在第i个数组的位置
size_t j = x % 8;//计算x映射的位在这个char的第j个比特位

       接下来我们要判断的问题就是,如何将该位设置为1->利用位运算的性质 只要j对应的位置按位或上0……1……0 (1左移j位得到)即可

1.2.4 reset

         reset的作用,就是将某一位设置成0

void reset(size_t x)//设置成0
{
	size_t i = x / 8;//计算x映射的位char在第i个数组的位置
	size_t j = x % 8;//计算x映射的位在这个char的第j个比特位
	//按位与 
	_bits[i] &= ~(1 << j);
}

        接下来我们要判断的问题就是,如何将该位设置为0->利用位运算的性质 只要j对应的位置按位或上1……0……1 (1左移j位再取反得到)即可

1.2.5 test

    test的作用,就是判断某一位是否为1(即该整数在不在)

bool test(size_t x) //x在不在  判断这一位是0还是1
{
	size_t i = x / 8;//计算x映射的位char在第i个数组的位置
	size_t j = x % 8;//计算x映射的位在这个char的第j个比特位
	return _bits[i] & (1 << j);
}

      还要按位与上0……1……0,那么除了第j位 其他位都会是0 那么第j位如果也是0 结果就会是0,如果是1 那么结果就是非0.

1.2.6 位图开满空间的方法

bitset<-1> bs;//开满的方法
bitset<0xFFFFFFFF> bs;

       -1强转成size_t 类型的时候就是无符号整型的最大值,而0xFFFFFFFF则是16进制下的无符号整型最大值。

1.3 位图的应用

1. 快速查找某个数据是否在一个集合中

      一般适用于整型
2. 排序 + 去重

       可以将一堆元素都丢进位图中,如果对应位置原来为0的话,就变成1,如果原来位置原来为1的话,就不处理,然后我们再通过映射关系将元素还原回来,就完成了排序+去重
3. 求两个集合的交集、并集等

       这边有两种思路:

1、将两个集合分别放在两个位图中,分别完成排序和去重,然后再一个个去比对。

2、先将其中一个集合放进位图中,然后再通过另一个集合去判断,如果位图中为1,说明该数就是交集,但是为了防止集合出现重复数字,我们此时将该位置变成0(改进方法).

       两种方法都可以,但是第一种方法有两个问题,一个是空间的消耗太大,另一个就是无论这个集合多大,我们都需要将所有的位置都遍历完了才可以确定,因为我们并不清楚集合里元素的范围。 而第二种方法可以有效改进上面两个问题,不仅空间消耗减少,而且我们只需要判断集合2的元素是否在位图中出现过即可,肯定比遍历全部位置效率高。  另一方面由于集合2没有进行去重,所以可能会出现重复元素,但是通过我们的改进方法——即发现交集的时候,将原先位图的位置变成0就可以解决这个问题。  

      总结:如果数据量达到亿级别,可能用第一种比较合适,但是大多数情况下,第二种显然是更合适的!!

4.数据相对集中时的统计次数

     假设说我们已知数据只有可能出现‘a’->‘z’ 此时我们发现也就26种可能性,这个时候我们也不需要单独去封装一个位图,直接用一个整型就可以解决问题!!

      附上一道位图有关的OJ题

面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode)

class Solution {
public:
    bool isUnique(string astr) 
    {
       if(astr.size()>26)  return false;//鸽巢原理做优化
       //利用位图的思想
       int bitmap=0;
       for(auto ch:astr)
       {
        int i=ch-'a';//找到映射关系
        if((bitmap>>i)&1==1)  return false;//先判断该字符是否出现过 判断第i位是否是1
        //如果没出现过,将第i位的0修改为1
        bitmap|=(1<<i);
       }
       return true;
    }
};

5. 操作系统中磁盘块标记

1.4 位图的海量数据处理

1. 1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
      该问题的关键就是:我们位图模型只能判断有或者无,但是无法区分出现多少次。 

       这个时候我们就要从本质去分析,为什么位图只能判断有或者无呢??原因就是我们只有一个比特位来表示状态的有无,所以我们这个时候可以用两个比特位来表示当前的状态。但是我们如果直接用一个为位图来解决,那么计算逻辑也会随之改变。所以我们还是用之前的位图,但是为了表示多种状态,我们使用两个位图。 00表示无,01表示1次,11表示2次,10表示2次以上即可。

     根据上述的分析,我们构建出解决这一问题的类

template<size_t N> //非类型模板参数   N表示需要开的位图的大小
class twobitset //解决100亿数据判断有无  找到数据中不超过2次的整数  00-无  01一个  11两个  10两个以上
{
public:
	void set(size_t x) //设置成1
	{
		if (_bit1.test(x) == false && _bit2.test(x) == false)  _bit2.set(x);//00->01
		else if (_bit1.test(x) == false && _bit2.test(x) == true) _bit1.set(x);//01->11
		else if (_bit1.test(x) == true && _bit2.test(x) == true) _bit2.reset(x);//11->10
		//如果是11 不处理
	}

	void Print() //打印只出现一次或者两次的整数
	{
		for (size_t i = 0; i < N; ++i)
			if (_bit2.test(i) == true)
				cout << i << endl;
		cout << endl;
	}
private:
	bitset<N> _bit1;
	bitset<N> _bit2;

};

2、给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

    解决方案我在1.3中已经讲过了!!

1.5 位图的优缺点

优点:速度快、节省内存

缺点:只能映射整型,如浮点数、string等等不能存储映射  

       首先我们要思考,为什么位图无法存储这两种类型的映射,原因就在于其可以表现的形式太多种了,比如说对于无符号整型来说,最多也就2^32-1种可能, 但是对于浮点数和string来说,其组合之多导致无法像整型一样在位图中可以直接使用直接定址法。

       浮点数还好,但是string平时非常常用,比如用户名,所以这个时候当数据量极大的时候,也是一个问题,所以后来出现了一种结构解决了这种问题——>布隆过滤器

二、布隆过滤器

2.1 布隆过滤器的提出

      我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那
些已经存在的记录
。 如何快速查找呢?
 

1. 用哈希表存储用户记录,缺点:浪费空间
2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理
了。

3. 将哈希与位图结合,即布隆过滤器
 

2.2 布隆过滤器的概念

        布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存
在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也
可以节省大量的内存空间。

    怎么去理解概率型数据结构呢??

        首先可以确定的是,由于字符串的排列组合种类非常多,所以我们无论如何都无法做到通过直接定址法让每个字符串正好都对应一个位置……也就是说,我们利用字符串哈希函数在位图中存储大量字符串信息是必然会造成哈希冲突的,而布隆大佬的想法并不是完全解决哈希冲突,而是想降低哈希冲突的概率。  策略就是:既然一个字符串映射一个位置容易出现哈希冲突,那么一个字符串映射多个位置出现哈希冲突的概率就会降低!!!这是一种退而求其次的策略。

所以我们要使用多个类型的字符串哈希函数来帮助我们让一个字符串映射多个位置。具体有哪些常用的哈希函数可以参照下面的文章:字符串哈希函数算法

通过上图,我们可以确定布隆过滤器的两个特点:

1、不在是准确的,因为只要有一个位置没映射上,就是不在。

2、在是不准备的,存在误判,因为可能该字符串映射的位置恰好被前面的字符串给映射了

2.3 布隆过滤器的应用场景

 既然布隆过滤器只是降低哈希冲突而不是避免哈希冲突,那么他适合的场景如下:

1、能容忍误判的场景(客户端感知不到)

比如注册时,快速判断昵称是否被使用过。

2、虽然不能容忍误判,但是可以确定不在是准确的,只需要对在的场景进行特殊处理

比如说注册时,快速判断该电话号码是否被注册过。

       通过上图我们可以发现,布隆过滤器至少对不在的场景是判断准确的,所以我们可以不需要去数据库查询,只需要对在的结果进行查询和比对即可

       同时大家会发现,上图就充分展现了为什么该解决就叫做布隆过滤器,其实就是将大量的信息进行过滤,处理其中可以解决的问题,对于少部分不能解决的问题,再到数据库中去解决。

2.4 布隆过滤器实现的基本结构

  //想把类当函数使用 就要想到仿函数  
  //提供三个字符串哈希函数
struct BKDRHash
{
	size_t operator() (const string& s) //字符串转整型
	{
		size_t hash = 0;
		for (auto e : s)
		{
			hash += e;
			hash *= 31;
		}
		return hash;
	}
};
struct APHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (long i = 0; i < s.size(); i++)
		{
			size_t ch = s[i];
			if ((i & 1) == 0)  hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
			else               hash ^= (~((hash << 11) ^ ch ^ (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;
	}
};
//布隆过滤器  基本上解决的是字符串的问题 如果是整型的话 还是位图更好用   至于用多少个哈希函数 取决于实际的需求
//N表示最多会插入key的个数
template<size_t N,
size_t X=6,
class K=string,
class Hash1= BKDRHash,
class Hash2= APHash,
class Hash3= DJBHash> //K并不确定是什么   通过多种Hash函数去帮助我们映射不同的位置
class BloomFilter
{
public:

private:
	bitset<N*X> _bits;
};

       我们一共用了6个模版参数,首先布隆过滤器大部分情况下是用来解决字符串的问题,所以我们的K默然给的是string,而后面Hash1——Hash3都是字符串哈希函数,这样一个字符串可以映射三个位置。而N表示的是要插入多少个字符串,而关于X是什么,我们来看看下面这个文章。

详解布隆过滤器的原理,使用场景和注意事项 - 知乎

 所以X在这边可以理解为我们需要多开X倍的空间,来尽可能达到误判率和效率尽可能合理。

经过计算后如果我们的哈希函数是3个的话,大概需要多开4.3倍的空间比较合理。

2.5 布隆过滤器的插入

void set(const K& key)
{
	size_t len = N * X;
	size_t hash1 = Hash1()(key) % len;
	_bits.set(hash1);
	size_t hash2 = Hash2()(key) % len;
	_bits.set(hash2);
	size_t hash3 = Hash3()(key) % len;
	_bits.set(hash3);
	//cout << hash1 << " " << hash2 << " " << hash3 << endl << endl; //看看映射的位置
}

 2.6 布隆过滤器的查找

         布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中。

bool test(const K& key)
{
	size_t len = N * X;
	size_t hash1 = Hash1()(key) % len;
	if (_bits.test(hash1) == false) return false;
	size_t hash2 = Hash2()(key) % len;
	if (_bits.test(hash2) == false) return false;
	size_t hash3 = Hash3()(key) % len;
	if (_bits.test(hash3) == false) return false;
	
	return true;//在可能会有误判
}

注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可
能存在,因为有些哈希函数存在一定的误判。

2.7 布隆过滤器的删除

      首先我们可以确定的是,布隆过滤器是不太支持删除的,因为我们删了一个位置,可能会影响其他位置。

      一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。

     
      但是这样会存在三种缺陷:

1、无法确认元素是否真正在布隆过滤器中

        因为无论是位图还是布隆过滤器并不是传统意义上的真的存了相对应的元素在里面,而只能反 应具体在不在。所以我们很难保证说我们当前要删除的元素是否存在布隆过滤器中,如果要删除的元素不存在布隆过滤器中,布隆过滤器是检测不出来的,这样就整个乱套了。

2、容易存在计数回绕

      既然是计数,那么我们就可以根据情况去控制一个位置大约用几个比特位来计数,比如我们用4个比特位来计数,那么就有2^4 也就是16个字符串可以映射该位置,如果用8个比特位就是2^8 也就是256个字符串可以映射该位置。但是如果我们超出了这个范围(比如有257个字符串映射了该位置),可能就会出现计数溢出导致计数回绕。

3、无法去重

     使用引用计数后,如果一个字符串不小心同时插入了两次,那么对应位置的计数都会增加,这个时候当我们下次要删除的时候,也必须要删除两次,如果只删除一次,那么还是可以找得到。

     综合上面的三种缺陷,我们可以得到一个结论就是——布隆过滤器并不适应删除的场景,尤其是第一个缺陷最为严重,严重时会导致数据丢失!!!

2.8 布隆过滤器的优点

1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关

2. 哈希函数相互之间没有关系(可多次进行哈希切割),方便硬件并行运算

3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势

5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能

6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算(哈希切割)

2.9 布隆过滤器的缺点

1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:1,再
建立一个白名单,存储可能会误判的数据。2,对在的场景进行特殊处理
)

2. 不能获取元素本身

3. 一般情况下不能从布隆过滤器中删除元素

4. 如果采用计数方式删除,可能会存在计数回绕、删除不在的元素、无法去重等问题。


2.10 布隆过滤器的海量数据处理

1、给两个文件,分别有100亿个query(本质就是一个字符串,如sql语句),我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法

总结:

1,利用一个哈希函数进行哈希切分,根据具体情况将A和B分别切割成多个小文件。

2,用一个set来读取小文件

        如果都可以插入,那么就是以下两种情况:(1)单个文件比较小,所以可以插入进去。(2)单个文件比较大,但是其中存在某个大量重复的query(此时换个哈希函数切割也是没用的,切不动)。但是由于set不支持键值冗余,所以都不会正确插入进去。 此时直接把另一个文件拿过来比对即可。

       如果插入的过程中出现抛异常(内存不够才会抛异常),那么就是以下情况:单个文件比较大,并且存在大量不同的query,此时我们就再换一个哈希函数进行切割,将该文件切得更小一点。

2.11 布隆过滤器的误判率测试代码

void test_bloomfilter2()
{
	srand((unsigned int)time(0));
	const size_t N = 10000;
	BloomFilter<N> bf;

	vector<string> v1;
	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 + to_string(i));
	}

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

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

	// 不相似字符串集
	vector<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 += 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;

当X为4的时候

 当X为5的时候

当X为6的时候

 当X是7的时候

 我们发现6的时候进步是比较明显的,所以最后我选择了6

2.12 哈希切割

          给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
    

如果是top-K的话,建个小堆去不断比对即可,最后剩下的就是top-K。

2.13 布隆过滤器的整体代码实现

  //想把类当函数使用 就要想到仿函数  
  //提供三个字符串哈希函数
struct BKDRHash
{
	size_t operator() (const string& s) //字符串转整型
	{
		size_t hash = 0;
		for (auto e : s)
		{
			hash += e;
			hash *= 31;
		}
		return hash;
	}
};
struct APHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (long i = 0; i < s.size(); i++)
		{
			size_t ch = s[i];
			if ((i & 1) == 0)  hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
			else               hash ^= (~((hash << 11) ^ ch ^ (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;
	}
};


//布隆过滤器  基本上解决的是字符串的问题 如果是整型的话 还是位图更好用   至于用多少个哈希函数 取决于实际的需求
//N表示最多会插入key的个数
template<size_t N,
	size_t X=6,
	class K=string,
	class Hash1= BKDRHash,
	class Hash2= APHash,
	class Hash3= DJBHash> //K并不确定是什么   通过多种Hash函数去帮助我们映射不同的位置
//6是测试用测试代码测试出来的
class BloomFilter
{
public:
	void set(const K& key)
	{
		size_t len = N * X;
		size_t hash1 = Hash1()(key) % len;
		_bits.set(hash1);
		size_t hash2 = Hash2()(key) % len;
		_bits.set(hash2);
		size_t hash3 = Hash3()(key) % len;
		_bits.set(hash3);
		//cout << hash1 << " " << hash2 << " " << hash3 << endl << endl; //看看映射的位置
	}

	bool test(const K& key)
	{
		size_t len = N * X;
		size_t hash1 = Hash1()(key) % len;
		if (_bits.test(hash1) == false) return false;
		size_t hash2 = Hash2()(key) % len;
		if (_bits.test(hash2) == false) return false;
		size_t hash3 = Hash3()(key) % len;
		if (_bits.test(hash3) == false) return false;
		
		return true;//在可能会有误判
	}

private:
	bitset<N*X> _bits;
};


      

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java——嵌套(二)

目录 一&#xff1a;方法的重写&#xff08;覆盖/覆写&#xff09; 1. 方法的重写的意义&#xff1a; 2. 重写&#xff08;overide&#xff09; 3. 案例 二&#xff1a;继承中构造方法的调用 1. 子类的构造方法会默认调用父类的构造方法&#xff1b; 2. super 关键字调用…

基于MPPT最大功率跟踪和SVPWM的光伏三相并网逆变器simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于MPPT最大功率跟踪和SVPWM的光伏三相并网逆变器simulink建模与仿真。包括PV模块&#xff0c;MPPT模块&#xff0c;SVPWM模块&#xff0c;电网模块等。 2.系统仿真结果 1不…

JavaScript异步编程——04-同源和跨域

同源和跨域 同源 同源策略是浏览器的一种安全策略&#xff0c;所谓同源是指&#xff0c;域名&#xff0c;协议&#xff0c;端口完全相同。 跨域问题的解决方案 从我自己的网站访问别人网站的内容&#xff0c;就叫跨域。 出于安全性考虑&#xff0c;浏览器不允许ajax跨域获取…

二总线,替代传统485总线通讯,主站设计

二总线通信设计专栏 《二总线&#xff0c;替代传统485总线通讯&#xff0c;选型及应用-CSDN博客》《二总线&#xff0c;替代传统485总线通讯&#xff0c;低成本直流载波方案实现及原理-CSDN博客》《二总线&#xff0c;替代传统485总线通讯&#xff0c;调试避坑指南之最大的电流…

基于控制工程的牛鞭效应simulink建模与仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 牛鞭效应”对供应链性能和绩效产生了严重的影响。基于控制理论建立了多级线性供应链的模型&#xff0c;分别利用噪声带宽和Matlab&#xff0f;Simulink对一个可扩…

【快捷部署】024_Hive(3.1.3)

&#x1f4e3;【快捷部署系列】024期信息 编号选型版本操作系统部署形式部署模式复检时间024Hive3.1.3Ubuntu 20.04tar包单机2024-05-07 一、快捷部署 #!/bin/bash ################################################################################# # 作者&#xff1a;cx…

竞赛 基于深度学习的人脸性别年龄识别 - 图像识别 opencv

文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 毕业设计…

CopyClip for Mac - 高效复制粘贴,轻松管理剪贴板

CopyClip for Mac&#xff0c;一款专为Mac用户打造的剪贴板管理工具&#xff0c;让你在复制粘贴的日常任务中&#xff0c;享受到前所未有的高效与便捷。 它常驻在菜单栏中&#xff0c;时刻准备为你服务。一旦你复制了内容&#xff0c;CopyClip就会自动将其保存至历史记录中&…

使用ffmpeg对视频进行转码(支持浏览器播放)

在开发中&#xff0c;算法保存的mp4视频文件通过路径打开该视频发现视频播放不了&#xff0c;需要转码进行播放。使用java代码进行转码。代码如下&#xff0c;inputFilePath是转之前的视频路径&#xff0c;outputFilePath是转之后的视频路径。ffmpeg命令中libx264也可以改为其它…

经验浅谈!伦敦银如何交易?

近期&#xff0c;伦敦银价格出现很强的上涨&#xff0c;这促使一些新手投资者进入了市场&#xff0c;但由于缺乏经验&#xff0c;他们不知道该怎么在市场中交易&#xff0c;下面我们就从宏观上介绍一些方法&#xff0c;来讨论一下伦敦银如何交易。 首先我们要知道&#xff0c;要…

vue3创建响应式数据ref和reactive的区别

reactive和ref在Vue.js中都是用于创建响应式数据的&#xff0c;但它们之间存在一些区别 定义数据类型不同。ref主要用于定义基本数据类型&#xff0c;如字符串、数字、布尔值等&#xff1b;reactive主要用于定义对象&#xff08;或数组&#xff09;类型的数据&#xff0c;但re…

【uniapp】阿里云OSS上传 [视频上传]

引用uniapp插件市场的插件,使用的是视频上传 &#xff08;阿里云 oss上传&#xff09; 我只使用了H5和App端&#xff0c;需要后端配置跨域 yk-authpup详情请参考 》》【用户告知权限申请的目的】 【插件市场】阿里云存储OSS前端直接上传(全端通用) - 前端JASON <template>…

scikit-learn多因子线性回归预测房价

1.首先是单因子线性回归预测房价 import numpy as np import pandas as pd from matplotlib import pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score# 1.读取csa房屋数据 path D:/pythonDATA/us…

【前端学习——css模块化】

https://segmentfault.com/a/1190000039772466#item-2 CSS 命名方法论 为选择器增加冗长的前缀或后缀&#xff0c;手写命名前缀后缀的方式来生成全局唯一的命名 很麻烦不方便 CSS Modules 在外部管理 CSS&#xff0c;然后将类名映射到组件内部&#xff0c;他会为每个 class 都…

[C++]哈希应用-布隆过滤器快速入门

布隆过滤器 布隆过滤器&#xff08;Bloom Filter&#xff09;是一个由布隆在1970年提出的概率型数据结构&#xff0c;它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器的主要特点是高效的插入和查询&#xff0c;可以用于检索一个元素是否在一个集合中。 原理…

box-decoration-break 使用介绍

box-decoration-break属性的使用 一、定义 box-decoration-break是CSS片段模块&#xff08;CSS Fragmentation Module Level 3&#xff09;中的一个属性&#xff0c;主要用于指定背景&#xff08;background&#xff09;、内边距&#xff08;padding&#xff09;、边框&#…

K邻近算法

简介 介绍了非常简单的算法&#xff1a;K邻近算法&#xff0c;即KNN。 基本介绍 K-近邻算法&#xff08;K-Nearest Neighbors&#xff0c;简称KNN&#xff09;是一种基本且广泛应用的监督学习算法&#xff0c;主要用于分类和回归任务。 工作原理非常简答直观&#xff1a;所谓…

nginx: [emerg] invalid number of arguments in “alias“ directive in D:\nginx

问题背景 最近在配置一个nginx的配置&#xff0c;但是reload的时候遇到了以下报错 D:\nginx>nginx -s reload nginx: [emerg] invalid number of arguments in "alias" directive in D:\nginx/conf /nginx.conf:113 解决方案 关于“alias”指令中的参数数量错误…

十二届蓝桥杯Python组1月中/高级试题 第一题

** 十二届蓝桥杯Python组1月中/高级试题 第一题 第一题&#xff08;难度系数2&#xff0c;18 个计分点&#xff09; 编程实现&#xff1a; 输入一个字符串&#xff0c;输出这个字符串的最后一个字符。 输入描述&#xff1a;输入一个字符串 输出描述&#xff1a;输出这个字符串…

OpenCV单窗口并排显示多张图片

OpenCV单窗口并排显示多张图片 效果代码 PS&#xff1a;本例的代码适合图片的宽度和高度都相同。 效果 原始三张图片&#xff1a; 合并显示&#xff1a; 代码 import cv2 import numpy as npdef opencv_multi_img():# 读取图片img1 cv2.imread(saw_1.jpeg)img2 cv2.im…
最新文章