비트 맵 (BitMap) & & 블 룸 필터 (BloomFilter)

[면접 문제] 반복 되 지 않 는 부호 없 는 정수 40 억 개 를 주 고 순 서 를 정 하지 않 았 습 니 다.부호 가 없 는 정 수 를 주 고 40 억 개의 숫자 에 있 는 지 여 부 를 어떻게 신속하게 판단 합 니까?
●  이 문 제 를 보고 가장 먼저 떠 오 르 는 방법 은 이 40 억 개의 수 를 옮 겨 다 니 며 순서대로 판단 하 는 것 이다. 그러나 이 방법 에 필요 한 메모리 가 매우 크다. 약 15G (400000000 * 4 이다. ÷ (1024 * 1024 * 1024)) 를 보면 이 알고리즘 은 바람 직 하지 않다 는 것 을 알 수 있다.
●  메모리 가 충분 하 다 면, 우 리 는 비트 맵 을 통 해 이 루어 질 수 있 습 니 다. 비트 맵 의 한 배열 의 모든 바 이 너 리 는 하나의 데 이 터 를 표시 하고, 한 사람 은 0, 1 로 현재 이 위치 에 값 이 저장 되 어 있 는 지 여 부 를 표시 하 며, 마찬가지 로 하 쉬 를 이용 하여 저장 하 는 방법 입 니 다.이 방법 은 메모 리 를 크게 줄 일 수 있 습 니 다. 이 문 제 는 int 형식 으로 32 비트 를 프로 그래 밍 할 수 있 습 니 다. 필요 한 메모리 공간 은 15G 에서 500 M 으로 낮 출 수 있 습 니 다.
구체 적 인 실현 은 다음 과 같다.
#pragma
class BitMap//  
{
public:
    BitMap()
    :_size(0)
    {}
    BitMap(size_t size)//size     ,      
    : _size(size)
    {//     size / 32 + 1   5  1( 1:        size,  10%8=1,    2)
    	_a.resize((size >> 5) + 1);
    }
    //   ,  1       num    32-num;
    void Set(size_t x)//  x , 1
    {
    	size_t index = x >> 5;
    	size_t num = x % 32;//eg:x = 35,num = 3,      _a[1]   001
    	++_size;
    	_a[index] |= 1 << num;//1  3 ,  | _a     
    }
    void Remove(size_t x)//  x , 0
    {
    	size_t index = x >> 5;
    	size_t num = x % 32;//eg:x = 35,num = 3,      _a[1]   110
    	--_size;
    	_a[index] &= (~(1 << num));//1  3   0,  & _a     0
    }
    bool Test(size_t x)//      
    {
    	size_t index = x >> 5;
    	size_t num = x % 32;
    	if (_a[index] & (1 << num))//      1,   
    	{
    		return true;
    	}
    	return false;
    }
    void Resize(size_t size)//    
    {
    	_a.resize((size >> 5) + 1);
    }
    size_t Size()//        
    {
    	return _size;
    }
    size_t Capacity()//  int    
    {
    	return _a.size();
    }
    void Print()
    {
    	for (size_t i = 0; i < _a.size(); i++)
    	{
    		cout << _a[i] << " " << endl;
    	}
    	cout << endl;
    }
private:
	vector<size_t> _a;
	size_t _size;
};

void TestBitMap()
{
	BitMap bm(65);
	bm.Set(3);
	bm.Set(4);
	bm.Set(5);
	bm.Print();
	cout << "is 4 EXTST? " << bm.Test(4) << endl;
	cout << "is 8 EXTST? " << bm.Test(8) << endl;

	bm.Remove(4);
	cout << "is 4 EXTST? " << bm.Test(4) << endl;
	bm.Print();

	cout << "size: " << bm.Size() << endl;
	cout << "capacity: " << bm.Capacity() << endl;
	bm.Resize(100);
	cout << "capacity: " << bm.Capacity() << endl;
}

