『 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.mzph.cn/news/683373.shtml

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

相关文章

(06)Hive——正则表达式

Hive版本&#xff1a;hive-3.1.2 一、Hive的正则表达式概述 正则表达式是一种用于匹配和操作文本的强大工具&#xff0c;它是由一系列字符和特殊字符组成的模式&#xff0c;用于描述要匹配的文本模式。 Hive的正则表达式灵活使用解决HQL开发过程中的很多问题&#xff0c;本篇文…

Activation of network connection failed(ubuntu连不上网)

ubuntu连不上网&#xff0c;看了好几个方法找到个有用的记录一下 1. 还原默认设置 2. 更改适配器&#xff1a;加上vmware bridge protocol

使用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…

关于Django的中间件使用说明。

目录 1.中间件2. 为什么要中间件&#xff1f;3. 具体使用中间件3.1 中间件所在的位置&#xff1a;在django的settings.py里面的MIDDLEWARE。3.2 中间件的创建3.3 中间件的使用 4. 展示成果 1.中间件 中间件的大概解释&#xff1a;在浏览器在请求服务器的时候&#xff0c;首先要…

NLP快速入门

NLP入门 课程链接&#xff1a;https://www.bilibili.com/video/BV17K4y1W7yb/?p1&vd_source3f265bbf5a1f54aab2155d9cc1250219 参考文档链接1&#xff1a;NLP知识点&#xff1a;Tokenizer分词器 - 掘金 (juejin.cn) 一、分词 分词是什么&#xff1f; 每个字母都有对应…

【Web】从零开始的js逆向学习笔记(上)

目录 一、逆向基础 1.1 语法基础 1.2 作用域 1.3 窗口对象属性 1.4 事件 二、浏览器控制台 2.1 Network Network-Headers Network-Header-General Network-Header-Response Headers Network-Header-Request Headers 2.2 Sources 2.3 Application 2.4 Console 三、…

车载诊断协议DoIP系列 —— DoIP会话模式(安全与非安全)

车载诊断协议DoIP系列 —— DoIP会话模式(安全与非安全) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖…

【STM32 CubeMX】学STM必会的数据结构——环形缓冲区

文章目录 前言一、环形缓冲区是什么二、实现环形缓冲区实现分析2.1 环形缓冲区初始化2.2 写buf2.3 读buf2.4 测试 三、代码总况总结 前言 在嵌入式系统开发中&#xff0c;经常需要处理数据的缓存和传输&#xff0c;而环形缓冲区是一种常见且有效的数据结构&#xff0c;特别适用…

幻兽帕鲁官方更新了,服务器端怎么更新?

幻兽帕鲁官方客户端更新了&#xff0c;那么它的服务器端版本也是需要更新的&#xff0c;不然版本不一致的话&#xff0c;就不能进入游戏了。 具体的更新方法有两种&#xff0c;一是手动输入命令进行更新。第二种是在面板一键更新。 无论你是在阿里云或者腾讯云购买的一键部署…

Pycharm里如何设置多Python文件并行运行

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 夕阳何事近黄昏&#xff0c;不道人间犹有未招魂。 大家好&#xff0c;我是皮皮。 一、前言 相信使用Pycharm的粉丝们肯定有和我一样的想法&#xff0c;…

算法学习——LeetCode力扣贪心篇1

算法学习——LeetCode力扣贪心篇1 455. 分发饼干 455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09; 描述 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[…

Vulnhub靶机:DC3

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;DC3&#xff08;10.0.2.56&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/dc-32,312…

嵌入式系统的基础知识:了解嵌入式系统的构成和工作原理

&#xff08;本文为简单介绍&#xff0c;个人观点仅供参考&#xff09; 嵌入式系统是建立在微处理器基础上的计算机系统,用于对专门的功能进行控制、运算和接口。它结合了硬件和软件,可以提供实时的响应,广泛应用于工业控制、通信、医疗、交通等领域。 嵌入式系统的核心是微处理…

猫头虎分享已解决Bug || 代码部署失败(Code Deployment Failure):DeploymentError, FailedRelease

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

PMDG 737

在Simbrief中生成计划后下载两个文件 放到A:\Xbox\Community\pmdg-aircraft-738\Config\Flightplans中

SpringCloud第一天

1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.1.单体架构 单体架构&#xff1a;将业务的所有功能集中在一个项目中开发&#xff0c;打…

波奇学Linux:文件系统

磁盘认识 磁盘被访问的基本单元是扇区-512字节。 磁盘可以看成多个同心圆&#xff0c;每个同心圆叫做磁道&#xff0c;多个扇区组成同心圆。 我们可以把磁盘看做由无数个扇区构成的存储介质。 要把数据存到磁盘&#xff0c;先定位扇区&#xff0c;用哪一个磁头&#xff0c;…

【原创 附源码】Flutter集成Apple支付详细流程(附源码)

最近有时间&#xff0c;特意整理了一下之前使用过的Flutter平台的海外支付&#xff0c;附源码及demo可供参考 这篇文章只记录Apple支付的详细流程&#xff0c;其他相关Flutter文章链接如下&#xff1a; 【原创 附源码】Flutter集成谷歌支付详细流程(附源码) 【原创 附源码】F…

《Java 简易速速上手小册》第2章:面向对象的 Java(2024 最新版)

文章目录 2.1 类和对象 - 构建你的小宇宙2.1.1 基础知识2.1.2 重点案例&#xff1a;设计一个简单的图书类2.1.3 拓展案例 1&#xff1a;学生管理系统2.1.4 拓展案例 2&#xff1a;账户管理系统 2.2 继承与多态 - 让一切变得更有趣2.2.1 基础知识2.2.2 重点案例&#xff1a;动物…

【51单片机】蜂鸣器(江科大)

11.1蜂鸣器 1.蜂鸣器介绍 蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号 蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器 有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定 无源蜂鸣器:内部不带振荡源,需…