Java-布隆过滤器的实现

文章目录

  • 前言
  • 一、概述
  • 二、误差率
  • 三、hash 函数的选择
  • 四、手写布隆过滤器
  • 五、guava 中的布隆过滤器


前言

如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路,但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢 (O(n)O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个 Hash 函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是 1 就可以知道集合中有没有它了。于是乎,布隆过滤器便应运而生了。

一、概述

布隆过滤器(Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。

主要作用:用于检索一个元素是否在一个集合中。

优点 :

  • 时间复杂度低,增加及查询元素的时间复杂度都是 O(k)kHash 函数的个数
  • 占用存储空间小,布隆过滤器相对于其他数据结构(如SetMap)非常节省空间

缺点:

  • 存在误判,只能证明一个元素一定不存在或者可能存在,返回结果是概率性的,但是可以通过调整参数来降低误判比例
  • 删除困难,一个元素映射到 bit 数组上的 x 个位置为 1,删除的时候不能简单的直接置为 0,可能会影响到其他元素的判断

原理:

布隆过滤器由长度为 m 的位向量和 k个随机映射函数组成。

假设 bit array 的长度 m=10,哈希函数的个数 k=3,默认情况下 bit array 中每个坐标的值均为 0
在这里插入图片描述
当一个元素被加入集合时,通过多个 hash 函数计算得到多个 hash 值,以这些 hash 值作为数组的坐标,将这些坐标位置上的值设置为 1

在这里插入图片描述
当查询该元素是否存在于集合中时,以同样的方法通过多个 hash 函数计算出对应的 hash 值,再查看这些 hash 值所对应的坐标是否均为 1,如果均为 1 则表示已存在,否则不存在

在这里插入图片描述


二、误差率

布隆过滤器的误差率(false positive rate)是指由于 hash 冲突,导致原本不再布隆过滤器中的数据,查询时显示在里面

哈希冲突是指当两个或多个不同的输入产生相同的哈希值时,就会发生哈希冲突

比如说有三个元素经过 k=3hash 函数计算得到的哈希值分别是 [1,6,9][2,5,9][3,4,8]
在这里插入图片描述
由上图可见 element_1element_2 经过 hash_3 函数时得到的值均为 9,这就是 hash 冲突,虽然可能发生哈希冲突,但是由于每个元素是通过多个 hash 值来判断是否存在,所以在插入上述三个元素时均会判断出该元素不存在,此时 bit array 如下:

在这里插入图片描述

如果再插入第四个元素时,经过这 k=3hash 函数计算后的结果是 [5,6,8],而 5,6,8 这三个坐标在 bit array 中值均为 1,此时就会判定 element_4 已存在,实际上并没有存在,这就是布隆过滤器会存在误判的原因

在这里插入图片描述

随着数据量增加, bit array 中每个位置的值为 1 的比率也会增加,误判的可能性也会随之增加

那如何减少这种因为 hash 冲突而导致的误判呢?可以用公式进行推导

假设 hash 函数以等概率条件选择并设置 bit array 中的某一位,m 是该位数组的大小,khash 函数的个数,那么位数组中某一特定的位在进行元素插入时的 hash 操作中没有被置为 1 的概率是:
1 − 1 m 1-\frac{1}{m} 1m1
那么在所有 khash 操作后该位没有被置为 1 的概率是:
( 1 − 1 m ) k (1-\frac{1}{m})^{k} (1m1)k
如果插入了 n 个元素,那么某一位仍然是 0 的概率是:
( 1 − 1 m ) k n (1-\frac{1}{m})^{kn} (1m1)kn
因而该位为 1 的概率是:
1 − ( 1 − 1 m ) k n 1-(1-\frac{1}{m})^{kn} 1(1m1)kn
现在检测某一元素是否在该集合中,标明某个元素是否在集合中所需的 k 个位置都按照如上的方法设置为 1 ,但是该方法可能会使算法错误的认为某一原本不在集合中的元素却被检测为在该集合中(误差 False Positives),该概率由以下公式确定:
( 1 − ( 1 − 1 m ) k n ) k ≈ ( 1 − e − k n / m ) k (1-(1-\frac{1}{m})^{kn})^{k}\approx (1-e^{-kn/m})^{k} (1(1m1)kn)k(1ekn/m)k

推导过程: ( 1 − ( 1 − 1 m ) k n ) k = ( 1 − ( 1 + 1 − m ) − m − k n m ) k (1-(1-\frac{1}{m})^{kn})^{k}= (1-(1+\frac{1}{-m})^{-m\frac{-kn}{m}})^{k} (1(1m1)kn)k=(1(1+m1)mmkn)k
由自然常数定义: e = lim ⁡ n → ∞ ( 1 + 1 n ) n e= \lim_{n \to \infty} (1+\frac{1}{n})^{n} e=nlim(1+n1)n
可知,当 n 趋近于无穷大时: e = ( 1 + 1 n ) n e= (1+\frac{1}{n})^{n} e=(1+n1)n
所以: ( 1 − ( 1 + 1 − m ) − m − k n m ) k = ( 1 − e − k n / m ) k (1-(1+\frac{1}{-m})^{-m\frac{-kn}{m}})^{k}= (1-e^{-kn/m})^{k} (1(1+m1)mmkn)k=(1ekn/m)k
即: ( 1 − ( 1 − 1 m ) k n ) k ≈ ( 1 − e − k n / m ) k (1-(1-\frac{1}{m})^{kn})^{k}\approx (1-e^{-kn/m})^{k} (1(1m1)kn)k(1ekn/m)k

由此可见,随着 m(位数组大小)的增加,误差(False Positives)的概率会下降,同时随着插入元素个数 n 的增加,误差(False Positives)的概率又会上升

对于给定的 mn,如何选择 hash 函数个数 k?由以下公式确定:
k = m n l n 2 ≈ 0.7 m n k=\frac{m}{n}ln2\approx 0.7\frac{m}{n} k=nmln20.7nm

推导过程:


k 为何值时可以使得误判率最低?设误判率为 k 的函数:
f ( k ) = ( 1 − e − k n / m ) k f(k)= (1-e^{-kn/m})^{k} f(k)=(1ekn/m)k
令: b = e n m b=e^{\frac{n}{m}} b=emn
则: f ( k ) = ( 1 − b − k ) k f(k)=(1-b^{-k})^{k} f(k)=(1bk)k
两边取对数: l n f ( k ) = k ⋅ l n ( 1 − b − k ) lnf(k)=k\cdot ln(1-b^{-k}) lnf(k)=kln(1bk)
两边对 k 进行求导:
1 f ( k ) ⋅ f ′ ( k ) = l n ( 1 − b − k ) + k ⋅ 1 1 − b − k ⋅ ( − b − k ) ⋅ ( − 1 ) ⋅ l n b = l n ( 1 − b − k ) + k ⋅ b − k ⋅ l n b 1 − b − k \frac{1}{f(k)}\cdot f'(k)=ln(1-b^{-k})+k\cdot \frac{1}{1-b^{-k}}\cdot (-b^{-k})\cdot (-1)\cdot lnb =ln(1-b^{-k})+k\cdot \frac{b^{-k}\cdot lnb}{1-b^{-k}} f(k)1f(k)=ln(1bk)+k1bk1(bk)(1)lnb=ln(1bk)+k1bkbklnb
等式左边,常量的导数为 0,所以: l n ( 1 − b − k ) + k ⋅ b − k ⋅ l n b 1 − b − k = 0 ln(1-b^{-k})+k\cdot \frac{b^{-k}\cdot lnb}{1-b^{-k}}=0 ln(1bk)+k1bkbklnb=0
( 1 − b − k ) ⋅ l n ( 1 − b − k ) = − k ⋅ b − k ⋅ l n b (1-b^{-k})\cdot ln(1-b^{-k})=-k\cdot b^{-k}\cdot lnb (1bk)ln(1bk)=kbklnb
因为: − k ⋅ l n b = l n b − k -k\cdot lnb = lnb^{-k} klnb=lnbk
所以: ( 1 − b − k ) ⋅ l n ( 1 − b − k ) = b − k ⋅ l n b − k (1-b^{-k})\cdot ln(1-b^{-k})=b^{-k}\cdot lnb^{-k} (1bk)ln(1bk)=bklnbk
因为, b-k 恒小于 1,所以: l n ( 1 − b − k ) = l n b − k ln(1-b^{-k}) = lnb^{-k} ln(1bk)=lnbk
则等式化简为: 1 − b − k = b − k 1-b^{-k}=b^{-k} 1bk=bk
b − k = 1 2 b^{-k}=\frac{1}{2} bk=21
转化 b 得: e − k n m = 1 2 e^{-\frac{kn}{m}}=\frac{1}{2} emkn=21
− k n m = l n 2 -\frac{kn}{m}=ln2 mkn=ln2
则误判率最低时,得出 k 的值为: k = l n 2 ⋅ m n ≈ 0.7 m n k=ln2\cdot \frac{m}{n} \approx 0.7 \frac{m}{n} k=ln2nm0.7nm

此时误差(False Positives)的概率为:
2 − k ≈ 0.618 5 m n 2^{-k}\approx 0.6185^{\frac{m}{n}} 2k0.6185nm

推导过程:

由上述推导过程可知,当: e − k n m = 1 2 e^{-\frac{kn}{m}}=\frac{1}{2} emkn=21
时函数 f(k) (即误差率)的值最小,所以:
f ( e r r o r ) = ( 1 − e − k n / m ) k = ( 1 − 1 2 ) k = 2 − k = 2 − l n 2 ⋅ m n ≈ 0.618 5 m n f(error)=(1-e^{-kn/m})^{k}=(1-\frac{1}{2})^{k}=2^{-k}=2^{-ln2\cdot \frac{m}{n}}\approx 0.6185^{\frac{m}{n}} f(error)=(1ekn/m)k=(121)k=2k=2ln2nm0.6185nm

而对于给定的误差(False Positives)概率 p,如何选择最优的数组大小 m 呢?
m = − n l n p ( l n 2 ) 2 m= -\frac{nlnp}{(ln2)^{2}} m=(ln2)2nlnp

推导过程:


由上述推导过程误差率得: p = 2 − l n 2 ⋅ m n p=2^{-ln2\cdot \frac{m}{n}} p=2ln2nm
两边取对数: l n p = l n 2 ⋅ ( − l n 2 ) ⋅ m n lnp=ln2\cdot (-ln2)\cdot \frac{m}{n} lnp=ln2(ln2)nm
则: m = − n ⋅ l n p ( l n 2 ) 2 m=-\frac{n\cdot lnp}{(ln2)^{2}} m=(ln2)2nlnp


三、hash 函数的选择

前面提到 hash 冲突是导致布隆过滤产生误差的主要原因,所以选择一个合适的 hash 函数是非常重要的,哈希函数的选择会影响到布隆过滤器的性能和准确性。

以下是选择哈希函数时需要考虑的一些因素:

  • 均匀分布:一个好的哈希函数应该能够将输入数据均匀地分布到布隆过滤器的位数组中,以最大限度地减少碰撞的可能性
  • 可扩展性:随着数据集的增加,布隆过滤器的大小也需要相应地扩展。因此,选择的哈希函数应该易于扩展,以便在增加数据集时有效地调整布隆过滤器的大小
  • 稳定性:在某些情况下,如果哈希函数对输入数据的变化过于敏感,可能会导致布隆过滤器中的大量位被频繁地置为 10,这会影响布隆过滤器的性能和准确性,因此,选择一个相对稳定的哈希函数可能更为合适
  • 易于实现:在选择哈希函数时,还需要考虑其实施的难易程度和可移植性。易于实现和移植的哈希函数可以减少布隆过滤器的实现和维护成本

常见的 hash 函数:

  • MD5:MD5(Message Digest Algorithm 5)是一种广泛使用的密码哈希函数,它将任意长度的 “字节串” 映射为一个 128 位的大数。尽管 MD5 在许多安全应用中已经不再被视为安全的哈希函数,但在某些情况下,它仍然可以用于布隆过滤器
  • SHA-1:SHA-1(Secure Hash Algorithm 1)是美国国家安全局设计,并由美国国家标准和技术研究所(NIST)发布的一系列密码散列函数。SHA-1 可以生成一个被称为消息摘要的 160 位( 20 字节)哈希值。尽管 SHA-1 的安全性也受到一定质疑,但在某些场景下仍可用于布隆过滤器
  • SHA-256:SHA-256 是 SHA-2 家族中的一种哈希函数,它生成一个 256 位(32字节)的哈希值。SHA-256 提供了比 SHA-1 更高的安全性,并且在实际应用中被广泛使用
  • MurmurHash:MurmurHash 是一种非加密型哈希函数,适用于一般数据检索应用。它能够提供较好的分布性和性能,因此在布隆过滤器中也被考虑使用

四、手写布隆过滤器

目前 MurmurHash 函数作为布隆过滤器的 hash 函数是使用得比较多的,所以以下内容也会采用这种函数

Google Guava 库:Guava 是一个广泛使用的 Java 库,其中包含了 MurmurHash 的实现,可以使用其中的 Hashing.murmur3_128() 方法来创建 MurmurHash 实例。

<!-- guava -->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>33.0.0-jre</version>
</dependency>

通过以上内容,就可以手写布隆过滤器了,代码如下:

import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import com.mike.common.core.utils.ArithmeticUtils;import java.util.BitSet;/*** 布隆过滤器*/
public class MyBloomFilter {// 位数组,使用BitSet实现private final BitSet bits;// 位数组大小private final int bitSize;// 哈希函数的个数private final int hashFunctionCount;// 预期要存放的数据量private final int dataCount;// 期望的误判率private final double falsePositiveRate;/*** 构造方法私有*/private MyBloomFilter(int dataCount, double falsePositiveRate) {// 设置预期要存放的数据量this.dataCount = dataCount;// 实则期望的误判率(如果期望的误判率为 0,则取 double 的最小值)this.falsePositiveRate = falsePositiveRate == 0? Double.MIN_VALUE: falsePositiveRate;// 计算所需的数组大小this.bitSize = getBitSize();// 计算所需 hash 函数的个数this.hashFunctionCount = getHashFunctionCount();// 创建位数组this.bits = new BitSet(this.bitSize);}/*** 创建布隆过滤器* @param dataCount 预期要存放的数据量* @param falsePositiveRate 期望的误判率* @return 自定义布隆过滤器*/public static MyBloomFilter create(int dataCount, double falsePositiveRate) {return new MyBloomFilter(dataCount, falsePositiveRate);}/*** 获取 bit 数组的大小*/private int getBitSize() {/** 计算公式:*          m = -1 * (n*ln(p))/(ln(2)*ln(2))**          m:数组大小*          n:预估要存的数据量*          p:误判率*/return (int) (-this.dataCount * Math.log(this.falsePositiveRate) / (Math.log(2) * Math.log(2)));// 注意:Math.log(n) 就是求以自然数 e 为底 n 的对数}/*** 获取 hash 函数的数量*/private int getHashFunctionCount() {/** 计算公式:*          k = ln(2)*(m/n)**          k:hash 函数的个数*          m:bitSize 数组的大小*          n:预估要存的数据量*/return Math.max(1, (int) Math.round((double) this.bitSize / this.dataCount * Math.log(2)));}/*** 往布隆过滤器中添加数据* @param data 数据* @return 结果:true 表示插入成功,false 表示插入失败*/public boolean add(String data) {// 先假设插入失败boolean insert = false;// 计算 hash 值long murmurHash = Hashing.murmur3_128().hashString(data, Charsets.UTF_8).asLong();int hash1 = (int)murmurHash;int hash2 = (int)(murmurHash >>> 32);for (int i = 1; i <= hashFunctionCount; i++) {// 通过 murmurHash 与哈希函数个数的序号得到一个新的 hash 值int hash = hash1 + i * hash2;if (hash < 0) {// 如果 hash 小于 0,则进行取反操作hash =~hash;}// 获取更新为 1 的数组坐标位置(取 % 可以让得到的数不超过数组的大小)int index = hash % bitSize;// 通过该坐标值判定该位置是否已被设置为 1boolean exist = bits.get(index);if (!exist) {// 未已设置过则进行更新bits.set(index, true);// 设置已插入insert = true;}}return insert;}/*** 检查数据是否存在*/public boolean exist(String data) {// 逻辑和 add 方法类似long murmurHash = Hashing.murmur3_128().hashString(data, Charsets.UTF_8).asLong();int hash1 = (int)murmurHash;int hash2 = (int)(murmurHash >>> 32);for (int i = 1; i <= this.hashFunctionCount; i++) {int hash = hash1 + i * hash2;if (hash < 0) {hash =~hash;}int index = hash % bitSize;if (!bits.get(index)) {// 有一个数组位上没有被设置过,则表示不存在return false;}}return true;}
}

测试代码:

public class BloomFilterDemo {public static void main(String[] args) {// 预期存放十万个数据int dataCount = 1000000;// 预期误差率为 0.01double falsePositiveRate = 0.01;// 创建布隆过滤器MyBloomFilter bloomFilter = MyBloomFilter.create(dataCount, falsePositiveRate);// 添加数据for (int i = 0; i < dataCount; i++) {String data = String.valueOf(i);bloomFilter.add(data);}// 添加从 dataCount 起后 100000 个数据int falsePositiveCount = 0;for (int i = dataCount; i < dataCount+100000; i++) {String data = String.valueOf(i);if (bloomFilter.exist(data)) {// 如果判断存在,则表示误判了,统计falsePositiveCount++;}}System.out.println("误差个数:" + falsePositiveCount);}
}

运行 main() 方法,控制台信息如下:

在这里插入图片描述

可以看到误差率也是接近前面预设的 0.01%


五、guava 中的布隆过滤器

上面内容我们通过布隆过滤器的原理和一些推导公式,实现了布隆过滤器,不过一般也不会自己去手写布隆过滤器,因为有些包中已经实现了布隆过滤器,比如: guava

在前面布隆过滤器的实现中,有些代码我也是参考了 guava 中的 BloomFilter 去实现的

那如何使用 guava 给的布隆过滤器呢?

导入 guava 依赖(就是第四部分导入的依赖):

<!-- guava -->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>33.0.0-jre</version>
</dependency>

我们可以使用 BloomFilter 中的 create() 方法来创造布隆过滤器

在这里插入图片描述

比如:创建一个针对存储字符串类型的布隆过滤器

        // 创建布隆过滤器BloomFilter<String> bloomFilter = BloomFilter.create(// 过滤器只存储字符串类型的数据,字符集为 uft-8Funnels.stringFunnel(Charsets.UTF_8),// 预期存放数据量dataCount, // 预期误差率falsePositiveRate);

使用 put() 方法添加元素,mightContain() 方法判断元素是否存在

上述测试代码用 guava 的布隆过滤器可改写为:

package com.mike.spider;import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;public class BloomFilterDemo {public static void main(String[] args) {// 预期存放十万个数据int dataCount = 1000000;// 预期误差率为 0.01double falsePositiveRate = 0.01;// 创建布隆过滤器BloomFilter<String> bloomFilter = BloomFilter.create(// 过滤器只存储字符串类型的数据,字符集为 uft-8Funnels.stringFunnel(Charsets.UTF_8),// 预期存放数据量dataCount,// 预期误差率falsePositiveRate);// 添加数据for (int i = 0; i < dataCount; i++) {String data = String.valueOf(i);bloomFilter.put(data);}// 添加从 dataCount 起后 100000 个数据int falsePositiveCount = 0;for (int i = dataCount; i < dataCount+100000; i++) {String data = String.valueOf(i);if (bloomFilter.mightContain(data)) {// 如果判断存在,则表示误判了,统计falsePositiveCount++;}}System.out.println("误差个数:" + falsePositiveCount);}
}

参考文章:

Java实现布隆过滤器的几种方式:https://blog.csdn.net/weixin_43888891/article/details/131407465

布隆过滤器(一):https://hardcore.feishu.cn/docs/doccntUpTrWmCkbfK1cITbpy5qc

布隆过滤器(Bloom Filter)- 原理、实现和推导:https://blog.csdn.net/hlzgood/article/details/109847282

布隆过滤器讲解及基于Guava BloomFilter案例:https://blog.csdn.net/weixin_42675423/article/details/130025590

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

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

相关文章

LeetCode 145. 二叉树的后序遍历

145. 二叉树的后序遍历 给你一棵二叉树的根节点 root &#xff0c;返回其节点值的 后序遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[3,2,1]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&…

k8s实践(14)--scheduler调度器和pod调度策略

一、scheduler调度器 1、kube-scheduler简介 k8s实践(10) -- Kubernetes集群运行原理详解 介绍过kube-scheduler。 kube-scheduler是运行在master节点上&#xff0c;其主要作用是负责资源的调度&#xff08;Pod调度&#xff09;&#xff0c;通过API Server的Watch接口监听新建…

three.js实现信号波效果

three.js实现信号波效果 图例 步骤 创建平面&#xff0c;添加贴图&#xff0c;平移几何体缩放 代码 <template><div class"app"><div ref"canvesRef" class"canvas-wrap"></div></div> </template><…

Apache JMeter 5.5: 新手指南

如何获取并运行 JMeter 首先&#xff0c;要使用 JMeter&#xff0c;你需要从官网获取软件包。前往 Apache JMeter 的官方页面&#xff0c;然后下载所 需的压缩文件。 配置和启动 JMeter 获取了 JMeter 后&#xff0c;由于它是无需安装即可使用的工具&#xff0c;直接解压下载…

AirSim 的 ROS 功能包测试

参考链接&#xff1a; Ubuntu18.04搭建AirSimROS仿真环境_airsim ros-CSDN博客 ROS: AirSim ROS Wrapper - AirSim 1.编译 ros 包&#xff08;必须是 gcc-8&#xff09; 如果您的默认 GCC 不是 8 或更高&#xff08;使用 gcc --version 检查&#xff09;&#xff0c;那么编译…

软件测试|Python urllib3库使用指南

简介 当涉及到进行网络请求和处理HTTP相关任务时&#xff0c;Python的urllib3库是一个强大且灵活的选择。它提供了一种简单的方式来执行HTTP请求、处理响应和处理连接池&#xff0c;使得与Web服务进行交互变得更加容易。本文将详细介绍如何使用urllib3库进行网络请求。 安装u…

java.net.ConnectException: Connection refused: connect已解决

&#x1f95a;今日鸡汤&#x1f95a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 —— 《枫》 遇见问题莫着急&#xff0c;着急也没用~&#x1f636;‍&#x1f32b;️ 目录 &#x1f9c2;1.令人发麻的问题 &am…

vagrant 用户名密码登录

正常登录后 sudo -i 切换到root权限 vim /etc/ssh/vim sshd_config 将PasswordAuthentication no设置 为yes 重启sshd.service服务 systemctl restart sshd.service

软件测试|详解 Pytest 参数化:简化测试用例的编写

简介 Pytest 是一个广泛使用的 Python 测试框架&#xff0c;它提供了丰富的功能来编写和执行测试用例。其中一个强大的特性是参数化&#xff0c;它允许我们通过一种简洁的方式运行多个输入参数的相似测试用例&#xff0c;从而减少冗余的代码。本文将详细介绍 Pytest 的参数化功…

MT36291 2.5A 高效的1.2MHz电流模式升压转换器 DCDC管理芯片 航天民芯

描述 MT36291是一个恒定频率、6引脚SOT23电流模式升压转换器&#xff0c;旨在用于小型、低功耗的应用。MT36291的开关频率为1.2MHz&#xff0c;并允许使用2mm或更低高度的微小、低成本的电容器和电感器。内部软启动导致注入电流小&#xff0c;延长电池寿命。MT36291的特点是在光…

二线厂商-线上测评-大数据开发

曾经投递过一些中级岗位&#xff0c;在面试之前&#xff0c;会通过邮件的方式把性格测试的题目发给你让你做一下。 一般分为单选题&#xff0c;多选题&#xff0c;性格测试题&#xff0c;认知理解题等等。 大概做了一个小时吧。 单选题&#xff1a; 感觉就是类似于以前高中时候…

golang实现加密解密文档

golang实现加密解密文档 package mainimport ("bytes""crypto/aes""crypto/cipher""crypto/rand""encoding/base64""flag""fmt""io""io/ioutil" )func main() {encodePtr : flag.…

中小企业实施了MES系统后,同样具备大企业的生产能力

工业4.0、智能制造是当前制造业最热门的话题。数字化工厂是实现智能制造的基础&#xff0c;在建设数字化工厂的过程中&#xff0c;MES系统是核心也是最重要的一环。万界星空MES系统是企业信息数据集成的纽带&#xff0c;可帮助企业实现监控与实际生产过程的同步化&#xff0c;全…

2024--Django平台开发-Django知识点(三)

day03 django知识点 项目相关路由相关 urls.py视图相关 views.py模版相关 templates资源相关 static/media 1.项目相关 新项目 开发时&#xff0c;可能遇到使用其他的版本。虚拟环境 老项目 打开项目虚拟环境 1.1 关于新项目 1.系统解释器命令行【学习】 C:/python38- p…

BUUCTF ---> Encrypto

转眼就一月十号了&#xff0c;本来今天不想更的&#xff0c;&#xff08;因为我懒&#xff09;是因为明天要考python&#xff0c;好像还不止 但是呢&#xff0c;发现BUUCTF的密码学模块刚好可以用到py的脚本&#xff0c;那就当时复习一下吧&#xff01;&#xff01; 这里就要介…

您的计算机已被.LIVE勒索病毒感染?恢复您的数据的方法在这里!

引言&#xff1a; 在数字时代&#xff0c;.LIVE勒索病毒如暗夜中的黑暗调&#xff0c;威胁着个人和企业的数字安全。本文将深入介绍.LIVE勒索病毒的特征&#xff0c;提供解密数据的方法&#xff0c;并讨论预防措施&#xff0c;让我们共同筑起数字世界的防护之墙。数据的重要性…

C++学习笔记——返回对象

一、返回对象 当我们说一个函数返回对象时&#xff0c;意味着该函数的返回值是一个对象。这种情况下&#xff0c;函数可以通过创建对象的副本、返回对象的引用或者返回对象的指针来实现。 返回对象的副本&#xff1a; 当一个函数返回对象的副本时&#xff0c;函数内部会创建一…

Mac上修复Gitee报错 Oauth: Access token is expired

一. 背景&#xff1a; 最近在gitee上拉了两次项目&#xff0c;两次使用的邮箱密码不一致&#xff08;换绑邮箱&#xff09;&#xff0c;第一次在idea中拉取后端项目&#xff0c;第二次在webstorm中拉取前端项目&#xff0c;出现该异常&#xff0c;记录下解决方案 二. 错误回显…

简易机器学习笔记(九)LeNet实例 - 在眼疾识别数据集iChallenge-PM上的应用

前言 上一节大概讲了一下LeNet的内容&#xff0c;这一章就直接来用&#xff0c;实际上用一下LeNet来进行训练和分类试试。 调用的数据集&#xff1a; https://aistudio.baidu.com/datasetdetail/19065 说明&#xff1a; 如今近视已经成为困扰人们健康的一项全球性负担&…

整形数据在内存中的存储(C语言)

整形数据在内存中的存储 1.整形家族2.(原码、反码、补码)基础知识3.大小端3.1 什么是大小端3.2 为什么有大端和小端3.3 一道关于大小端字节序的面试题3.4 关于整形数据存储的题目(7题)3.4.13.4.23.4.33.4.43.4.53.4.63.4.7 4.总结 1.整形家族 signed可省可不省&#xff0c;一般…