4.哈希扩展

一.位图

二.bit_set大致实现

cpp 复制代码
#pragma once
#include <vector>

namespace ltw
{
    template<size_t N>
    class bit_set
    {
    public:
        bit_set()
        {
            _bs.resize(N / 32 + 1);
        }
        //x映射的位标记为1
        void set(size_t x)
        {

        }
        //x映射的位标记为0
        void reset(size_t x)
        {

        }
        //返回x映射的位是否为1
        bool test(size_t x)
        {

        }
    private:
        std::vector<int> _bs;
    };
}

三.set实现

这里我们不用考虑大小端(因为这里只和我们的操作有关系,和底层的大小端没有关系)

cpp 复制代码
//x映射的位标记为1
        void set(size_t x)
        {
            size_t i = x / 32;
            size_t j = x % 32;
            _bs[i] |= (1 << j);
        }

四.reset的实现

cpp 复制代码
//x映射的位标记为0
        void reset(size_t x)
        {
            size_t i = x / 32;
            size_t j = x % 32;
            _bs[i] &= (~(1 << j));
        }

五.test的实现

cpp 复制代码
//返回x映射的位是否为1
        bool test(size_t x)
        {
            size_t i = x / 32;
            size_t j = x % 32;
            return (_bs[i] & (1 << j));
        }

六.总结和测试

实现了这个位图,我们就能够处理42亿个数据了(节省32倍空间,和很多时间(位运算的高效))

cpp 复制代码
#include <iostream>

using namespace std;

#include "BitSet.h"

int main()
{
    ltw::bitset<100> bs;
    bs.set(10);
    bs.set(20);
    bs.set(30);
    cout << bs.test(10) << endl;
    cout << bs.test(20) << endl;
    cout << bs.test(30) << endl;
    cout << bs.test(40) << endl;

    ltw::bitset<UINT_MAX> bs2;
    return 0;
}

七.C++标准bitset

bitset - C++ Reference

但是,库里面是开不出来的

cpp 复制代码
int main()
{
    // std::bitset<100> bs;
    // cout << sizeof(bs) << endl;
    // bs.set(10);
    // bs.set(20);
    // bs.set(30);
    // cout << bs.test(10) << endl;
    // cout << bs.test(20) << endl;
    // cout << bs.test(30) << endl;
    // cout << bs.test(40) << endl;

    std::bitset<-1>* ptr = new std::bitset<-1>();
    return 0;
}

我们可以通过new,将其在堆上面开出来

八.位图的优缺点

九.查找

我们可以用两个位置,来表示一个数出现的次数

1.twobitset设计

cpp 复制代码
template<size_t N>
    class twobitset
    {
    public:
        twobitset()
        {
            _bs1 = bitset<N>();
            _bs2 = bitset<N>();
        }
    private:
        bitset<N> _bs1;
        bitset<N> _bs2;
    };

2.set的实现

如果出现1次,就标记一位,直到,大于2次我们就不进行其他的操作了

cpp 复制代码
void set(size_t x)
        {
            bool bit1 = _bs1.test(x);
            bool bit2 = _bs2.test(x);
            if(bit1 && !bit2)//00 -> 01
            {
                _bs2.set(x);
            }
            else if(!bit1 && bit2)
            {
                _bs1.set(x);
                _bs2.reset();
            }
            else if(bit1 && !bit2)
            {
                _bs2.set(x);
            }
        }

3.reset的实现

cpp 复制代码
void reset(size_t x)
        {
            _bs1.reset(x);
            _bs2.reset(x);
        }

4.get_count的实现

cpp 复制代码
//0次,表示出现0次
        //1次,表示出现1次
        //2次,表示出现2次
        //3次,表示出现2次及以上
        int get_count(size_t x)
        {
            bool bit1 = _bs1.test(x);
            bool bit2 = _bs2.test(x);
            if(bit1 && !bit2)
            {
                return 1;
            }
            else if(!bit1 && bit2)
            {
                return 2;
            }
            else if(bit1 && bit2)
            {
                return 3;
            }
            else
            {
                return 0;
            }
        }

5.测试

cpp 复制代码
#include <iostream>
#include <bitset>

using namespace std;

void test_bitset3()
{
    int a1[] = { 5,7,9,2,5,99,5,5,7,5,3,9,2,55,1,5,6 };
    int a2[] = { 5,3,5,99,6,99,33,66 };

    bitset<100> bs1;
    bitset<100> bs2;

    for (auto e : a1)
    {
        bs1.set(e);
    }

    for (auto e : a2)
    {
        bs2.set(e);
    }

    for (size_t i = 0; i < 100; i++)
    {
        if (bs1.test(i) && bs2.test(i))
        {
            cout << i << endl;
        }
    }
}

