【C++笔记】位图和布隆过滤器

【C++笔记】位图和布隆过滤器

在这里插入图片描述

🔥个人主页大白的编程日记

🔥专栏C++笔记


文章目录

  • 【C++笔记】位图和布隆过滤器
    • 前言
    • 一. 位图
      • 1.1 位图相关面试题
      • 1.2 C++库中的位图
      • 1.3位图优缺点
      • 1.4位图相关考察题目
    • 二.布隆过滤器
      • 2.1 什么是布隆过滤器
      • 2.2 布隆过滤器器误判率推导
      • 2.3 布隆过滤器代码实现
      • 2.4 布隆过滤器删除问题
      • 2.5 布隆过滤器的应用
    • 三.海量数据处理问题
      • 3.1题目一
      • 3.2 题目二
      • 3.3 题目三
    • 后言

前言

哈喽,各位小伙伴大家好!上期我们讲了二叉搜索树。今天我们来讲一下位图和布隆过滤器。话不多说,我们进入正题!向大厂冲锋
在这里插入图片描述

一. 位图

1.1 位图相关面试题

给40亿个不重复的无符号整数,没排过序。给⼀个无符号整数,如何快速判断一个数是否在这40亿个数中。(本题为腾讯/百度等公司出过的⼀个面试题)

  • 解题思路1
    暴力遍历,时间复杂度O(N),太慢。

  • 解题思路2
    排序+二分查找。
    时间复杂度消耗 O(N*logN) + O(logN)
    深入分析:解题思路2是否可行。

  • 解题思路3
    数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。那么我们设计一个用位表数据是否存在的数据结构,这个数据结构就叫位图。

1.2 位图的设计及实现
位图本质是⼀个直接定址法的哈希表,每个整型值映射⼀个bit位,位图提供控制这个bit的相关接口。

  • bit映射关系
    实现中需要注意的是,C/C++没有对应位的类型,只能看int/char这样整形类型,我们再通过位运算去控制对应的比特位。比如我们数据存到vector中,相当于每个int值映射对应的32个值,比如第⼀个整形映射0-31对应的位,第⼆个整形映射32-63对应的位,后面的以此类推,那么来了⼀个整形值x,
    i = x / 32;j = x % 32;计算出x映射的值在vector的第i个整形数据的第j位。

  • 数据范围
    解决给40亿个不重复的无符号整数,查找⼀个数据的问题,我们要给位图开2^32个位,注意不能开40 亿个位,因为映射是按大小映射的,我们要按数据大小范围开空间,范围是是0-2^32-1,所以需要开
    2^32个位。然后从文件中依次读取每个set到位图中,然后就可以快速判断⼀个值是否在这40亿个数中
    了。

  • 代码实现
    这里注意我们映射的数据范围/32可能不整除,所以多开一个int即可。其他都是简单的使用位运算的标记和检测接口。

template<size_t N>
class Bit_Set
{
public:Bit_Set(){bitmap.resize(N / 32 + 1);//多开一个防止不整除}//将第N个数据标记为1void set(size_t n){int i = n / 32;int j = n  % 32;bitmap[i] |= (1 << j);}//将第N个数据标记为0void reset(size_t n){int i = n / 32;int j = n  % 32;bitmap[i] &= (~(1 << j));}//查看第N个数据是否标记bool test(size_t n){int i = n / 32;int j = n  % 32;return bitmap[i] & (1 << j);}
private:std::vector<int> bitmap;//封装vector
};

这里纠正一下左移的问题。

1.2 C++库中的位图

库里的位图
在这里插入图片描述

可以看到核心接口还是set/reset/和test,当然后面还实现了⼀些其他接口,如to_string将位图按位转成01字符串,再包括operator[]等⽀持像数组⼀样控制⼀个位的实现。

但是库的bitset有个小bug就是底层使用的是静态数组。
如果数据范围太大就会越界

1.3位图优缺点

优点:增删查改快,节省空间
缺点:只适用于整形。浮点数字符串这些都不行

1.4位图相关考察题目

