『 C++ - STL 』位图(BitMap)与布隆过滤器(Bloom Filter)

文章目录

    • 🧸 位图(BitMap)概念
    • 🧸 位图的实现
      • 🪅 总体框架
      • 🪅 位图的数据插入
        • 🧩 左移操作与右移操作的区别
      • 🪅 位图的数据删除
      • 🪅 位图的数据查找
      • 🪅 位图整体代码(供参考)
    • 🧸 布隆过滤器(Bloom Filter)概念
      • 🪅 哈希函数的个数及布隆过滤器的长度
    • 🧸 布隆过滤器的实现
      • 🪅 总体框架
      • 🪅 布隆过滤器的数据插入
      • 🪅 布隆过滤器的数据查找
      • 🪅 布隆过滤器的数据删除
      • 🪅 布隆过滤器整体代码(供参考)
      • 🪅 布隆过滤器的应用


🧸 位图(BitMap)概念

请添加图片描述

在之前的文章中提到了对于存储数据的数据结构分别为哈希表与红黑树;

STL当中底层为这两个数据结构所实现的容器分别为map,setunordered_map,unordered_set;

同时能知道这几个容器实际上是在内存当中存储数据的;

  • 腾讯曾经有一道面试题是这样的:

    存在40亿个不重复的无符号整数,其中这些数据并未排过序;给定一个无符号整数,如何快速判断这个数是否存在于这40亿个数中;

很显然,这个面试题问的是一个数据在不在的问题;

那么如何能够对数据进行判断在不在的问题?

  • 遍历数据逐个判断

    遍历数据逐个判断可以理解为一种暴力的解法,这种暴力的解法通过枚举的思路对数据逐个查询;

    但实际上该方法的时间复杂度为O(N)[由于需要遍历整组数据];

  • 排序后再利用二分查找的方式对数据进行查找

    排序所需要的时间复杂度大概为O(NlogN);

    利用二分查找的时间复杂度为O(logN);

以上面的两种方法确实可以达到在大量数据中判断一个数据是否存在;

除此之外还有两个问题?

本文主要围绕32位机器进行解析
  • 在内存当中如何对数据进行存储?

    已知一个无符号的整型大小为4byte,而一个字节为8bit;

    40亿个无符号整型的大小为4 * 108 * 40 = 160 * 108 byte,最终的换算结果为16GB;

    而若是使用上面提到的两种方法都不能很好的使数据完整的放置至内存当中;

    而若是使用char来存放数据的话则为16GB / 4 = 4GB,同时在此基础上使用其他算法将数据存入内存当中;

    那么实际上4GB放置在内存当中也会变得比较大;

    那么还有什么方式可以使得能够有效的存放数据?

  • 如何使得判断的效率变得更快?

    在上述的两种方法中,实际操作起来其总体效率都比较慢;

    如何提高判断的效率?

在之前的博客『 C++ - Hash 』闭散列与开散列哈希表详解及其实现 ( 万字 )-CSDN博客 中对于哈希函数提到了一种为直接定址法的方法;

这种方法根据直接定址来判断数据是否存在该组数据当中;

那么这种方法与该面试题有何种关联?

综上所述需要解决两个点:

  • 将数据"放置"在内存当中;
  • 根据对应算法使得提高判断效率;

实际上可以采用类似哈希函数中直接定址法的思路对问题进行解决;

在计算机当中,判断一个数据是否存在无非是一个==“是不是"的问题,而"是不是”==可以使用1或者0来进行判断;

10在内存当中只需要1bit;

所以可以开空间并使用bit为单位来判断这个数据是否存在;

以该图为例,设计一个以char为单位的数组;

已知char类型的大小为1byte,而1byte == 8bit;

若是存在一个数据为14,那么这个数据可以在对应的:

14/8char中的第14%8bit中;

那么已知size_t无符号整型的最大值为4294967295,也就是232-1;