●  Bloom Filter 는 공간 효율 이 높 은 랜 덤 데이터 구조 로 Bloom filter 는 bit - map 에 대한 확장 이 라 고 볼 수 있 습 니 다. 그 원 리 는:
      원소 가 집합 에 들 어 갔 을 때, K 개. Hash 함 수 는 이 요 소 를 비트 배열 (Bit array) 의 K 점 으로 매 핑 하여 설정 합 니 다. 1。검색 할 때 이 점 들 이 모두 1 인지 아 닌 지 를 보면 집합 에 있 는 지 없 는 지 알 수 있다.
1. 만약 에 이런 점 에 0 이 있 으 면 검색 요소 가 없 을 것 입 니 다.
2. 모두 1 이 라면 검색 요소 가 있 을 수 있 습 니 다.
소수 표 와 6 개의 해시 알고리즘 을 사 용 했 습 니 다.
#pragma

size_t _GetNextPrime(size_t size)//   :       
{
	const int _PrimeSize = 28;
	static const unsigned long _PrimeList[_PrimeSize] =
	{
		53ul, 97ul, 193ul, 389ul, 769ul,
		1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
		49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
		1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
		50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
		1610612741ul, 3221225473ul, 4294967291ul
	};
	for (size_t i = 0; i < _PrimeSize; ++i)
	{
		if (_PrimeList[i] > size)
		{
			return _PrimeList[i];
		}
		return _PrimeList[i - 1];
	}
	return _PrimeList[_PrimeSize];//  size           ,        
}
//6        
template<class T>
size_t BKDRHash(const T * str)
{
	register size_t hash = 0;
	while (size_t ch = (size_t)*str++)
	{
		hash = hash * 131 + ch;   //      31、131、1313、13131、131313..         
	}
	return hash;
}
template<class T>
size_t SDBMHash(const T *str)
{
	register size_t hash = 0;
	while (size_t ch = (size_t)*str++)
	{
		hash = 65599 * hash + ch;
		//hash = (size_t)ch + (hash << 6) + (hash << 16) - hash;  
	}
	return hash;
}
template<class T>
size_t RSHash(const T *str)
{
	register size_t hash = 0;
	size_t magic = 63689;
	while (size_t ch = (size_t)*str++)
	{
		hash = hash * magic + ch;
		magic *= 378551;
	}
	return hash;
}
template<class T>
size_t APHash(const T *str)
{
	register size_t hash = 0;
	size_t ch;
	for (long i = 0; ch = (size_t)*str++; i++)
	{
		if ((i & 1) == 0)
		{
			hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
		}
		else
		{
			hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
		}
	}
	return hash;
}
template<class T>
size_t JSHash(const T *str)
{
	if (!*str)        //        ,            0  
		return 0;
	register size_t hash = 1315423911;
	while (size_t ch = (size_t)*str++)
	{
		hash ^= ((hash << 5) + ch + (hash >> 2));
	}
	return hash;
}
template<class T>
size_t DEKHash(const T* str)
{
	if (!*str)        //             0  
		return 0;
	register size_t hash = 1315423911;
	while (size_t ch = (size_t)*str++)
	{
		hash = ((hash << 5) ^ (hash >> 27)) ^ ch;
	}
	return hash;
}
//6        6         
template<class T>
struct _HashFunc1
{
	size_t operator()(const T& str)
	{
		return BKDRHash(str.c_str());
	}
};
template<class T>
struct _HashFunc2
{
	size_t operator()(const T& str)
	{
		return SDBMHash(str.c_str());
	}
}; 
template<class T>
struct _HashFunc3
{
	size_t operator()(const T& str)
	{
		return RSHash(str.c_str());
	}
}; 
template<class T>
struct _HashFunc4
{
	size_t operator()(const T& str)
	{
		return APHash(str.c_str());
	}
}; 
template<class T>
struct _HashFunc5
{
	size_t operator()(const T& str)
	{
		return JSHash(str.c_str());
	}
};
template<class T>
struct _HashFunc6
{
	size_t operator()(const T& str)
	{
		return DEKHash(str.c_str());
	}
};

부 릉 필 터 는 다음 과 같이 구체 적 으로 실 현 됩 니 다.
#define _CRT_SECURE_NO_WARNINGS 1