  • 给定100亿个整数,设计算法找到只出现一次的整数?
    虽然是100亿个数,但是还是按范围开空间,所以还是开2^32个位,跟前面的题目是⼀样的。

  • ⼀个文件有100亿个整数,1G内存,设计算法找到出现次数不超过2次的所有整数
    这题目和上题一样只不过需要四种状态,两个bitset也能表示四种状态修改一下即可。

template<size_t N>
class TwoBitSet
{
public:void Set(size_t n){bool bit1 = b1.test(n);bool bit2 = b2.test(n);if (!bit1 && !bit2)//00->01{b2.set(n);}else if (!bit1&&bit2)//01->10{b1.set(n);b2.reset(n);}else//10->11 11->11{b1.set(n);b2.set(n);}}int get_count(size_t n)//检测出现次数{bool bit1 = b1.test(n);bool bit2 = b2.test(n);if (!bit1 && !bit2){return 0;//没出现}else if (!bit1 && bit2){return 1;//出现1次}else if (bit1 && !bit2){return 2;//出现2次}else{return 3;//出现3次及以上}}
private:Bit_Set<N> b1;Bit_Set<N> b2;//封装
};

这样这两道题都能解决了。

  • 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
    把数据读出来,分别放到两个位图,依次遍历,同时在两个位图的值都在就是交集。

二.布隆过滤器

2.1 什么是布隆过滤器

有⼀些场景下面,有大量数据需要判断是否存在,而这些数据不是整形,那么位图就不能使用了,使用红黑树/哈希表等内存空间可能不够。这些场景就需要布隆过滤器来解决。

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

  • 哈希映射
    布隆过滤器的思路就是把key先映射转成哈希整型值,再映射⼀个位,如果只映射⼀个位的话,冲突率会比较多,所以可以通过多个哈希函数映射多个位,降低冲突率。

  • 判断
    布隆过滤器这里跟哈希表不⼀样,它无法解决哈希冲突的,因为他压根就不存储这个值,只标记映射的位。它的思路是尽可能降低哈希冲突。判断⼀个值key在是不准确的,判断⼀个值key不在是准确的。
    =

2.2 布隆过滤器器误判率推导

那我们的哈希函数给几个比较好呢?空间又该开几个bit呢?


所以后来有人推导了一下:

推导过程: 说明:这个比较复杂,涉及概率论、极限、对数运算,求导函数等知识,有兴趣且数学功底比较好的可以看细看⼀下,其他同学记⼀下结论即可!