若是需要判断一个无符号整型是否在40亿个数据当中也只需要开辟大约512MB的空间;而判断在不在也只需要直接映射即可,在判断的情况下只需要常数级别的时间复杂度也就是O(1);

这种数据结构被称为位图;

在C++的STL中也有对应的容器,名为std::bitset;


🧸 位图的实现

请添加图片描述

在上文当中提到了关于位图的概念,那么位图应该如何实现?


🪅 总体框架

请添加图片描述

对于位图而言总体框架是一个char类型的数组,当然也可以选用int类型作为数组的数据类型(根据需求进行修改);

template <size_t N>
class bitset {public:bitset() { _bits.resize(N / 8 + 1, 0); }void set(size_t x);void reset(size_t x);bool test(size_t x) ;private:std::vector<char> _bits;
};

为了位图能够合理的开辟内存在框架之中还使用了非类型模板参数N,并根据N来确定内存的大小;


🪅 位图的数据插入

请添加图片描述

实际上在物理意义上位图由于内存的限制不能使得数据真正意义上的存放至内存当中,只能根据映射的方式使其能够与位图中产生关联;

那么如何使得在一个char类型当中映射单个bit而不影响单个bit?

实际上使用按位或'|'即可使得在不影响其他位的情况下对该bit进行置1操作;

