Aleph-w  1.5a.2
Biblioteca general de algoritmos y estructuras de datos
 Todo Clases Archivos Funciones Variables 'typedefs' Enumeraciones Amigas Grupos Páginas
tpl_linHash.H
1 
2 # ifndef TPL_LINHASH_H
3 # define TPL_LINHASH_H
4 
5 # include <iostream>
6 # include <primes.H>
7 # include <tpl_dynArray.H>
8 # include <tpl_dnode.H>
9 # include <hash-fct.H>
10 # include <hash-dry.H>
11 
12 
13 # ifdef N
14 # define NBACKUP N
15 # undef N
16 # endif
17 
18 # ifdef M
19 # define MBACKUP M
20 # undef M
21 # endif
22 
23 
24 using namespace Aleph;
25 
26 namespace Aleph {
27 
28 # define LINBUCKET_BODY(BUCKETNAME) \
29  \
30  Dlink link; \
31  \
32 public: \
33  \
34  BUCKETNAME(const BUCKETNAME & bucket) : Dnode<Key>(bucket) {} \
35  \
36  BUCKETNAME() {} \
37  \
38  BUCKETNAME(const Key & key) : Dnode<Key>(key) {} \
39  \
40  Key & get_key() { return this->get_data(); } \
41  \
42  Dlink * get_link() { return &link; } \
43  \
44  DLINK_TO_BASE(BUCKETNAME, link);
45 
46 
54  template <typename Key>
55 class LinHashBucket : public Dnode<Key>
56 {
57  LINBUCKET_BODY(LinHashBucket);
58 };
59 
67  template <typename Key>
68 class LinHashBucketVtl : public Dnode<Key>
69 {
70  LINBUCKET_BODY(LinHashBucketVtl);
71 
73  virtual ~LinHashBucketVtl() {}
74 };
75 
76 
77 
103  template <typename Key, template <class> class BucketType,
104  class Cmp = Aleph::equal_to<Key>>
106 {
107 public:
108 
111  typedef size_t (*Hash_Fct)(const Key &);
112 
114  typedef BucketType<Key> Bucket;
115 
116  typedef Key Key_Type;
117 
118  typedef Key Item_Type;
119 
120 private:
121 
122  typedef Dnode<Key> BucketList;
123 
124  typedef typename Dnode<Key>::Iterator BucketItor;
125 
126  static size_t multiply_by_two(const size_t & n) { return n << 1; }
127 
128  static size_t divide_by_two(const size_t & n) { return n >> 1; }
129 
130  DynArray<BucketList> table;
131  Dlink entries_list;
132 
133 protected:
134 
135  Hash_Fct hash_fct; // puntero a función hash
136 
137 private:
138 
139  Cmp cmp = Cmp();
140 
141  size_t M; // Tamaño de la tabla
142  size_t N; // Número de elementos que tiene la tabla
143  size_t busy_slots_counter; // Cantidad de entradas ocupadas
144  bool remove_all_buckets; // Indica si destructor debe liberar
145  // las cubetas
146 
147 protected:
148 
149  float upper_alpha; // factor de carga superior
150  float lower_alpha; // factor de carga inferior
151 
152 private:
153 
154  size_t p; // índice de la lista que se particiona (o aumenta)
155  size_t l; // cantidad de veces que se ha duplicado la tabla
156  size_t MP; // guarda el valor p + M
157  size_t MM; // producto 2*M
158 
159 protected:
160 
161  mutable size_t len;
162 
163 private:
164 
165  size_t call_hash_fct(const Key & key) const
166  {
167  const size_t hash = (*hash_fct)(key);
168  const size_t i = hash % M;
169  return i < p ? hash % MM : i;
170  }
171 
172  void expand()
173  { // expandir la tabla hasta que la carga esté debajo de upper_alpha
174  for (float alpha = 1.0*N/MP; alpha >= upper_alpha; alpha = 1.0*N/MP)
175  {
176  BucketList * src_list_ptr = table.test(p);
177  if (src_list_ptr != NULL) // ¿table[p] está escrita?
178  if (not src_list_ptr->is_empty()) // ¿table[p] no está vacía'
179  {
180  BucketList * tgt_list_ptr = NULL;
181 
182  // recorrer lista colisiones y mover cubetas de table[p+M]
183  for (BucketItor it(*src_list_ptr); it.has_current(); /* nada */)
184  {
185  Bucket * bucket = static_cast<Bucket*>(it.get_current());
186 
187  it.next(); // avance al siguiente elemento de la lista
188 
189  const Key & key = bucket->get_key();
190  const int i = (*hash_fct)(key) % MM;
191  if (i == p) // ¿pertenece esta clave a table[p]?
192  continue; // sí ==> clave sigue en table[p] ==> siguiente
193 
194  if (tgt_list_ptr == NULL)
195  tgt_list_ptr = &table.touch(MP);
196 
197  // bucket no pertenece a table[p] sino a table[p+m] ==>
198  // eliminar bucket de table[i] e insertarlo en table[p+m]
199  bucket->del();
200  tgt_list_ptr->append(bucket);
201  }
202 
203  if (src_list_ptr->is_empty()) // ¿table[p] quedó vacía?
204  --busy_slots_counter; // sí ==> un slot vacío
205 
206  ++busy_slots_counter; // uno nuevo por table[p+M]
207  }
208  ++p;
209  ++MP;
210  if (p == M) // (p == 2*M) ¿debe duplicarse el tamaño de la tabla?
211  { // sí ==> modificar el tamaño de la tabla a 2*M
212  ++l; // Cantidad de veces que se ha duplicado la tabla
213  p = 0;
214  MP = M = MM; // se les asigna 2*M
215  MM = multiply_by_two(MM);
216  }
217  }
218  }
219 
220  void contract()
221  { // contraer la tabla hasta que carga esté debajo de lower_alpha
222  for (float alpha = (1.0*N)/MP; alpha <= lower_alpha and MP > len;
223  alpha = (1.0*N)/MP)
224  {
225  if (p == 0) // ¿debe dividirse entre 2 el tamaño de la tabla?
226  { // sí ==> actualizar tamaño de la tabla a M/2
227  --l; // Cantidad de veces que se ha duplicado la tabla disminuye
228  MM = M; // se divide entre dos
229  M = divide_by_two(M);
230  p = M - 1;
231  }
232  else
233  --p; // no ==> sólo reducir índice p
234 
235  --MP;
236  if (MP < table.size()) // ¿Existe table[MP]]?
237  {
238  BucketList * src_list_ptr = table.test(MP);
239  if (src_list_ptr != NULL) // ¿existe entrada para table[p+M]?
240  {
241  if (not src_list_ptr->is_empty()) // ¿table[p+M] está vacía?
242  { // no ==> fusionar las listas
243  BucketList & tgt_list = table.touch(p);// aparta table[p]
244  tgt_list.concat_list(src_list_ptr);
245  --busy_slots_counter; // table[p+M] devino vacía
246  }
247  table.cut(MP); // eventualmente liberar memoria de table[p+M]
248  }
249  }
250  }
251  }
252 
253 public:
254 
257  {
258  hash_fct = fct;
259  }
260 
261  Hash_Fct get_hash_fct() const { return hash_fct; }
262 
263  const Cmp & get_compare() const { return cmp; }
264 
266  float current_alpha() const { return 1.0*N/MP; }
267 
290  GenLinearHashTable(Hash_Fct __hash_fct = Aleph::dft_hash_fct<Key>,
291  const size_t & __len = Primes::DefaultPrime,
292  const float & __lower_alpha = hash_default_lower_alpha,
293  const float & __upper_alpha = hash_default_upper_alpha,
294  const bool __remove_all_buckets = true,
295  const bool __with_resize = true/* fictitious */)
296  throw(std::exception, std::length_error, std::domain_error,
297  std::bad_alloc, std::overflow_error)
298  : table(__len), hash_fct(__hash_fct), M(__len), N(0),
299  busy_slots_counter(0), remove_all_buckets(__remove_all_buckets),
300  upper_alpha(__upper_alpha), lower_alpha(__lower_alpha),
301  p(0), l(0), MP(M), MM(multiply_by_two(M)), len(__len)
302  {
303  if (M == 0)
304  std::length_error("table's length is zero");
305 
306  if (MM > table.max_size())
307  throw std::length_error("table's length too big");
308 
309  if (upper_alpha <= lower_alpha)
310  throw std::domain_error("lower alpha is greater than lower alpha");
311  }
312 
313  void swap(GenLinearHashTable & other)
314  {
315  std::swap(table, other.table);
316  std::swap(hash_fct, other.hash_fct);
317  std::swap(M, other.M);
318  std::swap(N, other.N);
319  std::swap(busy_slots_counter, other.busy_slots_counter);
320  std::swap(remove_all_buckets, other.remove_all_buckets);
321  std::swap(upper_alpha, other.upper_alpha);
322  std::swap(lower_alpha, other.lower_alpha);
323  std::swap(p, other.p);
324  std::swap(l, other.l);
325  std::swap(MP, other.MP);
326  std::swap(MM, other.MM);
327  std::swap(len, other.len);
328  }
329 
344  void empty()
347  {
348  while (not entries_list.is_empty())
349  {
350  Bucket * bucket = Bucket::dlink_to_base(entries_list.remove_first());
351  bucket->del(); // borra de la lista en el arreglo
352  bucket->get_link()->del(); // borra de entries list
353  delete bucket;
354  }
355 
356  M = MP = len;
357  MM = multiply_by_two(M);
358  N = p = l = 0;
359  table.cut(len);
360  }
361 
363  {
364  if (remove_all_buckets)
365  empty();
366  }
367 
368 private:
369 
370  Bucket * search_in_bucket_list(BucketList * list, const Key & key) const
371  {
372  for (BucketItor it(*list); it.has_curr(); it.next())
373  {
374  Bucket * bucket = static_cast<Bucket*>(it.get_current());
375  if (cmp (key, bucket->get_key()))
376  return bucket;
377  }
378 
379  return NULL;
380  }
381 
382 public:
383 
387  Bucket * search(const Key & key) const
388  {
389  const int i = call_hash_fct(key);
390  BucketList * list = table.test(i);
391  if (list == NULL) // ¿Ha sido escrita alguna vez table[i]?
392  return NULL; // No ==> el elemento no se encuentra en la tabla
393 
394  if (list->is_empty())
395  return NULL;
396 
397  return search_in_bucket_list(list, key);
398  }
399 
401  const size_t & size() const { return N; }
402 
404  bool is_empty() const { return N == 0; }
405 
407  const size_t & capacity() const { return MP; }
408 
410  const size_t & busy_slots() const { return busy_slots_counter; }
411 
414  const size_t & expansions() const { return l; }
415 
418  Bucket * insert(Bucket * bucket)
419  {
420  const int i = call_hash_fct(bucket->get_key());
421  BucketList & list = table.touch(i); // allocates memory for table[i]
422  if (list.is_empty())
423  ++busy_slots_counter;
424 
425  if (search_in_bucket_list(&list, bucket->get_key()) != NULL)
426  return NULL; // duplicated key
427 
428  list.append(bucket);
429  entries_list.append(bucket->get_link());
430  ++N;
431  expand();
432 
433  return bucket;
434  }
435 
437  size_t resize(size_t) { return MP; }
438 
439 private:
440 
441  // This routine deletes bucket from hash table EXCEPT from
442  // entries_list. The end of this routine is to dry the deletion and to
443  // allow remove from other places; ofor instance, from the del()
444  // method of Iterator class
445  Bucket * remove_bucket(Bucket * bucket)
446  {
447  I(bucket != NULL);
448  I(search(bucket->get_key()) == bucket);
449 
450  Bucket * next = static_cast<Bucket*>(bucket->get_next());
451 
452  bucket->del(); // elimine de lista de colisiones
453 
454  if (next->is_empty()) // ¿lista de colisiones vacía?
455  --busy_slots_counter; // sí ==> un slot vacío
456 
457  --N;
458  contract();
459 
460  return bucket;
461  }
462 
463 public:
464 
467  Bucket * remove(Bucket * bucket)
468  {
469  bucket->get_link()->del(); // elimine de entries_list
470  return remove_bucket(bucket);
471  }
472 
473  void print()
474  {
475  for (int i = 0; i < MP; ++i)
476  {
477  cout << "table[" << i << "] = [ ";
478 
479  if (table.exist(i))
480  {
481  BucketList & list = table.access(i);
482 
483  if (not list.is_empty())
484  for (BucketItor it(list); it.has_current(); it.next())
485  {
486  Bucket * bucket = static_cast<Bucket*>(it.get_current());
487  const Key & key = bucket->get_key();
488  cout << key << ",";
489  }
490  }
491  cout << "]" << endl;
492  }
493  }
494 
495  HASH_STATS();
496 
497  class Iterator : public Dlink::Iterator
498  {
499  private:
500 
501  GenLinearHashTable * hash_table;
502 
503  public:
504 
507 
509  typedef Bucket * Item_Type;
510 
512  Iterator(const GenLinearHashTable & table)
513  : Dlink::Iterator(const_cast<Dlink &>(table.entries_list)),
514  hash_table(& const_cast<GenLinearHashTable &>(table))
515  {
516  // Empty
517  }
518 
520  Iterator() : hash_table(NULL)
521  {
522  // Empty
523  }
524 
527  {
528  return Bucket::dlink_to_base(this->Dlink::Iterator::get_curr());
529  }
530 
531  Bucket * del()
532  {
533  Bucket * bucket = Bucket::dlink_to_base(this->Dlink::Iterator::del());
534  return (Bucket *) hash_table->remove_bucket(bucket);
535  }
536  };
537 };
538 
539 
559  template <typename Key, class Cmp = Aleph::equal_to<Key>>
560 struct LinearHashTable : public GenLinearHashTable<Key, LinHashBucket, Cmp>
561 {
563 
564  using Base::Base;
565 };
566 
586  template <typename Key, class Cmp = Aleph::equal_to<Key>>
588  : public GenLinearHashTable<Key, LinHashBucketVtl, Cmp>
589 {
591 
592  using Base::Base;
593 };
594 
595 } // end namespace Aleph
596 
597 # ifdef NBACKUP
598 # define N NBACKUP
599 # undef NBACKUP
600 # endif
601 
602 # ifdef MBACKUP
603 # define M MBACKUP
604 # undef MBACKUP
605 # endif
606 
607 # endif // TPL_LINHASH_H
608 
size_t(* Hash_Fct)(const Key &)
Definition: tpl_linHash.H:111
Bucket * Item_Type
El tipo de elemento que retorna get_curr().
Definition: tpl_linHash.H:509
bool is_empty() const
return true is table is empty
Definition: tpl_linHash.H:404
Definition: tpl_linHash.H:68
GenLinearHashTable Set_Type
El tipo de conjunto sobre el cual se itera.
Definition: tpl_linHash.H:506
Definition: tpl_linHash.H:587
Bucket * insert(Bucket *bucket)
Definition: tpl_linHash.H:418
BucketType< Key > Bucket
El tipo de cubeta.
Definition: tpl_linHash.H:114
Definition: tpl_linHash.H:55
const size_t & expansions() const
Definition: tpl_linHash.H:414
Iterator(const GenLinearHashTable &table)
Instancia un iterador sobre la tabla hash table.
Definition: tpl_linHash.H:512
const size_t & capacity() const
Retorna el tamaño de la tabla.
Definition: tpl_linHash.H:407
Definition: tpl_linHash.H:105
Iterator()
Instancia un iterador vacío.
Definition: tpl_linHash.H:520
const size_t & size() const
Retorna la cantidad de elementos que tiene la tabla.
Definition: tpl_linHash.H:401
void empty()
Definition: tpl_linHash.H:346
Bucket * get_curr()
Retorna la cubeta actual.
Definition: tpl_linHash.H:526
Definition: tpl_linHash.H:497
const size_t & busy_slots() const
Retorna la cantidad de entradas vacía que tiene la tabla.
Definition: tpl_linHash.H:410
size_t resize(size_t)
Provided for generic programming compatibility.
Definition: tpl_linHash.H:437
Definition: tpl_dynArray.H:70
GenLinearHashTable(Hash_Fct __hash_fct=Aleph::dft_hash_fct< Key >, const size_t &__len=Primes::DefaultPrime, const float &__lower_alpha=hash_default_lower_alpha, const float &__upper_alpha=hash_default_upper_alpha, const bool __remove_all_buckets=true, const bool __with_resize=true)
Definition: tpl_linHash.H:290
void set_hash_fct(Hash_Fct fct)
Set the internal hash function.
Definition: tpl_linHash.H:256
Definition: ahFunction.H:115
float current_alpha() const
return the current table load
Definition: tpl_linHash.H:266
Definition: List.H:23
Definition: tpl_linHash.H:560
Bucket * search(const Key &key) const
Definition: tpl_linHash.H:387
Nodo perteneciente a lista doblemente enlazada circular.
Definition: tpl_dnode.H:19

Leandro Rabindranath León