Skip to content

Commit 746ee2d

Browse files
author
VirtualRoyalty
committed
(pre) final commit
1 parent 4bc8718 commit 746ee2d

30 files changed

+1968
-1951
lines changed

README.md

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,12 @@
1010

1111
1) Метод цепочек;
1212

13-
2) Метод открытой адресации (см. Кормен, 3-е издание, 11.4 Открытая адреация);
13+
2) Метод открытой адресации
1414

15-
3) Метод кукушки (см. https://en.wikipedia.org/wiki/Cuckoo_hashing).
15+
3) Метод кукушки
1616

17-
4) Для интереса, предлагается также сделать сравнения со стандартными средствами языка c++: std::map, std::hash_map. Вдруг получится их обогнать.
17+
4) std::map, std::hash_map
1818

19-
**Замечание:**
20-
В качестве хеш-функций используйте только функции из универсальных
21-
семейств, про них рассказывалось на семинарах. Список универсальных хеш-функций
22-
можно найти здесь https://en.wikipedia.org/wiki/Universal_hashing.
2319

2420
**Что измеряется?**
2521

@@ -29,35 +25,20 @@
2925

3026
3) Время поиска.
3127

32-
**Более конкретно об измерении:**
33-
Нужно выбрать какое-то стартовое значение N, скажем 100, выбрать шаг step, скажем пусть
34-
step = 100, и выбрать максимальное значение, скажем 100 000. После чего нужно для каждого
35-
N с шагом step от минимального значения до максимального построить таблицу размера N
36-
(из случайных элементов, или сделать выборку из заранее подготовленной базы) и
37-
произвести одну или несколько операций (если несколько, скажем 10, то нужно усреднить).
38-
Измеряем именно время одной операции. Некоторые допускают ошибку и делают N вставок
39-
с замером времени, но не понятно, что в итоге Вы измерили.
4028

4129
**Входные данные:**
4230

4331
a) Случайные натуральные числа.
4432

45-
б) Случайные вектора или строки.
33+
б) Случайные строки.
4634

4735
с) Очень бы хотелось увидеть как поведут себя таблицы на real life данных, например на
4836
словарях или словах какого нибудь литературного произведения.
4937

50-
**Ваш вывод должен содержать:**
38+
**Вывод:**
5139

5240
1) График зависимости скорости вставки от количества элементов в таблице;
5341

5442
2) График зависимости скорости удаления от количества элементов в таблице;
5543

5644
3) График зависимости скорости поиска от количества элементов в таблице;
57-
58-
На каждом графике должно быть несколько кривых, по одной или больше для каждого
59-
подхода.
60-
61-
Заметим также, что таблицы из подходов 1) и 2) имеют дополнительный параметр m
62-
– ёмкость таблицы. Хорошо бы построить на графиках кривые для разных значений m,
63-
например m = 2n, m = n, m = ½ n. Но это не обязательно.