而数据只需要使用按位左移<<即可使得数据偏移至单byte中的对应bit使其能够进行上述操作;

  • 那么如何计算对应位置?

    由于数组的数据类型是char,且单个char的大小为1byte == 8bit;

    所以只需要对数据进行/8操作即可知道数据所在的char位置;

    而对数据进行%8操作即可知道数据映射在对应char位置中的bit位置;

    具体演示参照上文中的 “示例(点击跳转)” ;

  • 代码(供参考)

      void set(size_t x) {size_t i = x / 8;size_t j = x % 8;/*第i个char中的第j个比特位*/_bits[i] |= (1 << j);  // 运算符优先级}
    

当然在代码中应该时刻注意运算符的优先级(位运算的优先级往往是比较低的);


🧩 左移操作与右移操作的区别

请添加图片描述

  • 在上文当中对数据的单个bit映射位置的设置为什么是左移而不是右移,左移右移之间有什么区别?是否需要根据机器的情况来调整左移或者右移的操作?

左移和右移虽然听起来是一个方向的位置,若是按字面意思来解左移右移的话可能对应的则为左移是向左移动,而右移是向右移动;

而实际上在c/C++中,左移或者右移实际上是对于高低值进行移动;

  • 左移

    向高位移动;

  • 右移

    向低位移动;

无论是十六进制0x12ff40,或者十进制1024,更或者是二进制0101而言都是左高右低;

以该篇文章而言所实现的位图而言,以我们的角度而言他可能是这样的:

而实际上在内存当中是这样的:

所以并不需要根据机器的情况来调整左移或者右移的操作;


🪅 位图的数据删除

请添加图片描述

由于在位图当中没有真正的存储数据而是对数据与位图进行一种映射关系;

所以实质上对数据的删除只需要对对应的bit位置进行置0操作即可;

1左移至对应的位置并对该数据进行按位取反'~',最终将该数据与对应位置进行按位与'&'的操作即可;

  • 代码(供参考)

    void reset(size_t x) {size_t i = x / 8;size_t j = x % 8;_bits[i] &= ~(1 << j);}
    

🪅 位图的数据查找

请添加图片描述

对于数据的查找而言只需要将1左移至对应的偏移量最后与对应位置进行按位与'&'操作即可;

  • 代码(供参考)

      bool test(size_t x) {size_t i = x / 8;size_t j = x % 8;return _bits[i] & (1 << j);}
    

按位与'&'操作的结果为1说明该数据存在,若是为0则不存在;


🪅 位图整体代码(供参考)

请添加图片描述

#pragma once#include <iostream>
#include <vector>namespace MyBitset {
template <size_t N>
class bitset {public:bitset() { _bits.resize(N / 8 + 1, 0); }void set(size_t x) {size_t i = x / 8;size_t j = x % 8;/*第i个char中的第j个比特位*/_bits[i] |= (1 << j);  // 运算符优先级}void reset(size_t x) {size_t i = x / 8;size_t j = x % 8;_bits[i] &= ~(1 << j);}bool test(size_t x) {size_t i = x / 8;size_t j = x % 8;return _bits[i] & (1 << j);}private:std::vector<char> _bits;
};
}  // namespace MyBitset

🧸 布隆过滤器(Bloom Filter)概念

请添加图片描述

在上文中所讲述的是位图所对应的内容;

位图在对于解决数据在不在的问题无论是在效率上还是在内存大小当中都是数据结构中的佼佼者;

但是对应的位图也存在着缺点:

  • 优点:

    • 内存需求小;

    • 效率高(时间复杂度为常数级);

  • 缺点:

    • 只能存储整型类型;

在位图当中普遍都是对大量的整型数据进行映射,判断其是否存在,而遇到其他类型则只能袖手旁观;

可能会有人想到使用哈希中的除留余数法并于位图进行结合;

但是这样的话以字符串类型string为例,使用除留余数法主要是依靠计算ASCII值对数据进行映射存储,而使用该种方法避免不了的为哈希冲突;

在1970年,伯顿·霍华德·布隆(Burton Howard Bloom)提出了一个概率型数据结构,这个数据结构则为今天的布隆过滤器(Bloom Filter);

这个数据结构是利用位图以及哈希算法来实现的;

在实现布隆过滤器中布隆并未想解决哈希冲突,而是降低了哈希冲突的概率;

以上图为例,若是只以一种哈希函数来对位图进行映射,那么当发生哈希冲突时,由于例如相同字符而字符顺序不同的两个字符串而言极容易发生冲突,其将会被对应的位图的去重性而无法判断该数据是否存在;

那么在一个哈希函数的基础上再加上几个哈希函数那么由于哈希函数的不同,对应每个哈希函数都需要映射一个位置,那么其将大大减小哈希冲突的概率;

以该图为例,该图演示了当使用两个哈希函数对数据进行映射时,其在一个哈希函数中可能发生冲突,但另一个哈希函数不一定发生同样的冲突,这种方式有效的降低了在位图当中的哈希冲突,即为布隆过滤器;


🪅 哈希函数的个数及布隆过滤器的长度

请添加图片描述

在上文当中提到了对于布隆过滤器的基本概念,其本质是通过多个哈希函数将同一个字符串映射至位图中的不同位置从而降低布隆过滤器中的整体的哈希冲突概率;

同时根据上文的内容当中可以得知,实际上布隆过滤器不仅与概念有关,而且与对应的哈希函数个数与布隆过滤器长度的选择有关;

  • 举个例子

    存在100string数据,若是在一个布隆过滤器当中使用4个哈希函数作为布隆过滤器的哈希函数,而在布隆过滤器的选择上开辟了400bit的空间;

    那么在这种情况下由于数据的插入将会引来大量的哈希冲突,大量的bit都将被置为1从而导致每次的判断都为可能存在;

    故实际上布隆过滤器的长度 m>=n*k ,其中m为长度,n为数据个数,k为哈希函数的个数;

而对于哈希函数的个数也是如此;

以降低误判率而言,哈希函数的个数越多越好,但实际上越多的哈希函数虽然能够有效降低布隆过滤器中的误判率,但随着哈希函数的个数越多,为了对应的降低误判率,其布隆过滤器的长度也要变得更大,从而增大了整体的内存开销;

其中p为误判率,k为哈希函数个数,m为布隆过滤器的长度,n为插入元素个数;

通常情况下,哈希函数的个数与布隆过滤器的长度都可以使用公式进行计算:

  • 哈希函数个数

    k = m n ln ⁡ ( 2 ) k = \frac{m}{n} \ln(2) k=nmln(2)

  • 布隆过滤器长度

    m = − n ln ⁡ p ( ln ⁡ 2 ) 2 m = - \frac{n \ln p}{(\ln 2)^2} m=(ln2)2nlnp


🧸 布隆过滤器的实现

请添加图片描述

在上文当中提到了布隆过滤器采用的是多个哈希算法与位图的结合;

在本文章中着重实现哈希函数数量为3的布隆过滤器;


🪅 总体框架

请添加图片描述

在上文当中提到了布隆过滤器的大致原理;

实际上就是采用多个哈希函数使同一个字符串根据不同的哈希函数规则映射至不同的位置,从而降低布隆过滤器中整体的哈希冲突;

而本文章中重点实现使用3个哈希函数实现布隆过滤器;

所使用的哈希函数分别为DKBRHash,APHashDJBHash;

#pragma once#include "BitSet.h"struct _BKDR__Hash {size_t operator()(const std::string& key) {size_t hash = 0;for (auto e : key) {hash *= 31;hash += e;}return hash;}
};struct _AP__Hash {size_t operator()(const std::string& key) {size_t hash = 0;for (size_t i = 0; i < key.size(); i++) {char ch = key[i];if ((i & 1) == 0) {hash ^= ((hash << 7) ^ ch ^ (hash >> 3));} else {hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));}}return hash;}
};struct _DJB__Hash {size_t operator()(const std::string& key) {size_t hash = 5381;for (auto ch : key) {hash += (hash << 5) + ch;}return hash;}
};template <size_t N, class K = std::string, class Hash1 = _BKDR__Hash , class Hash2 = _AP__Hash,class Hash3 = _DJB__Hash>
class BloomFilter {public:void set(const K& key);bool test(const K& key);private:const static size_t _X = 5;MyBitset::bitset<N*_X> _bits;  // N为数据量最多
};