  • 推导
    假设
    m:布隆过滤器的bit长度。
    n:插入过滤器的元素个数。
    k:哈希函数的个数。
    布隆过滤器哈希函数等条件下某个位设置为1的概率: 1 / m 1/m 1/m
    布隆过滤器哈希函数等条件下某个位设置不为1的概率: 1 − 1 / m 1-1/m 11/m
    在经过k次哈希后,某个位置依旧不为1的概率: ( 1 − 1 / m ) k (1 − 1/m)^k (11/m)k
    根据极限公式: lim ⁡ m → ∞ ( 1 − 1 m ) − m = e \lim_{m \to \infty} \left( 1 - \frac{1}{m} \right)^{-m} = e mlim(1m1)m=e ( 1 − 1 m ) k = ( ( 1 − 1 m ) m ) k m ≈ e − k m \left(1 - \frac{1}{m}\right)^k = \left(\left(1 - \frac{1}{m}\right)^m\right)^{\frac{k}{m}} \approx e^{-\frac{k}{m}} (1m1)k=((1m1)m)mkemk
    添加n个元素某个位置不置为1的概率: ( 1 − 1 m ) k n ≈ e − k n m \left(1 - \frac{1}{m}\right)^{kn} \approx e^{-\frac{kn}{m}} (1m1)knemkn
    添加n个元素某个位置置为1的概率: 1 − ( 1 − 1 m ) k n ≈ 1 − e − k n m 1 - \left(1 - \frac{1}{m}\right)^{kn} \approx 1 - e^{-\frac{kn}{m}} 1(1m1)kn1emkn
    查询⼀个元素,k次hash后误判的概率为都命中1的概率: ( 1 − ( 1 − 1 m ) k n ) k ≈ ( 1 − e − k n m ) k \left(1 - \left(1 - \frac{1}{m}\right)^{kn}\right)^k \approx \left(1 - e^{-\frac{kn}{m}}\right)^k (1(1m1)kn)k(1emkn)k
  • 结论
    结论:
    布隆过滤器的误判率为: f ( k ) = ( 1 − e − k n m ) k f(k) = \left(1 - e^{-\frac{kn}{m}}\right)^k f(k)=(1emkn)k f ( k ) = ( 1 − 1 e k n m ) k f(k) = \left(1 - \frac{1}{e^{\frac{kn}{m}}}\right)^k f(k)=(1emkn1)k
    由误判率公式可知,在k⼀定的情况下,当n增加时,误判率增加,m增加时,误判率减少。
    在m和n⼀定,在对误判率公式求导,误判率尽可能小的情况下,可以得到hash函数个数:
    k = m n ln ⁡ 2 k = \frac{m}{n} \ln 2 k=nmln2时误判率最低。
    期望的误判率p和插⼊数据个数n确定的情况下,再把上面的公式带入误判率公式可以得到,通过期望的误判率和插入数据个数n得到bit长度: m = − n ⋅ ln ⁡ p ( ln ⁡ 2 ) 2 m = -\frac{n \cdot \ln p}{(\ln 2)^2} m=(ln2)2nlnp

以上两个公式的推导过程尤其复杂,这里放两篇博客链接布隆过滤器(Bloom Filter)- 原理、实现和推导和[布隆过滤器BloomFilter] 举例说明+证明推导。

2.3 布隆过滤器代码实现

首先我们需要几个哈希函数,这里我们就简单的以字符串的哈希函数为例。
各种字符串Hash函数-clq-博客
这里有几个比较好的字符串哈希函数。

struct HashFuncBKDR
{/// @detail 本 算法由于在Brian Kernighan与Dennis Ritchie的《The C //Programming Language》// ⼀书被展⽰⽽得 名,是⼀种简单快捷的hash算法,也是Java⽬前采⽤的字符串的Hash//算法累乘因⼦为31。size_t operator()(const std::string& s){size_t hash = 0;for (auto ch : s){hash *= 31;hash += ch;}return hash;}
};
struct HashFuncAP
{// 由Arash Partow发明的⼀种hash算法。 size_t operator()(const std::string& s){size_t hash = 0;for (size_t 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 HashFuncDJB
{// 由Daniel J. Bernstein教授发明的⼀种hash算法。 size_t operator()(const std::string& s){size_t hash = 5381;for (auto ch : s){hash = hash * 33 ^ ch;}return hash;}
};
template<size_t N>
class Bit_Set
{
public:Bit_Set(){bitmap.resize(N / 32 + 1);}//将第N个数据标记为1void set(size_t n){int i = n / 32;int j = n % 32;bitmap[i] |= (1 << j);}//将第N个数据标记为0void reset(size_t n){int i = n / 32;int j = n % 32;bitmap[i] &= (~(1 << j));}//查看第N个数据是否标记bool test(size_t n){int i = n / 32;int j = n % 32;return bitmap[i] & (1 << j);}
private:std::vector<int> bitmap;
};
template<size_t N, size_t X=6,class K=string,class Hash1= HashFuncBKDR,class Hash2= HashFuncAP,class Hash3= HashFuncDJB>
class Bloom
{
public:void Set(const K& key){size_t b1 = Hash1()(key) % M;size_t b2 = Hash2()(key) % M;size_t b3 = Hash3()(key) % M;//三个哈希函数映射的值b.set(b1);b.set(b2);b.set(b3);//标记}bool Test(const K& key){size_t b1 = Hash1()(key) % M;size_t b2 = Hash2()(key) % M;size_t b3 = Hash3()(key) % M;//三个哈希函数映射的值if (!b.test(b1))//只要有一个没标记就返回0说明不在{return 0;}if (!b.test(b2))//只要有一个没标记就返回0说明不在{return 0;}if (!b.test(b3))//只要有一个没标记就返回0说明不在{return 0;}return true;//可能存在误判}// 获取公式计算出的误判率double getFalseProbability(){double p = pow((1.0 - pow(2.71, -3.0 / X)), 3.0);return p;}
private:static const size_t M = X * N;Bit_Set<M> b;
};
  • 测试
void Test()
{qcj::Bloom<10> b;b.Set("孙悟空");b.Set("猪八戒");b.Set("唐僧");cout << b.Test("孙悟空") << endl;;cout << b.Test("猪八戒") << endl;;cout << b.Test("唐僧") << endl;cout << b.Test("沙僧") << endl;;cout << b.Test("猪八戒1") << endl;;
}

void TestBloomFilter2()
{srand(time(0));const size_t N = 100000;/*qcj::Bloom<N> bf;*/qcj::Bloom<N, 3> bf;/*qcj::Bloom<N, 10> bf;*/std::vector<std::string> v1;std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";//std::string url = "https://www.baidu.com/s?ie=utf8&f=8&rsv_bp=1&rsv_idx=1&tn=65081411_1_oem_dg&wd=ln2&fenlei=256&rsv_pq=0x8d9962630072789f & rsv_t = ceda1rulSdBxDLjBdX4484KaopD % 2BzBFgV1uZn4271RV0PonRFJm0i5xAJ % 2FDo & rqlang = en & rsv_enter = 1 & rsv_dl = ib & rsv_sug3 = 3 & rsv_sug1 = 2 & rsv_sug7 = 100 & rsv_sug2 =0 & rsv_btype = i & inputT = 330 & rsv_sug4 = 2535";//std::string url = "猪⼋戒";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是相似字符串集(前缀⼀样),但是后缀不⼀样v1.clear();for (size_t i = 0; i < N; ++i){std::string urlstr = url;urlstr += std::to_string(9999999 + i);v1.push_back(urlstr);}size_t n2 = 0;for (auto& str : v1){if (bf.Test(str)) // 误判{++n2;}}cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;// 不相似字符串集 前缀后缀都不⼀样v1.clear();for (size_t i = 0; i < N; ++i){//string url = "zhihu.com";string url = "孙悟空";url += std::to_string(i + rand());v1.push_back(url);}size_t n3 = 0;for (auto& str : v1){if (bf.Test(str)){++n3;}}cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;cout << "公式计算出的误判率:" << bf.getFalseProbability() << endl;
}

这里我们用相似字符串,不相似字符串,和公式计算的误判率对比。


可以发现这里我们公式算的还是非常的准的。

2.4 布隆过滤器删除问题

布隆过滤器默认是不支持删除的,因为比如"猪八戒“和”孙悟空“都映射在布隆过滤器中,他们映射的位有⼀个位是共同映射的(冲突的),如果我们把孙悟空删掉,那么再去查找“猪八戒”会查找不到,因为那么“猪八戒”间接被删掉了。

所以删除一个值可能会影响另外一个值。

