Тема: Подвійне хешування
Привіт!
Мені поставили завдання реалізувати хеш-таблицю з використанням подвійного хешування. Моя програма працює, і, здається, без помилок. Але єдине - додавання нових елементів (особливо у великій кількості - потрібно 100 000) виконується дуже повільно.
Чи не могли б ви допомогти розібратися, як краще оптимізувати цю функцію (void DoubleHashing<E, X>::add(const E e[], size_t len))?
Дякую.
Ось код:
DoubleHashing.h - файл з реалізацією
#ifndef DOUBLEHASHING_H
#define DOUBLEHASHING_H
#include <iostream>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include "Container.h"
using namespace std;
class EmptyExc : public ContainerException
{
public:
virtual const char* what() const throw()
{
return "empty";
}
};
class PrimExc : public ContainerException
{
public:
virtual const char* what() const throw()
{
return "max_prim";
}
};
template <typename E, size_t X = 7>
class DoubleHashing : public Container<E>
{
size_t nmax;
size_t n;
double maxbelfak;
class HashElement
{
public:
size_t status;
E data;
size_t hash;
HashElement() //: data()
{
status = 0; hash = 0;
}
~HashElement()
{
}
void fill(const E e, unsigned int h)
{
hash = h; status = 1; data = e;
}
};
HashElement * value;
public:
DoubleHashing<E, X>()
{
value = new HashElement[X]; n = 0; nmax = X;
maxbelfak = 0.7;
}
virtual ~DoubleHashing<E, X>()
{
delete[] value;
}
using Container<E>::add;
virtual void add(const E e[], size_t len);
void rehash(const E e[], size_t len);
using Container<E>::remove;
virtual void remove(const E e[], size_t s);
virtual bool member(const E& e) const;
void ocbigger(size_t n, size_t nmax);
virtual size_t size() const
{
return n;
}
void Quicksort(E *a, size_t m) const
{
if (m < 2)
return;
E p = a[m / 2];
E *l = a;
E *r = a + m - 1;
while (r >= l) {
if (p > *l)
{
l++;
continue;
}
if (*r > p)
{
r--;
continue; // we need to check the condition (l <= r) every time we change the value of l or r
}
E t = *l;
*l++ = *r;
*r-- = t;
}
Quicksort(a, r - a + 1);
Quicksort(l, a + m - l);
}
virtual size_t apply(const Functor<E>& f, Order order) const
{
size_t x = 0;
size_t z = 0;
E *copyvalue;
if (n > 0)
{
if (order == dontcare)
{
for (z = 0; z < nmax; z++)
{
if (value[z].status == 1)
{
x++;
if (!f(value[z].data))
{
break;
}
}
}
}
else
{
copyvalue = new E[n];
for (z = 0; z < nmax; z++)
{
if (value[z].status == 1)
{
copyvalue[x] = value[z].data;
x++;
}
}
x = 0;
Quicksort(©value[0], n);
if (order == ascending)
{
for (size_t i = 0; i < n; i++)
{
x++;
if (!f(copyvalue[i]))
{
break;
}
}
}
if (order == descending)
{
for (size_t i = n; i > 0; i--)
{
x++;
if (!f(copyvalue[i - 1]))
{
break;
}
}
}
delete[] copyvalue;
}
}
return x;
}
virtual E min() const
{
if (this->empty()) throw EmptyExc();
else
{
E rc;
size_t j = 0;
for (j = 0; j < nmax; ++j)
{
if (value[j].status == 1)
{
rc = value[j].data;
break;
}
}
for (size_t i = j; i<nmax; i++)
{
if (value[i].status == 1 && rc > value[i].data)
{
rc = value[i].data;
}
}
return rc;
}
}
virtual E max() const
{
if (this->empty()) throw EmptyExc();
else
{
E rc;
size_t j = 0;
for (j = 0; j < nmax; ++j)
{
if (value[j].status == 1)
{
rc = value[j].data;
break;
}
}
for (size_t i = j; i<nmax; i++)
{
if (value[i].status == 1 && value[i].data > rc)
{
rc = value[i].data;
}
}
return rc;
}
}
virtual ostream& print(ostream& o) const;
};
size_t prime(size_t number)
{
size_t a = 2;
while (a < number)
{
if (number % a == 0)
{
return 0;
}
a++;
}
return 1;
}
size_t nextprime(size_t number)
{
size_t a = number + 1;
while (1)
{
if (a < ((size_t)-1))
{
if (!(prime(a) == 1))
{
a++;
}
else
{
break;
}
}
else
{
throw PrimExc();
}
}
return a;
}
template <typename E, size_t X>
void DoubleHashing<E, X>::add(const E e[], size_t len) // ось ця функція працює повільно
{
size_t hash1, hash2, hashg = 0;
size_t newnmax = nmax;
HashElement *a;
size_t q = 0;
if ((n + len) > (newnmax * maxbelfak))
{
HashElement * newvalue;
do
{
newnmax = nextprime(newnmax);
} while (newnmax < ((n + len) * 10));
newvalue = new HashElement[newnmax];
for (size_t i = 0; i < nmax; ++i)
{
if (value[i].status == 1)
{
hash1 = (value[i].hash) % newnmax;
hash2 = 1 + ((value[i].hash) % (newnmax - 2));
while (1)
{
hashg = (hash1 + (q*hash2)) % newnmax;
if (newvalue[hashg].status == 0)
{
newvalue[hashg].fill(value[i].data, value[i].hash);
q = 0;
break;
}
else
{
q++;
}
}
}
}
delete[] value;
value = new HashElement[newnmax];
for (size_t t = 0; t < newnmax; t++)
{
value[t] = newvalue[t];
}
delete[] newvalue;
nmax = newnmax;
}
for (size_t i = 0; i < len; ++i)
{
q = 0;
if (!member(e[i]))
{
hash1 = hashValue(e[i]) % nmax;
hash2 = 1 + (hashValue(e[i]) % (nmax - 2));
while (1)
{
hashg = (hash1 + (q*hash2)) % nmax;
a = &value[hashg];
if (a->status == 0)
{
value[hashg].fill(e[i], hashValue(e[i]));
n++;
break;
}
else
{
q++;
}
}
}
}
}
template <typename E, size_t X>
void DoubleHashing<E, X>::remove(const E e[], size_t s)
{
size_t hash1;
size_t hash2;
size_t hashg;
size_t q = 0;
for (size_t i = 0; i < s; ++i)
{
q = 0;
hash1 = hashValue(e[i]) % nmax;
hash2 = 1 + (hashValue(e[i]) % (nmax - 2));
for (size_t w = 0; w < nmax; w++)
{
hashg = (hash1 + (q*hash2)) % nmax;
if ((value[hashg].status == 1) && (value[hashg].data == e[i]))
{
value[hashg].status = 0;
n--;
break;
}
else
{
q++;
}
}
}
}
template <typename E, size_t X>
bool DoubleHashing<E, X>::member(const E& e) const
{
size_t hashg;
size_t hash1;
size_t hash2;
size_t q = 0;
size_t x = 0;
hash1 = hashValue(e) % nmax;
hash2 = 1 + (hashValue(e) % (nmax - 2));
for (size_t j = 0; j < nmax; ++j)
{
hashg = (hash1 + (q*hash2)) % nmax;
x++;
if (value[hashg].status == 1 && value[hashg].data == e)
{
return true;
}
else
{
q++;
}
}
return false;
}
template <typename E, size_t X>
ostream& DoubleHashing<E, X>::print(ostream& o) const
{
HashElement *p;
o << "Double Hashing [ n=" << n << " nmax=" << nmax << " value=";
for (size_t i = 0; i < nmax; ++i)
{
if (value[i].status == 1)
{
p = &value[i];
o << ' ' << p->data;
}
else
{
o << " .";
}
}
o << " ]";
return o;
}
#endif //DOUBLEHASHING_H
Container.h - допоміжний файл (має залишатися незмінним)
#ifndef CONTAINER_H
#define CONTAINER_H
#include <iostream>
enum Order
{
dontcare, ascending, descending
};
template <typename E> class Functor;
class ContainerException : public std::exception
{
public:
virtual const char* what() const throw() = 0;
};
template <typename E>
class Container
{
Container& operator=(const Container<E>&);
Container(const Container<E>&);
public:
Container()
{
}
virtual ~Container()
{
}
virtual void add(const E& e)
{
add(&e, 1);
}
virtual void add(const E e[], size_t s) = 0;
virtual void remove(const E& e)
{
remove(&e, 1);
}
virtual void remove(const E e[], size_t s) = 0;
virtual bool member(const E& e) const = 0;
virtual size_t size() const = 0;
virtual bool empty() const
{
return size() == 0;
}
virtual size_t apply(const Functor<E>& f, Order order = dontcare) const = 0;
virtual E min() const = 0;
virtual E max() const = 0;
virtual std::ostream& print(std::ostream& o) const = 0;
};
template <typename E>
inline std::ostream& operator<<(std::ostream& o, const Container<E>& c)
{
return c.print(o);
}
template <typename E>
class Functor
{
public:
virtual bool operator( )(const E& e) const = 0;
virtual ~Functor()
{
}
};
template <typename E> inline unsigned long hashValue(const E& e)
{
return (unsigned long)e;
} //e * 47114711UL
template <typename E> inline double doubleValue(const E& e)
{
return double(e);
}
template <typename E> inline unsigned long ordinalValue(const E& e)
{
return (unsigned long)e;
}
#endif //CONTAINER_H
simpletest.cpp - виконуючий файл (також має залишатися незмінним)
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include "DoubleHashing.h"
#ifndef ETYPE
#define ETYPE int
#endif
//#define str(x) #x
const char helpstr[] =
"new ............................... create new Container\n"
"delete ............................ delete Container\n"
"add <key> [...] ................... add <key>(s) with Container::add( int )\n"
"remove <key> [...] ................ remove <key>(s) with Container::remove( int )\n"
"member <key> ...................... call Container::member( <key> )\n"
"size .............................. call Container::size()\n"
"empty ............................. call Container::empty()\n"
"min ............................... call Container::min()\n"
"max ............................... call Container::max()\n"
"print ............................. print container with operator<<()\n"
"apply [asc|desc|dontcare [<n>]] ... traverse container with PrintN functor\n"
"trace ............................. toggle tracing on/off\n"
"fadd <filename> ................... add values read from file <filename>\n"
"fremove <filename> ................ remove values read from file <filename>\n"
"radd [<n> [<seed>]] ............... add <n> random values, optionally reset generator to <seed>\n"
"rremove [<n> [<seed>]] ............ remove <n> random values, optionally reset generator to <seed>\n"
"quit .............................. quit program\n\n"
"arguments surrounded by [] are optional\n";
template <typename E>
class PrintN : public Functor<E>
{
std::ostream& o;
mutable int n;
public:
explicit PrintN(int n = 0, std::ostream& o = std::cout) : o(o), n(n)
{
}
explicit PrintN(std::ostream& o) : o(o), n(0)
{
}
bool operator()(const E& e) const
{
o << e << ' ';
return n <= 0 || --n;
}
};
void setrandom(int seed)
{
srand(seed);
}
template <typename E> E nextrandom()
{
return E(rand());
}
// Template-Spezialisierungen fuer Klasse std::string
template <> inline double doubleValue(const std::string& e)
{
double rc = 0.; for (size_t i = e.length(); i--;) rc /= 256., rc += e[i]; return rc;
}
template <> inline unsigned long hashValue(const std::string& e)
{
unsigned long rc = 0; for (size_t i = 0; i < e.length(); ++i) rc = rc * 13 + e[i]; return rc;
}
template <> std::string nextrandom()
{
const char* start = helpstr + rand() % sizeof helpstr;
while (!isalpha(*start)) if (*start) ++start; else start = helpstr;
const char* end = start + 1;
while (isalpha(*end)) ++end;
char buf[10];
sprintf(buf, "%d", rand() % 1000);
return std::string(start, end - start) += buf;
}
// Klasse Person mit allen fГјr die Verwendung als Container-Elementdatentyp noetigen Methoden und Funktionen
class Person
{
std::string vorname;
std::string nachname;
public:
Person()
{
}
Person(std::string vorname, std::string nachname) : vorname(vorname), nachname(nachname)
{
}
bool operator==(const Person& p) const
{
return vorname == p.vorname && nachname == p.nachname;
}
bool operator>(const Person& p) const
{
return nachname > p.nachname || (nachname == p.nachname && vorname > p.vorname);
}
std::ostream& print(std::ostream& o) const
{
return o << '[' << nachname << ", " << vorname << ']';
}
std::istream& read(std::istream& i)
{
return i >> vorname >> nachname;
}
friend double doubleValue<Person>(const Person& e);
friend unsigned long hashValue<Person>(const Person& e);
friend unsigned long ordinalValue<Person>(const Person& e);
};
inline std::ostream& operator<<(std::ostream& o, const Person& p)
{
return p.print(o);
}
inline std::istream& operator>>(std::istream& i, Person& p)
{
return p.read(i);
}
// Template-Spezialisierungen fuer Klasse Person
template <> inline double doubleValue(const Person& e)
{
return doubleValue(e.nachname);
}
template <> inline unsigned long hashValue(const Person& e)
{
return hashValue(e.nachname);
}
template <> Person nextrandom()
{
return Person(nextrandom<std::string>(), nextrandom<std::string>());
}
bool match(const std::string& s, const char * c)
{
return c && s.length() <= std::strlen(c) && s.compare(0, s.length(), c, s.length()) == 0;
}
int main()
{
Container<ETYPE>* c = 0;
bool traceIt = false;
std::cout.setf(std::ios_base::boolalpha);
while (true)
{
if (traceIt)
{
if (c)
{
std::cout << std::endl << "container: " << *c;
}
else
{
std::cout << std::endl << "no container";
}
}
std::cout << std::endl << "> ";
std::string cmdline;
if (!std::getline(std::cin, cmdline)) break;
std::istringstream cmdstream(cmdline);
std::string cmd;
cmdstream >> cmd;
try
{
if (cmd.length() == 0)
{
}
else if (match(cmd, "quit"))
{
break;
}
else if (match(cmd, "new"))
{
if (c)
{
std::cerr << "container exists, 'delete' it first";
}
else
{
// c = new DoubleHashing<ETYPE,13>;
c = new DoubleHashing<ETYPE>;
}
}
else if (match(cmd, "help") || cmd == "?")
{
std::cout << helpstr;
}
else if (match(cmd, "trace"))
{
std::cout << "trace " << ((traceIt = !traceIt) ? "on" : "off");
}
else if (!c)
{
std::cout << "no container (use 'new')";
}
else
{
ETYPE key;
if (match(cmd, "delete"))
{
delete c;
c = 0;
}
else if (match(cmd, "add"))
{
while (cmdstream >> key)
{
c->add(key);
}
}
else if (match(cmd, "remove"))
{
while (cmdstream >> key)
{
c->remove(key);
}
}
else if (match(cmd, "member"))
{
cmdstream >> key;
std::cout << "returns " << c->member(key);
}
else if (match(cmd, "size"))
{
std::cout << "returns " << c->size();
}
else if (match(cmd, "empty"))
{
std::cout << "returns " << c->empty();
}
else if (match(cmd, "min"))
{
std::cout << "returns " << c->min();
}
else if (match(cmd, "max"))
{
std::cout << "returns " << c->max();
}
else if (match(cmd, "print"))
{
std::cout << *c;
}
else if (match(cmd, "apply"))
{
int n = 0;
std::string order = "dontcare";
cmdstream >> order >> n;
size_t rc = c->apply(PrintN<ETYPE>(n), match(order, "ascending") ? ascending : match(order, "descending") ? descending : dontcare);
std::cout << "\nreturns " << rc;
}
else if (match(cmd, "fadd"))
{
std::string filename;
cmdstream >> filename;
std::ifstream keystream(filename.c_str());
while (keystream >> key)
{
c->add(key);
}
}
else if (match(cmd, "fremove"))
{
std::string filename;
cmdstream >> filename;
std::ifstream keystream(filename.c_str());
while (keystream >> key)
{
c->remove(key);
}
}
else if (match(cmd, "radd"))
{
int seed = -1, count = 1;
cmdstream >> count >> seed;
if (seed != -1) setrandom(seed);
while (count-- > 0) c->add(nextrandom<ETYPE>());
}
else if (match(cmd, "rremove"))
{
int seed = -1, count = 1;
cmdstream >> count >> seed;
if (seed != -1) setrandom(seed);
while (count-- > 0) c->remove(nextrandom<ETYPE>());
}
else
{
std::cout << cmd << "? try 'help'";
}
}
}
catch (ContainerException& e)
{
std::cout << "ContainerException " << e.what();
}
catch (std::exception& e)
{
std::cout << "Exception " << e.what();
}
catch (...)
{
std::cout << "OOPS!";
}
}
return 0;
}