在模板参数中定义一个非类型模板参数N,该非类型模板参数作为处理数据时的数据个数;

从该段代码中可以看出,其成员函数重点为一个位图_bits及对应的一个乘数因子_X;

其中乘数因子_X决定了最终布隆过滤器的长度;

  • 根据公式

    k = m n ln ⁡ ( 2 ) k = \frac{m}{n} \ln(2) k=nmln(2)

    可以推断出最终的布隆过滤器的长度大概为4.3n;

    此处实现为向上取整将乘数因子的大小控制在5(可根据项目条件酌情替换);

该处将哈希函数以仿函数的形式定义,并将该仿函数声明于模板参数中作为缺省参数,分别为Hash1,Hash2,Hash3;


🪅 布隆过滤器的数据插入

请添加图片描述

布隆过滤器的数据插入只需要调用对应的仿函数(哈希函数),并调用位图中的set接口将其映射至位图中的对应部分即可;

 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);}

🪅 布隆过滤器的数据查找

请添加图片描述

关于布隆过滤器的数据查找而言只需要使用与插入同样的逻辑即可;

bool test(const K& key){size_t len = N * _X;size_t hash1 = Hash1()(key) % len;if(!_bits.test(hash1)){return false;}size_t hash2 = Hash2()(key) % len;if (!_bits.test(hash2)) {return false;}size_t hash3 = Hash3()(key) % len;if (!_bits.test(hash3)) {return false;}else{return true;}
}

当然实际上布隆过滤器的数据查找在判断是否存在中仍有误判的概率;

  • 数据存在

    数据存在是存在误判的概率,所以在布隆过滤器当中当数据返回结果判定为数据存在时其真正的意义是 “可能存在”;

    由于布隆过滤器是将同一个数据通过不同的哈希函数映射至位图中不同的位置;

    真正在对数据进行查找时,无法真正判断该数据是否存在于该位图当中;

    以该图为例,当一个数据不存在时,实际上其对应映射的位置可能被其他数据所映射,从而导致了返回其存在的误判;

    故实际上在布隆过滤器当中对==“存在”==的概念是不确定的;

  • 数据不存在

    相对的,对于布隆过滤器而言,数据不存在才是准确且值得信任的;

    当一个数据不存在时,其数据对应所映射的位置也必定为空,当其映射的位置其中的一个位置为空时则表示该数据不存在于位图当中;


    以该图为例;