template<class K = string, 
class HashFunc1 = _HashFunc1<K>, class HashFunc2 = _HashFunc2<K>, 
class HashFunc3 = _HashFunc3<K>, class HashFunc4 = _HashFunc4<K>, 
class HashFunc5 = _HashFunc5<K>, class HashFunc6 = _HashFunc6<K>>
class BloomFilter
{
public:
	BloomFilter(size_t size = 0)
	{
		_capacity = _GetNextPrime(size);
		_bm.Resize(_capacity);//  BitMap Resize    
	}
	void Set(const K& key)
	{
		size_t index1 = HashFunc1()(key);
		size_t index2 = HashFunc2()(key);
		size_t index3 = HashFunc3()(key);
		size_t index4 = HashFunc4()(key);
		size_t index5 = HashFunc5()(key);
		size_t index6 = HashFunc6()(key); 

		_bm.Set(index1 % _capacity);//     index1 % _capacity,  BitMap Set
		_bm.Set(index2 % _capacity);
		_bm.Set(index3 % _capacity);
		_bm.Set(index4 % _capacity);
		_bm.Set(index5 % _capacity);
		_bm.Set(index6 % _capacity);
	}
	bool Test(const K& key)
	{
		//       0    ,    
		size_t index1 = HashFunc1()(key);
		if (!_bm.Test(index1 % _capacity))
			return false;

		size_t index2 = HashFunc2()(key);
		if(!_bm.Test(index2 % _capacity))
			return false;

		size_t index3 = HashFunc3()(key);
		if(!_bm.Test(index3 % _capacity))
			return false;

		size_t index4 = HashFunc4()(key);
		if(!_bm.Test(index4 % _capacity))
			return false;

		size_t index5 = HashFunc5()(key);
		if(!_bm.Test(index5 % _capacity))
			return false;

		size_t index6 = HashFunc6()(key);
		if(!_bm.Test(index6 % _capacity))
			return false;
		return true;
	}
private:
	BitMap _bm;
	size_t _capacity;
};
void TestBloomFilter()
{
	BloomFilter<> bf(50);
	bf.Set("Scen");
	bf.Set("  ");
	bf.Set("http://blog.51cto.com/user_index.php?action=addblog_new&job=modify&tid=1773181");
	cout << "exist? " << bf.Test("Scen") << endl;
	cout << "exist? " << bf.Test("Scenluo") << endl;
	cout << "exist? " << bf.Test("  ") << endl;
	cout << "exist? " << bf.Test("http://blog.51cto.com/user_index.php?action=addblog_new&job=modify&tid=1773181") << endl;
	cout << "exist? " << bf.Test("http://blog.51cto.com/user_index.php?action=addblog_new&job=modify&tid=1773131") << endl;

}

부 릉 필터 의 결함:
1. 오산 율 (False Positive) 은 그 중의 하나 이다.
      저 장 된 요소 의 수량 이 증가 함 에 따라 오산 율 이 증가한다.그러나 원소 의 수량 이 너무 적 으 면 산열 표를 사용 하면 충분 하 다.그래서 우 리 는 여러 개의 해시 표 로 데 이 터 를 저장 했다.그러면 문제 가 또 생 겼 다. 우 리 는 좀 더 쓸 까, 아니면 좀 적 게 쓸 까?해시 시 계 를 많이 사용 하면 위의 문제 와 같이 해시 하나 에 500 M 이 필요 하 다. 그러면 많이 넣 을 수록 메모리 가 차지 하 는 것 이 아 닐 까?너무 적 으 면 오산 율 이 높 은 거 아니 야? 그 러 니까 적당 한 걸 로.나의 프로그램 실현 은 여섯 개의 해시 표를 얻 은 것 이다.
2. 현재 위치 가 0 이면 존재 하지 않 지만 1 이면 존재 하지 않 습 니 다.
일반 부 릉 필 터 는 디자인 만 지원 하고 삭 제 는 지원 되 지 않 습 니 다.인용 수 를 통 해 계산 할 수 있 지만 공간 낭비 가 크다.

좋은 웹페이지 즐겨찾기