  • 解决方案一
    可以考虑计数标记的方式,⼀个位置用多个位标记,记录映射这个位的计数值,删除时,仅仅减减计数,那么就可以某种程度⽀持删除。但是这个方案也有缺陷,如果⼀个值不在布隆过滤器中,我们去删除,减减了映射位的计数,那么会影响已存在的值,也就是说,⼀个确定存在的值,可能会变成不存在,这里就很坑。

  • 解决方案二
    当然也有⼈提出,我们可以考虑计数方式支持删除,但是定期重建⼀
    下布隆过滤器,这样也是⼀种思路。

所以严格来说布隆过滤器不支持删除,因为会进一步导致误判。但是可以结合实际场景和需求进行挑选删除的解决方案。

2.5 布隆过滤器的应用

首先我们分析⼀下布隆过滤器的优缺点:
优点:效率高,节省空间,相比位图,可以适⽤于各种类型的标记过滤
缺点:存在误判(在是不准确的,不在是准确的),不好支持删除
布隆过滤器在实际中的⼀些应用:

  • 爬虫系统中URL去重:
    在爬虫系统中,为了避免重复爬取相同的URL,可以使用布隆过滤器来进行URL去重。爬取到的URL可以通过布隆过滤器进行判断,已经存在的URL则可以直接忽略,避免重复的网络请求和数据处理。