🪅 布隆过滤器的数据删除

请添加图片描述

在布隆过滤器当中实际上是不能进行删除的,而对于有些布隆过滤器的变种而言其可以进行数据的删除操作,这里不进行阐述;

  • 为什么布隆过滤器不支持数据的删除操作?

    在上文中关于布隆过滤器的查找中的"数据是否存在"问题中对于误判有着详细的解释;

    由于无法百分百确认一个数据在布隆过滤器中的位图当中是否存在,所以在布隆过滤器当中不能对数据进行随意的删除;

与上文的例子相符,假设一个数据不存在,而该数据所对应映射的位置恰巧有其他数据的映射,那么则会判断该数据 “可能存在”;

而若是对这个 “可能存在” 的数据进行删除则会影响其他数据;

可能有些人设想在实现布隆过滤器当中使其加入一个关于计数的功能使其能够在布隆过滤器当中对数据进行计数删除操作;

而实际上,若是增加位的计数器从而达到能够对数据进行数据的方法确实可以在某种程度上达到一定效果;

而布隆过滤器中所设置的计数器是按照二进制展开从而达到计数的目的,当数据量大过其本身能够计数的大小时,其将会进行 “计数器回绕”;

  • 例:

    101 -> 110 -> 111 -> 000

这将完全影响对应的秩序;

故对于一般的布隆过滤器而言,其不能进行数据的删除操作;


🪅 布隆过滤器整体代码(供参考)

请添加图片描述

#pragma once#include "BitSet.h"struct _BKDR__Hash {size_t operator()(const std::string& key) {size_t hash = 0;for (auto e : key) {hash *= 31;hash += e;}return hash;}
};
struct _AP__Hash {size_t operator()(const std::string& key) {size_t hash = 0;for (size_t i = 0; i < key.size(); i++) {char ch = key[i];if ((i & 1) == 0) {hash ^= ((hash << 7) ^ ch ^ (hash >> 3));} else {hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));}}return hash;}
};
struct _DJB__Hash {size_t operator()(const std::string& key) {size_t hash = 5381;for (auto ch : key) {hash += (hash << 5) + ch;}return hash;}
};template <size_t N, class K = std::string, class Hash1 = _BKDR__Hash , class Hash2 = _AP__Hash,class Hash3 = _DJB__Hash>
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);}bool test(const K& key){size_t len = N * _X;size_t hash1 = Hash1()(key) % len;if(!_bits.test(hash1)){return false;}size_t hash2 = Hash2()(key) % len;if (!_bits.test(hash2)) {return false;}size_t hash3 = Hash3()(key) % len;if (!_bits.test(hash3)) {return false;}else{return true;}
}private:const static size_t _X = 5;MyBitset::bitset<N*_X> _bits;  // N为数据量最多
};

其中"BitSet.h"文件参考上文当中的位图;


🪅 布隆过滤器的应用

请添加图片描述

布隆过滤器(Bloom Filter),其功能的应用范围可以以字面意思进行理解;

其为 “过滤器”,即实际上其本身的功能并不是将数据进行存储,而是对数据进行一个过滤的作用,例如使用布隆过滤器减少磁盘IO或者网络请求;

当一个数据在布隆过滤器当中被判断不存在,由于不存在在布隆过滤器中是一个肯定的,可以信任的返回;

所以当一个数据被判断不存在时,其就可以不需要再进行后续的查询请求;

除了上述应用以外,由于其存在误判性,其也可以在一些可以对误判进行容忍的场景下进行应用;

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

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

相关文章

使用Vue.js输出一个hello world