十.布隆过滤器

十一.布隆过滤器误判率

十二.布隆过滤器代码大致实现

cpp 复制代码
#pragma once
#include "BitSet.h"

template<class K,size_t N,size_t X,class Hash1,class Hash2,class Hash3>
class BloomFilter
{
public:
private:
    static const size_t M = N * X;
    ltw::bitset<M> _bs;
};

这里我们开的空间是N * X个位置

十三.set的实现

根据三个不同的hash函数,将我们的值进行映射

cpp 复制代码
void Set(const K& key)
    {
        size_t hash1 = Hash1()(key) % M;
        size_t hash2 = Hash2()(key) % M;
        size_t hash3 = Hash3()(key) % M;
        _bs.set(hash1);
        _bs.set(hash2);
        _bs.set(hash3);
    }

十四.test的实现

cpp 复制代码
bool test(const K& key)
    {
        size_t hash1 = Hash1()(key) % M;
        if(!_bs.test(hash1))
        {
            return false;
        }
        size_t hash2 = Hash2()(key) % M;
        if(!_bs.test(hash2))
        {
            return false;
        }
        size_t hash3 = Hash3()(key) % M;
        return _bs.test(hash3);
    }

十五.对应的hash函数

我们可以直接在网上进行查找对应的hash函数

cpp 复制代码
#include <string>
using namespace std;

// BKDR Hash 算法
struct HashFuncBKDR
{
    /// @detail 本算法由于在Brian Kernighan与Dennis Ritchie的《The C Programming Language》
    /// 一书被展示而得名,是一种简单快捷的hash算法,也是Java目前采用的字符串的Hash算法(累乘因子为31)。
    size_t operator()(const string& s)
    {
        size_t hash = 0;
        for (auto ch : s)
        {
            hash *= 31;
            hash += ch;
        }
        return hash;
    }
};

// AP Hash 算法 (Arash Partow)
struct HashFuncAP
{
    // 由Arash Partow发明的一种hash算法。
    size_t operator()(const 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;
    }
};

// DJB Hash 算法 (Daniel J. Bernstein)
struct HashFuncDJB
{
    // 由Daniel J. Bernstein教授发明的一种hash算法。
    size_t operator()(const string& s)
    {
        size_t hash = 5381;
        for (auto ch : s)
        {
            hash = hash * 33 ^ ch;
        }
        return hash;
    }
};

或者也可以自己写(一般都是乘31,131等)

十六.测试

cpp 复制代码
void TestBloomFilter()
{
    BloomFilter<10> bf;
    bf.Set("猪八戒");
    bf.Set("孙悟空");
    cout << bf.test("猪八戒") << endl;
    cout << bf.test("孙悟空") << endl;
    cout << bf.test("沙僧") << endl;
    cout << bf.test("唐僧") << endl;
}

我们可以看到是没有什么错误的(但是我们测试的数据太少了)

如果我们想降低冲突率,是可以增加我们的X,来进行调整的

十七.误判率计算

这里只是用公式进行计算

误判率其实差不多(真实的和公式计算的)

十八.布隆过滤器的删除问题

默认的布隆过滤器是不支持删除的(删除一个值可能会影响另一个值)

如果使用引用计数,会导致我们的计数成本变高

所以,引用计数会导致数据不正确(我们就要定时重建布隆过滤器)

十九.布隆过滤器的应用

二十.海量数据处理

这本质就将A文件和B文件的相同的字符串,会被映射到同一个小空间(公用一个空间)

相关推荐
满分观察网友z2 小时前
刷 LeetCode 看不懂题解?我做了一个能"播放"算法的开源可视化平台
前端·算法·leetcode
70asunflower2 小时前
CUDA基础知识巩固检验练习题【附有参考答案】(6)
c++·人工智能·cuda
普通网友2 小时前
探索Perl在ASIC中的应用:深入标量、数组和哈希的应用
scala·哈希算法·perl
Σίσυφος19002 小时前
PCL聚类 之K-Means
算法·kmeans·聚类
Flying pigs~~2 小时前
机器学习之数据挖掘时间序列预测
人工智能·算法·机器学习·数据挖掘·线性回归
仰泳的熊猫2 小时前
题目1882:蓝桥杯2017年第八届真题-k倍区间
数据结构·c++·算法·蓝桥杯
Mikowoo0072 小时前
Visual Studio 2022 下CUDA程序开发
c++·visual studio
Darkwanderor2 小时前
图论——拓扑排序和图上DP
c++·算法·动态规划·图论·拓扑排序
有时间要学习2 小时前
面试150——第六周
算法·面试·深度优先