algorithm/include/ChainedHashMap.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,23 @@ template <typename K, typename V> class HashMap {
2121
this->tableSize = tableSize;
2222
table = new HashNode<K, V> *[tableSize]();
2323
}
24+
2425
bool insert(const K &key, const V &value) {
2526
unsigned int attempt = 0;
2627
unsigned int hashValue = hashFunc[key];
2728
HashNode<K, V> *prev = NULL;
2829
HashNode<K, V> *entry = table[hashValue];
29-
// std::cout << "before while" << &entry <<std::endl;
3030
while (entry != NULL && !comp.compare(entry->getKey(), key)) {
31-
// std::cout << "entry != NULL" << std::endl;
3231
++attempt;
3332
prev = entry;
3433
entry = entry->getNext();
3534
}
3635
collisions += attempt;
3736

3837
if (entry == NULL) {
39-
// std::cout << "entry == NULL" << std::endl;
4038
entry = new HashNode<K, V>(key, value);
4139

4240
if (prev == NULL) {
43-
// insert as first bucket
4441
table[hashValue] = entry;
4542
return true;
4643

@@ -50,7 +47,6 @@ template <typename K, typename V> class HashMap {
5047
}
5148

5249
} else {
53-
// just update the value
5450
entry->setValue(value);
5551
return true;
5652
}
@@ -70,8 +66,6 @@ template <typename K, typename V> class HashMap {
7066
entry = entry->getNext();
7167
}
7268
collisions += attempt;
73-
// std::cout << "Number of tries: "<< ccounter <<std::endl;
74-
// std::cout << "UKNOWN KEY!"<< std::endl;
7569
return false;
7670
}
7771

algorithm/include/ChainedHashNode.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ template <typename K, typename V> class HashNode {
77
// key-value pair
88
K _key;
99
V _value;
10-
// next bucket with the same key
1110
HashNode *_next;
12-
// disallow copy and assignment
1311
HashNode(const HashNode &);
1412
HashNode &operator=(const HashNode &);
1513

algorithm/include/CuckooHash.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#pragma once
2+
#include "UniversalHash.h"
3+
#include "primeNumbers.h"
4+
#include "utils.h"
5+
#include "primeNumbers.h"
6+
7+
template <typename K> class CuckooKeyHash {
8+
private:
9+
size_t tableSize;
10+
KeyHash<K> uniHashFunc;
11+
unsigned int a, b;
12+
13+
public:
14+
CuckooKeyHash(size_t tableSize)
15+
: tableSize(tableSize),
16+
uniHashFunc(tableSize),
17+
a(genRandomUid(1, 12)),
18+
b(genRandomUid(0, PRIME.size()-1))
19+
{};
20+
21+
unsigned int operator[](const K &key) const {
22+
unsigned int l = (uniHashFunc[key] << a) + PRIME[b];
23+
return (l >= tableSize) ? l % tableSize : l;
24+
}
25+
};
26+
27+
28+
template<> class CuckooKeyHash<std::string>{
29+
private:
30+
size_t tableSize;
31+
KeyHash<std::string> uniHashFunc;
32+
unsigned int a, b;
33+
34+
public:
35+
CuckooKeyHash(size_t tableSize)
36+
: tableSize(tableSize),
37+
uniHashFunc(tableSize),
38+
a(genRandomUid(1, 12)),
39+
b(genRandomUid(0, PRIME.size()-1))
40+
{};
41+
42+
unsigned int operator[](const std::string &key) const {
43+
unsigned int l = PRIME[b];
44+
for (auto i = 0; i < key.size(); ++i) {
45+
l = ((l << a)+l) + key[i];
46+
}
47+
return (l >= tableSize) ? l % tableSize : l;
48+
}
49+
};

algorithm/include/CuckooHashMap.h

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#pragma once
2+
#include <iostream>
3+
#include <math.h>
4+
5+
#include "utils.h"
6+
#include "CuckooHash.h"
7+
#include "OpenedHashNode.h"
8+
#include "VComparator.h"
9+
10+
11+
// m - tableSize
12+
template <typename K, typename V>
13+
class CuckooHashMap
14+
{
15+
private:
16+
// CuckooHashMap(const CuckooHashMap &item);
17+
// const CuckooHashMap& operator=(const CuckooHashMap& other);
18+
OpenHashNode<K, V>** table_left;
19+
OpenHashNode<K, V>** table_right;
20+
unsigned int max_cuckoo;
21+
unsigned int collisions = 0;
22+
CuckooKeyHash<K> hashFunc_1;
23+
CuckooKeyHash<K> hashFunc_2;
24+
size_t tableSize;
25+
VComparator<K> comp;
26+
bool verbose = 0;
27+
int numberOfInserts = 0, numberOfSearch = 0, numberOfRemove = 0, numberOfRehash = 0;
28+
public:
29+
// m - tableSize, n - numberOfElements m >= 2n
30+
CuckooHashMap(const size_t tableSize, const size_t numberOfElements):
31+
hashFunc_1(tableSize),
32+
hashFunc_2(tableSize),
33+
tableSize(tableSize),
34+
comp()
35+
36+
{
37+
if ((this->tableSize < 2*numberOfElements) && (verbose)) {
38+
std::cerr<<"\nWarning: m - tableSize, n - numberOfElements m < 2n!";
39+
}
40+
max_cuckoo = 6*log2(numberOfElements);
41+
table_left = new OpenHashNode<K, V>*[tableSize]();
42+
table_right = new OpenHashNode<K, V>*[tableSize]();
43+
}
44+
45+
void swap(OpenHashNode<K, V>* item, OpenHashNode<K, V>* item_) {
46+
OpenHashNode<K, V> temp_item = *item;
47+
*item = *item_;
48+
*item_ = temp_item;
49+
}
50+
51+
bool insert(const K& key, const V& value) {
52+
numberOfInserts++;
53+
auto* item = new OpenHashNode<K, V>(key, value);
54+
55+
for (auto i = 0; i < max_cuckoo; ++i) {
56+
unsigned int pos_1, pos_2;
57+
pos_1 = hashFunc_1[item->getKey()];
58+
pos_2 = hashFunc_2[item->getKey()];
59+
if ((table_left[pos_1] == NULL) ||
60+
(comp.compare(table_left[pos_1]->getKey(), item->getKey()))) {
61+
table_left[pos_1] = item;
62+
return true;
63+
}
64+
if ((table_right[pos_2] == NULL) ||
65+
(comp.compare(table_right[pos_2]->getKey(), item->getKey()))) {
66+
table_right[pos_2] = item;
67+
return true;
68+
}
69+
if (verbose) {
70+
//std::cout << "item:\n K, V = " << item->getKey() << ", " << item->getValue() << ", table K, V = " << table_left[pos_1]->getKey() << ", " << table_left[pos_1]->getValue() << "\n";
71+
}
72+
swap(item, table_left[pos_1]);
73+
collisions++;
74+
75+
if (verbose) {
76+
//std::cout << "item:\n K, V = " << item->getKey() << ", " << item->getValue() << ", table K, V = " << table_left[pos_1]->getKey() << ", " << table_left[pos_1]->getValue() << "\n";
77+
//std::cout << "pos_1: " << pos_1 << ", pos_2: " << pos_2 << std::endl;
78+
}
79+
}
80+
rehash();
81+
return insert(item->getKey(), item->getValue());
82+
}
83+
84+
bool search(const K& key, V& value) {
85+
numberOfSearch++;
86+
if (verbose) {
87+
std::cout << "\nsearch: key [" << key << "] ";
88+
}
89+
OpenHashNode<K, V>* entry;
90+
if ((entry = table_left[ hashFunc_1[key] ]) != NULL) {
91+
if (comp.compare(entry->getKey(), key)) {
92+
value = entry->getValue();
93+
if (verbose) {
94+
std::cout << "key equal: hash1: " << hashFunc_1[key] << " hash2: " << hashFunc_2[key] << std::endl;
95+
}
96+
// std::cout << entry->getKey() <<
97+
return true;
98+
}
99+
if (verbose) {
100+
std::cout << "not key equal: hash1: " << hashFunc_1[key] << " hash2: " << hashFunc_2[key] << std::endl;
101+
}
102+
}
103+
if ((entry = table_right[ hashFunc_2[key] ]) != NULL) {
104+
if (comp.compare(entry->getKey(), key)) {
105+
value = entry->getValue();
106+
if (verbose) {
107+
std::cout << "search(key equal): hash1: " << hashFunc_1[key] << " hash2: " << hashFunc_2[key]
108+
<< std::endl;
109+
}
110+
return true;
111+
}
112+
if (verbose) {
113+
std::cout << "search(not key equal): hash1: " << hashFunc_1[key] << " hash2: " << hashFunc_2[key]
114+
<< std::endl;
115+
}
116+
}
117+
118+
return false;
119+
}
120+
121+
122+
void remove(const K& key) {
123+
numberOfRemove++;
124+
if (verbose) {
125+
std::cout << "\ndelete: key [" << key << "] ";
126+
}
127+
OpenHashNode<K, V>* entry;
128+
unsigned long hash1 = hashFunc_1[key];
129+
unsigned long hash2 = hashFunc_2[key];
130+
131+
if ((entry = table_left[ hash1 ]) != NULL) {
132+
if (comp.compare(entry->getKey(), key)) {
133+
table_left[ hash1 ] = NULL;
134+
}
135+
}
136+
if ((entry = table_right[ hash2 ]) != NULL) {
137+
if (comp.compare(entry->getKey(), key)) {
138+
table_right[ hash2 ] = NULL;
139+
}
140+
}
141+
if (verbose) {
142+
std::cerr << "Error: cannot remove by key: " << key << std::endl;
143+
std::cerr << "hash1: " << hash1 << " hash2: " << hash2 << std::endl;
144+
}
145+
}
146+
void rehash() {
147+
numberOfRehash++;
148+
hashFunc_1 = CuckooKeyHash<K>(tableSize);
149+
hashFunc_2 = CuckooKeyHash<K>(tableSize);
150+
auto** table_copy_left = new OpenHashNode<K, V>*[tableSize]();
151+
auto** table_copy_right = new OpenHashNode<K, V>*[tableSize]();
152+
153+
for (int i = 0; i < tableSize; i++) {
154+
table_copy_left[i] = table_left[i];
155+
}
156+
for (int i = 0; i < tableSize; i++) {
157+
table_copy_right[i] = table_right[i];
158+
}
159+
160+
delete table_left, table_right;
161+
table_left = new OpenHashNode<K, V>*[tableSize]();
162+
table_right = new OpenHashNode<K, V>*[tableSize]();
163+
for (int i = 0; i < tableSize; i++) {
164+
if (table_copy_left[i] != NULL) {
165+
OpenHashNode<K, V>* entry = table_copy_left[i];
166+
insert(entry->getKey(), entry->getValue());
167+
}
168+
}
169+
for (int i = 0; i < tableSize; i++) {
170+
if (table_copy_right[i] != NULL) {
171+
OpenHashNode<K, V>* entry = table_copy_right[i];
172+
insert(entry->getKey(), entry->getValue());
173+
}
174+
}
175+
if (verbose) {
176+
std::cout << "\n\trehashed: \n";
177+
}
178+
delete[] table_copy_left, table_copy_right;
179+
}
180+
181+
unsigned int getNcollisions(){ return collisions; }
182+
183+
void resetCollisions(){ collisions = 0; }
184+
185+
// unsigned int getNumberOfRemoves() {
186+
// return numberOfRemove;
187+
// }
188+
// unsigned int getNumberOfSearches() {
189+
// return numberOfSearch;
190+
// }
191+
// unsigned int getNumberOfInserts() {
192+
// return numberOfInserts;
193+
// }
194+
195+
// void resetCounters() {
196+
// numberOfRemove = 0;
197+
// numberOfSearch = 0;
198+
// numberOfInserts = 0;
199+
// }
200+
// void displayHash() {
201+
// OpenHashNode <K, V> *entry = NULL;
202+
// if (verbose) {
203+
// std::cout << "\ntable1: table2:\n";
204+
// }
205+
// for (int i = 0; i < tableSize; i++) {
206+
// entry = table_left[i];
207+
// std::cout << i;
208+
// if (verbose) {
209+
// if (entry != NULL) {
210+
// std::cout << " -> (" << entry->getKey() << ", " << entry->getValue() << ") " ;
211+
// } else {
212+
// std::cout << " ";
213+
// }
214+
// }
215+
// entry = table_right[i];
216+
// if (verbose) {
217+
// std::cout << i;
218+
// if (entry != NULL) {
219+
// std::cout << " -> (" << entry->getKey() << ", " << entry->getValue() << ")";
220+
// }
221+
// std::cout << std::endl;
222+
// }
223+
// }
224+
// }
225+
};

0 commit comments

Comments
 (0)