home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c100 / 3.ddi / BRENT.ZIP / BRENT.CPP
Encoding:
Text File  |  1990-09-27  |  11.4 KB  |  421 lines

  1. Brent's technique was invented R.P. Brent, a computer scientist and
  2. mathematician, about whom I know little, other than his citations in Donald
  3. Knuth's The Art of Computer Programming Volume 3: Sorting and Searching.  It
  4. is a variation on the primary/secondary hashing technique Knuth calls
  5. Algorithm D (SEE FIGURE 1).
  6.  
  7. ************************* FIGURE 1 ********************************
  8. Psuedocode for Algorithm D:
  9.  
  10. Define two mapping functions -- H1 and H2
  11.  
  12. INSERT IS
  13.   address = H1(data)
  14.   if table[address] is empty
  15.     insert data in table[address]
  16.   else
  17.     address = H1(data) + H2(data)
  18.     while table[address] isn't empty
  19.       address = address + H2(data)
  20.     end while
  21.   end if
  22.   insert data in table[address]
  23.  
  24. LOOKUP IS
  25.   address = H1(data)
  26.   if table[address] isn't equal to data then
  27.     address = H1(data) + H2(data)
  28.     while table[address] isn't equal to data and
  29.       table[address] isn't empty
  30.       address = address + H2(data)
  31.     end while
  32.   end if
  33.   if table[address] is empty then
  34.     return empty
  35.   else
  36.     return table[address]
  37.   end if
  38. ************************* END FIGURE 1 ********************************
  39.  
  40. Brent's technique is a method for optimizing the time complexity of successful
  41. LOOKUP operations.  For certain applications (notable compilers) the number of
  42. successful LOOKUPS is overwhelmingly larger than the number of INSERTS. Brent's
  43. technique works by spending more time inserting objects into the table, in
  44. hopes of making the table better suited for LOOKUP. (SEE FIGURE 2)
  45.  
  46. ************************* FIGURE 2 ********************************
  47. Psuedocode for Brent's Technique:
  48. INSERT IS
  49.   address = H1(data)
  50.   depth = 0
  51.   while table[address] isn't empty
  52.     address = address + H2(data)
  53.     increment(depth)
  54.   end while
  55.   if depth < 2 then
  56.     table[address] = data
  57.   else
  58.     address = H1(data)
  59.     for i = 0 to depth
  60.       address2 = address + H2(table[address])
  61.       depth2 = 0
  62.       while depth2 <= i and table[address2] isn't empty
  63.         address2 = address + H2(table[address])
  64.         increment(depth2)
  65.       end while
  66.       if depth2 <= i
  67.         table[address2] = table[address]
  68.         return
  69.       end if
  70.       address = address + H2(data)
  71.     end for
  72.     table[address] = data
  73.   end if
  74.   insert data in table[address]
  75. ************************* END FIGURE 2 ********************************
  76.  
  77. BRENT'S TECHNIQUE IN C++:
  78.  
  79. HASHTAB.HPP
  80.  
  81. #define DEBUG_HASHTAB
  82. //
  83. // hashtab.hpp -- class definition for hash table
  84. class hashtab {
  85.   size_t tablesize;                    // size of the table
  86.   size_t inserted;                     // # of elements inserted
  87.   void **table;                        // actual table
  88.   size_t (*map)(void *element);        // map a key to the table index type
  89.   int (*compare)(void *e1,void *e2);   // compare two elements a la strcmp
  90.   size_t _lookup(void *element);       // internal lookup function
  91. public:
  92.   hashtab(size_t tablesize,
  93.     size_t (*map)(void *element),
  94.     int (*compare)(void *e1, void *e2));
  95.   ~hashtab(void) {
  96.     delete table;
  97.   }
  98.   void *lookup(void *element);
  99.   void insert(void *element);
  100.   void remove(void *element);
  101. #if  defined(DEBUG_HASHTAB) && defined(__ZTC__)
  102.   void dump_tab(void (*d)(void *e));
  103. #endif
  104. };
  105.  
  106.  
  107. BRENT.CPP
  108.  
  109. #include <stdio.h>
  110. #if !defined(__ZTC__)
  111. #include <stream.h>
  112. // Bad old unix doesn't have ansi headers
  113. #define UINT_MAX ((unsigned)-1)
  114. typedef unsigned size_t;  // should be correct most places.
  115. #else
  116. #include <stream.hpp>
  117. #include <limits.h>
  118. #endif
  119. #include "hashtab.hpp"
  120.  
  121. //
  122. // find_tabsize returns the next twin prime >= the desired table size
  123. static size_t find_tabsize(size_t tabsize) {
  124. //
  125. // each element of primes is the lower twin of a twin prime.  The table
  126. // also has the property that primes[i] >= (primes[i]+100) to keep the
  127. // table size down.
  128.   static unsigned primes[] = {
  129.   5,107,227,347,461,569,809,1019,1151,1277,1427,1607,1721,
  130.   1871,1997,2111,2237,2339,2549,2657,2789,2969,3119,3251,3359,
  131.   3461,3581,3767,3917,4019,4127,4229,4337,4481,4637,4787,4931,
  132.   5099,5231,5417,5519,5639,5741,5849,6089,6197,6299,6449,6551,
  133.   6659,6761,6869,7127,7307,7457,7559,7757,7877,8009,8219,8387,
  134.   8537,8819,8969,9239,9341,9461,9629,9767,9929,10037,10139,10271,
  135.   10427,10529,10709,10859,11057,11159,11351,11489,11699,11831,11939,12041,
  136.   12161,12377,12539,12821,13001,13217,13337,13679,13829,13931,14081,14249,
  137.   14387,14549,14867,15137,15269,15581,15731,15887,16061,16187,16361,16631,
  138.   16829,16979,17189,17291,17417,17579,17681,17789,17909,18041,18251,18521,
  139.   18911,19079,19181,19379,19541,19697,19841,19961,20147,20357,20477,20639,
  140.   20747,20897,21011,21191,21317,21491,21599,21737,21839,22037,22157,22271,
  141.   22481,22619,22739,22859,22961,23201,23369,23537,23669,23831,24107,24371,
  142.   24917,25031,25169,25301,25409,25577,25799,25931,26111,26249,26681,26861,
  143.   27059,27239,27407,27527,27689,27791,27917,28097,28277,28409,28547,28661,
  144.   29021,29129,29387,29567,29669,29879,30011,30137,30269,30389,30491,30839,
  145.   31079,31181,31319,31511,31721,31847,
  146.   };
  147. #  define PTABSIZ  (sizeof(primes)/sizeof(size_t))
  148.  
  149.   int i;
  150.   for(i = 0; i < PTABSIZ; i++)
  151.     if(tabsize <= primes[i])
  152.       return primes[i] + 2;
  153.   cerr << "Table size too big!\n";
  154.   exit(-1);  
  155. }
  156.  
  157. #define empty ((void *) 0)
  158. #define deleted ((void *) -1)
  159.  
  160. //
  161. // constructor for the hashtab class
  162. hashtab::hashtab(size_t tablesize,
  163.     size_t (*map)(void *element),
  164.     int (*compare)(void *e1, void *e2)) {
  165.   //
  166.   // round up table size to nearest twin prime
  167.   this->tablesize = find_tabsize(tablesize);
  168.   //
  169.   // allocate the table
  170.   table = new void *[this->tablesize];
  171.   if(table == NULL) {
  172.     cerr << "Out of memory in hashtab constructor";
  173.     exit(1);
  174.   }
  175.   //
  176.   // clear the table
  177.   for(int i = 0; i < this->tablesize; i++)
  178.     table[i] = empty;
  179.   //
  180.   // store comparison and mapping functions
  181.   this->map = map;
  182.   this->compare = compare;
  183.   //
  184.   // clear insertion count
  185.   inserted = 0;
  186. }
  187.  
  188. //
  189. // insert an element into the hash table, using brent's technique.
  190. void hashtab::insert(void *element) {
  191.   size_t mapped_val,h1,h2,cur,depth,i;
  192.   size_t c2,cur2,depth2;
  193.  
  194.   //
  195.   // test predicate for finding an empty slot in the table.
  196. #  define VACANT(x) (table[(x)] == empty || table[(x)] == deleted)
  197.   //
  198.   // check for table full -- always leave at least
  199.   // one empty slot in the table, or you'll search endlessly for
  200.   // keys not in a full table!
  201.   if(++inserted == tablesize) {
  202.     cerr << "hash table full\n";
  203.     exit(1);
  204.   }
  205.   //
  206.   // map key to value, and do primary hash
  207.   mapped_val = map(element);
  208.   h1 = mapped_val % tablesize;
  209.   //
  210.   // secondary hash
  211.   h2 = (mapped_val % (tablesize - 2)) + 1;
  212.   //
  213.   // compute depth of collision list
  214.   for(cur = h1, depth = 0; !VACANT(cur); cur = (cur + h2) % tablesize) {
  215.     //
  216.     // if the element is already in the table then replace it and
  217.     // return
  218.     if(compare(element,table[cur]) == 0) {
  219.       table[cur] = element;
  220.       return;
  221.     }
  222.     depth++;
  223.   }
  224.   //
  225.   // if we aren't too deep, just jam the element here.
  226.   if(depth < 2) {
  227.     table[cur] = element;
  228.     return;
  229.   }
  230.   for(i = 0,cur = h1; i < depth; i++,cur = (h1+h2) % tablesize) {
  231.     //
  232.     // secondary hash of current element of collision list
  233.     c2 = (map(table[cur]) % (tablesize - 2)) + 1;
  234.     //
  235.     // see if this table element would be displaced fewer 
  236.     // positions than the object being inserted.
  237.     for(depth2 = 0,cur2 = (cur + c2) % tablesize;
  238.       !VACANT(cur2) && depth2 <= i;
  239.       depth2++,cur2 = (cur2 + c2) % tablesize)
  240.       ;
  241.     //
  242.     // if we're still going to improve matters by inserting here
  243.     if(depth2 <= i) {
  244.       table[cur2] = table[cur];
  245.       break;
  246.     }
  247.   }
  248.   //
  249.   // postcondition : cur is the index of an empty slot in which
  250.   // to place element.
  251.   table[cur] = element;
  252. }
  253.  
  254. #define NOTFOUND (UINT_MAX)
  255.  
  256. //
  257. // lookup function private to hashtab class.
  258. size_t hashtab::_lookup(void *element) {
  259.   size_t mapped_val,h1,h2,cur;
  260.   //
  261.   // map key to value, and do primary hash
  262.   mapped_val = map(element);
  263.   h1 = mapped_val % tablesize;
  264.   //
  265.   // secondary hash.
  266.   h2 = (mapped_val % (tablesize - 2)) + 1;
  267.   for(cur = h1;
  268.     table[cur] != empty && 
  269.     (table[cur] == deleted ? 1 : compare(element,table[cur]) != 0);
  270.     cur = (cur + h2) % tablesize)
  271.     ;
  272.   return (table[cur] == empty ? NOTFOUND : cur);
  273. }
  274.  
  275. //
  276. // find an element in the table
  277. void *hashtab::lookup(void *element) {
  278.   size_t index = _lookup(element);
  279.   return (index == NOTFOUND ? empty : table[index]);
  280. }
  281.  
  282. //
  283. // remove an element from the table.
  284. void hashtab::remove(void *element) {
  285.   int x;
  286.   if(inserted-- == 0) {
  287.     cerr << "deletion from empty hash table\n";
  288.     exit(1);
  289.   }
  290.   x = _lookup(element);
  291.   if(x != NOTFOUND)
  292.     table[x] = deleted;
  293. }
  294.  
  295. #if defined(DEBUG_HASHTAB) && defined(__ZTC__)
  296. void hashtab::dump_tab(void (*display)(void *e)) {
  297.   size_t i;
  298.   for(i = 0; i < tablesize; i++) {
  299.     if(table[i] == empty)
  300.       cout << "empty";
  301.     else if(table[i] == deleted)
  302.       cout << "deleted";
  303.     else
  304.       display(table[i]);
  305.     if (i && (i % 5 == 0))
  306.       cout.put('\n');
  307.     else
  308.       cout.put(' ');
  309.   }
  310.   if (i % 5 == 0)
  311.     cout.put('\n');
  312. }
  313. #endif
  314.  
  315.  
  316. HASHTEST.CPP
  317.  
  318. //
  319. // hashtest.cpp -- test generic hash table classes
  320.  
  321. #if !defined(__ZTC__)
  322. #include <stream.h>
  323. typedef unsigned size_t;  // should be correct most places.
  324. #else
  325. #include <stdio.h>    // to pull in size_t
  326. #include <stream.hpp>  
  327. #endif
  328.  
  329. #include <ctype.h>
  330. #include "hashtab.hpp"
  331.  
  332. size_t imap(void *element) {
  333.   return *((int *)element);
  334. }
  335.  
  336. int icompare(void *e1,void *e2) {
  337.   if(*(int *)e1 > *(int *)e2)
  338.     return 1;
  339.   else if(*(int *)e1 == *(int *)e2)
  340.     return 0;
  341.   else
  342.     return -1;
  343. }
  344.  
  345. #if  defined(DEBUG_HASHTAB) && defined(__ZTC__)
  346. void idisplay(void *e1) {
  347.   cout << *(int *)e1;
  348. }
  349. #endif
  350.  
  351. class inthashtab : hashtab {
  352. public:
  353.   inthashtab(size_t size) : (size,imap,icompare) {}
  354.   int *lookup(int x) {
  355.     return (int *)hashtab::lookup((void *)&x);
  356.   }
  357.   void insert(int x) { 
  358.     int *ip = new int;
  359.     *ip = x;
  360.     hashtab::insert((void *)ip); 
  361.   }
  362.   void remove(int x) {
  363.     // get a pointer to the storage for this object,
  364.     // so we can delete it.
  365.     int *ip = (int *)hashtab::lookup((void *)&x);
  366.     if(ip != NULL) {
  367.       hashtab::remove((void *)ip);
  368.       delete ip;
  369.     }
  370.   }
  371. #if defined(DEBUG_HASHTAB)
  372.   void dump_tab(void) {
  373.     hashtab::dump_tab(idisplay);
  374.   }
  375. #endif
  376. };
  377.  
  378. inthashtab inttab(4);
  379.  
  380. main() {
  381.   char command;
  382.   int *current;
  383.   int i;
  384.   while(!cin.eof()) {
  385.     // prompt and pickup command
  386.     cout << "i = insert l = lookup d = delete s = show table q = quit : ";
  387.     eatwhite(cin);
  388.     cin.get(command);
  389.     // act upon commands
  390.     switch(tolower(command)) {
  391.     case 'i':
  392.       // grab an integer
  393.       cin >> i;
  394.       inttab.insert(i);
  395.       cout << i << " inserted.";
  396.       break;
  397.     case 'l':
  398.       // grab an integer
  399.       cin >> i;
  400.       current = inttab.lookup(i);
  401.       cout << i << " " << 
  402.         (current == NULL  ? "isn't" : "is") << " in the table";
  403.       break;
  404.     case 'd':
  405.       // grab an integer
  406.       cin >> i;
  407.       inttab.remove(i);
  408.       cout << i << " deleted";
  409.       break;
  410.     case 'q':
  411.       exit(0);
  412. #if defined(DEBUG_HASHTAB) && defined(__ZTC__)
  413.     case 's':
  414.       inttab.dump_tab();
  415.       break;
  416. #endif
  417.     }
  418.     cout.put('\n');
  419.   }
  420. }
  421.