  • 垃圾邮件过滤:
    在垃圾邮件过滤系统中,布隆过滤器可以用来判断邮件是否是垃圾邮件。系统可以将已知的垃圾邮件的特征信息存储在布隆过滤器中,当新的邮件到达时,可以通过布隆过滤器快速判断是否为垃圾邮件,从而提高过滤的效率。

  • 预防缓存穿透
    在分布式缓存系统中,布隆过滤器可以用来解决缓存穿透的问题。缓存穿透是指恶意用户请求⼀个不存在的数据,导致请求直接访问数据库,造成数据库压力过大。布隆过滤器可以先判断请求的数据是否存在于布隆过滤器中,如果不存在,直接返回不存在,避免对数据库的无效查询。

  • 对数据库查询提效
    在数据库中,布隆过滤器可以⽤来加速查询操作。例如:⼀个app要快速判断⼀个电话号码是否注册过,可以使用布隆过滤器来判断⼀个用户电话号码是否存在于表中,如果不存在,可以直接返回不存在,避免对数据库进行无用的查询操作。如果在,再去数据库查询进行二次确认。

三.海量数据处理问题

3.1题目一

  • 100亿个整数里面求最大的前100个。
    这里我们建一个100的小堆。在把100亿个整数比堆顶元素大的进堆即可。
    剩下的100堆元素就是最大的100个数。
    具体细节看这篇博文:Top_K问题

3.2 题目二

  • 给两个文件,分别有100亿个query(字符串),我们只有1G内存,如何找到两个文件交集
    分析:假设平均每个query字符串50byte,100亿个query就是5000亿byte,约等于500G(1G 约等于10亿多Byte)
    哈希表/红黑树等数据结构肯定是无能为力的。
  • 解决方案1
    这个首先可以用布隆过滤器解决,⼀个文件中的query放进布隆过滤器,另⼀个文件依次查找,在的就是交集,问题就是找到交集不够准确,因为在的值可能因为哈希冲突导致误判的,但是交集一定被找到了。
    所以这种方法还是难以满足我们的需求。
  • 解决方案2
    • 哈希切分,首先内存的访问速度远大于硬盘,大文件放到内存搞不定,那么我们可以考虑切分为小文件,再放进内存处理。
    • 但是不要平均切分,因为平均切分以后,每个小文件都需要依次暴力处理,效率还是太低了。
    • 可以利用哈希切分,依次读取文件中query,i = HashFunc(query)%N,N为准备切分多少分小文件,N取决于切成多少份,内存能放下,query放进第i号小文件,这样A和B中相同的query算出的hash值i是⼀样的,相同的query就进入的编号相同的小文件就可以编号相同的我文件直接找交集,不用交叉找,效率就提升了。
    • 本质是相同的query在哈希切分过程中,一定进入的同一个小问件Ai和Bi,不可能出现A中的的query进入Ai,但是B中的相同query进入了和Bj的情况,所以对Ai和Bi进行求交集即可,不需要Ai和Bj求交集。(本段表述中i和j是不同的整数)
    • 哈希切分的问题就是每个小文件不是均匀切分的,可能会导致某个小文件很大内存放不下。我们细细分析⼀下某个小文件很大有两种情况:
    1.这个小文件中大部分是同⼀个query。
    2.这个小文件是有很多的不同query构成,本质是这些query冲突了。针对情况1,其实放到内存的set中是可以放下的,因为set是去重的。针对情况2,需要换个哈希函数继续⼆次哈希切分。所以本体我们遇到大于1G小文件,可以继续读到set中找交集,若set insert时抛出了异常(set插⼊数据抛异常只可能是申请内存失败了,不会有其他情况),那么就说明内存放不下是情况2,换个哈希函进行⼆次哈希切分后再对应找交集。

3.3 题目三