导入vue.js <script src"https://cdn.jsdelivr.net/npm/vue2/dist/vue.js"></script> 创建一个标签 <div id"app">{{message}}</div> 接管标签内容&#xff0c;创建vue实例 <script type"text/javascript">va…

MySQL学习记录——십일 索引

文章目录 1、理解索引2、聚簇、非聚簇索引3、操作1、主键索引2、唯一键索引3、普通索引4、注意事项 4、全文索引 1、理解索引 MySQL服务器是在内存中的&#xff0c;所有数据库的CURD操作都是在内存中进行&#xff0c;索引也是如此。索引是用来提高性能的&#xff0c;它通过组织…

Mysql Day03

多表设计 一对多 在多的一方添加外键约束&#xff0c;关联另外一方主键 一对一 任意一方添加外键约束&#xff0c;关联另外一方主键 多对多 建立第三张中间表&#xff0c;中间表至少包含两个外键&#xff0c;分别关联两方主键 idstu_idcourse_id 1 11 2 12313421524 案…

使用cocos2d-console初始化一个项目

先下载好cocos2d-x的源码包 地址 https://www.cocos.com/cocos2dx-download 这里使用的版本是 自己的电脑要先装好python27 用python安装cocos2d-console 看到项目中有个setup.py的一个文件 python setup.py 用上面的命令执行一下。 如果执行正常的话回出现上面的图 然后…

备战蓝桥杯---图论之最短路Bellman-Ford算法及优化

目录 上次我们讲到复杂度为&#xff08;nm)logm(m为边&#xff0c;n为点&#xff09;的迪杰斯特拉算法&#xff0c;其中有一个明显的不足就是它无法解决包含负权边的图。 于是我们引进Bellman-Ford算法。 核心&#xff1a;枚举所有的点&#xff0c;能松弛就松弛&#xff0c;直…

2.16学习总结

1.邮递员送信&#xff08;dijkstra 不只是从起到到目标点&#xff0c;还要走回去&#xff09; 2.炸铁路(并查集) 3.统计方形&#xff08;数据加强版&#xff09;&#xff08;排列组合&#xff09; 4.滑雪&#xff08;记忆化&#xff09; 5.小车问题&#xff08;数学问题&#x…

枚举,#define,C中程序内存区域划分

目录 一、枚举 1.1枚举类型的声明 1.2枚举类型的优点 1.3枚举类型的使用 二、#define定义常量 三、C中程序内存区域划分 一、枚举 1.1枚举类型的声明 枚举顾名思义就是⼀⼀列举。 把可能的取值⼀⼀列举。 比如我们现实生活中&#xff1a; ⼀周的星期⼀到星期日是有限…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第三天-ARM Linux ADC和触摸屏开发 (物联技术666)

链接&#xff1a;https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd1688 提取码&#xff1a;1688 教学内容&#xff1a; 1、ADC S3C2440的A/D转换器包含一个8通道的模拟输入转换器&#xff0c;可以将模拟输入信号转换成10位数字编码。 在A/D转换时钟频率为2.5MHz时&…

【QT+QGIS跨平台编译】之四十:【gsl+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、GSL介绍二、GSL下载三、文件分析四、pro文件五、编译实践一、GSL介绍 GSL(GNU Scientific Library)是一个开源的数值计算库,用于提供一系列常用的数学函数和算法。它为科学计算和数据分析提供了高效、可靠的工具。 GSL库提供了丰富的功能,包括数值积分、数值…

数据分析基础之《pandas(8)—综合案例》

一、需求 1、现在我们有一组从2006年到2016年1000部最流行的电影数据 数据来源&#xff1a;https://www.kaggle.com/damianpanek/sunday-eda/data 2、问题1 想知道这些电影数据中评分的平均分&#xff0c;导演的人数等信息&#xff0c;我们应该怎么获取&#xff1f; 3、问题…

Compose 自定义 - 数据转UI的三阶段(组合、布局、绘制)

