가장 간단한 방법은 bool type 배열을 하나 만들고, 입력 정수를 배열 원소의 인덱스로 취급하는 것이다.
data[x] = true
이런식으로 설정. 새 원소 삽입 시 해당 인덱스의 배열 값을 1로 설정하는 것이다.하지만 문제가 발생할 수 있다.
위 문제 해결을 위해 어떤 Type의 값이든 원하는 범위의 정수로 매핑하는 function을 만들 수 있다. (hashing function)
가장 간단한 hashing function은 나머지를 반환하는 modulo function이 있다. (% 연산)
하지만 (9 % 7), (16 % 7) 모두 2를 반환한다. 여기에 9가 들어갔는지, 16이 들어갔는지 모른다 ! 해당 x가 아니라 다른 값이 들어있을 수 있다는 것이다. 이런 현상을 collision이라한다.
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using uint = unsigned int;
class hash_map
{
std::vector<std::list<int>> data;
public:
hash_map(sizt_t n)
{
data.resize(n);
}
void insert(uint value)
{
// value 값을 항상 hash map(data vector)에 추가
int n = data.size();
data[vaue % n].push_back(value);
std::cout << value << "삽입" << std::endl;
}
bool find(uint value)
{
// 특정 원소가 해시 맵에 있는지 확인하는 look-up function
int n = data.size();
auto& entries = data[value % n];
return std::find(entries.begin(), entries.end(), value) != entries.end();
}
void erase(uint value)
{
int n = data.size();
auto& entries = data[value % n];
auto iter = std::find(entries.begin(), entries.end(), value);
if (iter != entries.end())
{
entries.erase(iter);
}
}
};
체이닝과 다르게 모든 원소를 해시 테이블 내부에 저장하는 방식
선형 탐색
이차함수 탐색(quadratic probing)
std::hash<stdd::string>(std::string)
함수 객체를 제공std::unordered_set<key>
, std::unordered_map<key, value>
형태로 템플릿 형태로 바꿔 모든 데이터 타입에 대해 사용할 수 있는 해시 테이블을 제공한다.#include <iostream>
#include <unordered_map>
#include <unordered_set>
void find(const std::unordered_map<int>& container, const int element)
{
if (container.find(element) == container.end())
std::cout << element << " serach fail " << std::endl;
else
std::cout << element << " search success " << std::endl;
}
void find(const std::unordered_map<int, int>& container, const int element)
{
auto it = container.find(element);
if (it == container.end())
std::cout << element << " search fail " << std::endl;
else
std::cout << element << " search success " << it->second << std::endl;
int main()
{
std::unordered_set<int> set = {1, 2, 3, 4, 5};
set.insert(2);
find(set, 4);
set.erase(2);
std::unordered_map<int, int> squareMap;
squareMap.insert({2, 4});
squareMap[3] = 9; // insert
find(squareMap, 10);
std::unordered_multiset, multimap
을 사용해야한다.bucket_count()
, load_factor()
, max_bucket_count()
등 함수를 통해 컨테이너 내부에서 사용되는 설정 값을 알 수 있다.rehash()
함수를 통해 재해싱 수행도 가능하다.#include <boost/functional/hash.hpp>
strcut Car
{
...
};
struct CarHasher
{
std::size_t operator()(const Car& car) const
{
std::size_t seed = 0;
boost::hash_combine(seed, car.model);
boost::hash_combine(seed, car.brand);
return seed;
}
};
struct CarComparator
{
bool operator()(const Car& car1, const Car& car2) const
{
return (car1.model == car2.model) && (car1.brand == car2.brand);
}
};
std::unordered_set<Car, CarHaser, CarComparator> carSet;
std::unordered_map<Car, std::string, CarHasher, CarComparator> carDescriptionMap;
0 0 0 0 0 0 0
Hash(100) = {2, 0, 2} 100 삽입
1 0 1 0 0 0 0
Hash(54) = {5, 0, 4} 54 삽입
1 0 1 0 1 1 0
Hash(82) = {5, 4, 0} 82 삽입
1 0 1 0 1 1 0
Hash(5) = {5, 0, 0} 5 검색
1 0 1 0 1 1 0 뭐지 ? 5가 없는데 1이 나오네 ? False Positive
Hash(50) = {1, 0, 4} 50 검색
1 0 1 0 1 1 0 중간에 0이 있으므로 False 절대 없음
#include <iostream>
#include <vector>
class bloom_filter
{
std::vector<bool> data;
int nBits;
int hash(int num, int key)
{
switch (num)
{
case 0: return key % nBits;
case 1: return (key / 7) % nBits;
case 2: return (key / 11) % nBits;
}
return 0;
}
public:
bloom_filter(int n) : nBits(n)
{
data = std::vector<bool>(nBits, false);
}
void lookup(int key)
{
// 모든 비트가 1로 설정되었는지만 검사하면 된다.
bool result = data[hash(0, key)] & data[hash(1, key)] & data[hash(2, key)];
if (result)
{
std::cout << " mady be there " << std::endl;
}
else
{
std::cout << " Never there " << std::endl;
}
}
void insert(int key)
{
data[hash(0, key)] = true;
data[hash(1, key)] = true;
data[hash(2, key)] = true;
}
};
int main()
{
bloom_filter bf(7);
bf.insert(100);
bf.insert(54);
bf.insert(82);
bf.lookup(5);
bf.lookup(50);
}