  • 给⼀个超过100G大小的log file, log中存着ip地址, 设计算法找到出现次数最
    多的ip地址?查找出现次数前10的ip地址
    本题的思路跟上题完全类似,依次读取文件A中query,i = HashFunc(query)%500,query放进Ai号小文件,然后依次⽤map<string, int>对每个Ai小文件统计ip次数,同时求出现次数最多的ip或者topkip。本质是相同的ip在哈希切分过程中,⼀定进⼊的同⼀个小文件Ai,不可能出现同一个ip进入Ai和Aj的情况,所以对Ai进行统计次数就是准确的ip次数。

后言

这就是位图和布隆过滤器。大家自己好好消化!今天就分享到这!感谢各位的耐心垂阅!咱们下期见!拜拜~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小迪安全第四十二天笔记 简单的mysql注入 mysql的基础知识 用户管理数据库模式 mysql 写入与读取 跨库查询

前言 之前的安全开发我们学习了 php联动数据库的模式 &#xff0c;这个模式是现在常用的模式 这一节来学习 如何 进行数据库的注入和数据库相关知识 1、了解数据库的结构 我们使用 navicate连接数据库之后看一下 一共四层结构 库 》表》字段》数据 这个层级关系…

如何估算自然对流传热系数

介绍 一般来说&#xff0c;对流可以定义为通过加热流体&#xff08;例如空气或水&#xff09;的运动来传递热量的过程。 自然对流&#xff08;对流的一种特定类型&#xff09;可以定义为流体在重力作用下由于较热因此密度较小的物质上升&#xff0c;而较冷且密度较大的物质下…

阿里云服务器(centos7.6)部署前后端分离项目(MAC环境)

Jdk17安装部署 下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/ 选择自己需要的jdk版本进行下载。 通过mac终端scp命令上传下载好的jdk17到服务器的/usr/local目录下 scp -r Downloads/jdk-17.0.13_linux-x64_bin.tar.gz 用户名服务器ip地址:/us…

ipad项目 蓝湖宽度

ipad项目 横屏状态时 蓝湖宽度设置930px media screen and (orientation: portrait) {/* 竖屏时的样式 */ } media screen and (orientation: landscape) {/* 默认是 横屏时的样式 */ }

【Linux——实现一个简易shell】

黑暗中的我们都没有说话&#xff0c;你只想回家&#xff0c;不想你回家............................................................... 文章目录 前言 一、【shell工作过程】 二、【命令行参数】 2.1、【获取命令行参数】 1、【输出命令行提示符】 2、【输入命令行参数】 2…

理解Linux的select、poll 和 epoll:从原理到应用场景

I/O 多路复用并不是什么新东西&#xff0c;select 早在 1983 年就出现了&#xff0c;poll 在 1997 年&#xff0c;epoll 是 2002 年的产物。面试题总爱问“多路复用多厉害&#xff1f;”其实它就是把轮询的锅甩给了操作系统&#xff0c;而操作系统不过是用 CPU 指令帮你完成事件…

阅读方法论

选择固有缺陷,选项是对比出来的

关于函数式接口和编程的解析和案例实战

文章目录 匿名内部类“匿名”在哪里 函数式编程lambda表达式的条件Supplier使用示例 ConsumeracceptandThen使用场景 FunctionalBiFunctionalTriFunctional 匿名内部类 匿名内部类的学习和使用是实现lambda表达式和函数式编程的基础。是想一下&#xff0c;我们在使用接口中的方…

ChatGPT 网络安全秘籍(二)

第三章&#xff1a;代码分析和安全开发 这一章深入探讨软件开发的复杂过程&#xff0c;关注当今数字世界中的一个关键问题&#xff1a;确保软件系统的安全。随着技术的不断复杂和威胁的不断演变&#xff0c;采用融合了安全考虑的安全软件开发生命周期&#xff08;SSDLC&#x…

学习笔记044——HashMap源码学习2

文章目录 1、HasMap 底层实现2、HashMap 加载顺序 1、HasMap 底层实现 JDK 1.8 HashMap 底层设计涉及到三种不同的数据结构&#xff0c;分别是数组、链表、红黑树。 1、基本的存储是数组&#xff0c;根据 key 值求出一个数组下标&#xff0c;将元素&#xff08;key-value&am…

计算机网络常见面试题总结(上)

计算机网络基础 网络分层模型 OSI 七层模型是什么&#xff1f;每一层的作用是什么&#xff1f; OSI 七层模型 是国际标准化组织提出的一个网络分层模型&#xff0c;其大体结构以及每一层提供的功能如下图所示&#xff1a; 每一层都专注做一件事情&#xff0c;并且每一层都需…

用micropython 操作stm32f4单片机的定时器实现蜂鸣器驱动

import pyb import time # 初始化引脚和定时器通道作为PWM输出 # 注意&#xff1a;这里我们假设您使用的是支持PWM的引脚和定时器 # 在不同的MicroPython板上&#xff0c;支持的引脚和定时器可能不同 # 请查阅您的板的文档以确认正确的引脚和定时器 buzzer_pin pyb.Pin(PD15,…

前端框架Vue3项目实战(基于Vue3实现一个小相册)

下面是是对Vue3操作的一个项目实战 下面代码是html的基本骨架&#xff08;没有任何的功能&#xff09;&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>相册</title> <style&…

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-39

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…

群控系统服务端开发模式-应用开发-前端短信配置开发

一、添加视图 在根目录下src文件夹下views文件夹下param文件夹下sms文件夹下&#xff0c;新建index.vue&#xff0c;代码如下 <template><div class"app-container"><div class"filter-container" style"float:left;"><el…

极致性能:19个Vue 项目的优化手段

前言 在前端开发领域&#xff0c;Vue.js 广泛应用于各种类型的项目中。然而&#xff0c;随着项目规模的扩大和用户需求的增加&#xff0c;性能优化的重要性愈发凸显。优化不仅可以提升用户体验&#xff0c;还能显著减少资源消耗&#xff0c;提高应用的响应速度和稳定性。 本文…

基于Java Springboot个人记账之财来财往微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

【maven-5】Maven 项目构建的生命周期:深入理解与应用

1. 生命周期是什么 ​在Maven出现之前&#xff0c;项目构建的生命周期就已经存在&#xff0c;软件开发人员每天都在对项目进行清理&#xff0c;编译&#xff0c;测试及部署。虽然大家都在不停地做构建工作&#xff0c;但公司和公司间&#xff0c;项目和项目间&#xff0c;往往…

LLamafactory API部署与使用异步方式 API 调用优化大模型推理效率

文章目录 背景介绍第三方大模型API 介绍LLamafactory 部署API大模型 API 调用工具类项目开源 背景介绍 第三方大模型API 目前&#xff0c;市面上有许多第三方大模型 API 服务提供商&#xff0c;通过 API 接口向用户提供多样化的服务。这些平台不仅能提供更多类别和类型的模型…

【Python网络爬虫笔记】6- 网络爬虫中的Requests库

一、概述 Requests 是一个用 Python 语言编写的、简洁且功能强大的 HTTP 库。它允许开发者方便地发送各种 HTTP 请求&#xff0c;如 GET、POST、PUT、DELETE 等&#xff0c;并且可以轻松地处理请求的响应。这个库在 Python 生态系统中被广泛使用&#xff0c;无论是简单的网页数…