一、概念 组合阶段 Compisition 界面首次渲染时会将可组合函数转化为一个个布局节点 Layout Node, 使用多叉树的数据结构构建一个UI树。 布局阶段 Layout 多叉树中父节点会测量他们的子节点&#xff0c;然后在一个二维空间里进行摆放。通过从上往下测量&#xff08;如果存在子…

【C语言】Linux内核accept 系统调用代码

一、Linux 4.19内核accept 系统调用代码中文注释 /** 在使用accept时&#xff0c;我们尝试创建一个新的socket&#xff0c;与客户端建立连接&#xff0c;* 唤醒客户端&#xff0c;然后返回新的连接文件描述符&#xff08;fd&#xff09;。我们在内核空间收集* 连接方的地址&am…

C#利用接口实现选择不同的语种

目录 一、涉及到的知识点 1.接口定义 2.接口具有的特征 3.接口通过类继承来实现 4.有效使用接口进行组件编程 5.Encoding.GetBytes(String)方法 &#xff08;1&#xff09;检查给定字符串中是否包含中文字符 &#xff08;2&#xff09;编码和还原前后 6.Encoding.GetS…

NLP_Transformer架构

文章目录 Transformer架构剖析编码器-解码器架构各种注意力的应用Transformer中的自注意力Transformer中的多头自注意力Transformer中的编码器-解码器注意力Transformer中的注意力掩码和因果注意力 编码器的输入和位置编码编码器的内部结构编码器的输出和编码器-解码器的连接解…

【Web】小白友好的Java内存马基础学习笔记

目录 简介 文件马与内存马的比较 文件马原理 内存马原理 内存马使用场景 内存马分类 内存马注入方式 这篇文章主要是概念性的&#xff0c;具体技术细节不做探究&#xff0c;重点在祛魅。 简介 内存马&#xff08;Memory Shellcode&#xff09;是一种恶意攻击技术&…

抽象的问题1

vue3&#xff0c;在使用v-mode绑定属性时&#xff0c;发生了奇怪的问题&#xff0c;渲染失败了 代码如下 <template><div><form><div>账号<input v-model"form_user_Data.username" type"text"></div><div>密…

HCIA-HarmonyOS设备开发认证V2.0-3.2.轻量系统内核基础-软件定时器

目录 一、软件定时器基本概念二、软件定时器运行机制三、软件定时器状态四、软件定时器模式五、软件定时器开发流程六、软件定时器使用说明七、软件定时器接口八、代码分析&#xff08;待续...&#xff09;坚持就有收获 一、软件定时器基本概念 软件定时器&#xff0c;是基于系…

bpmn-js 事件总线处理

bpmn-js中使用EventBus作为事件的处理句柄&#xff0c;EventBus的使用和我们常规使用的事件总线没啥大的区别&#xff0c;其源码位于&#xff1a;/diagram-js/lib/core/EventBus.js &#xff08;bpmn-js使用diagram-js实现流程图的web端绘制呈现工具&#xff09;。 EventBus使用…

Kibana:如何嵌入 Kibana 仪表板

作者&#xff1a;Carly Richmond 像我这样的前端工程师经常提出的要求是将 Kibana 等来源的现有仪表板嵌入到 JavaScript Web 应用程序中。 这是我必须多次执行的任务&#xff0c;因为我们希望快速部署用户生成的视图或允许用户控制给定的视图。 从我们从精彩的开发者社区收到的…

算法沉淀——BFS 解决 FloodFill 算法(leetcode真题剖析)

算法沉淀——BFS 解决 FloodFill 算法 01.图像渲染02.岛屿数量03.岛屿的最大面积04.被围绕的区域 BFS&#xff08;广度优先搜索&#xff09;解决 Flood Fill 算法的基本思想是通过从起始点开始&#xff0c;逐层向外扩展&#xff0c;访问所有与起始点相连且具有相同特性&#xf…