【问题标题】:How to make a flexible size hash table如何制作灵活大小的哈希表
【发布时间】:2014-10-30 09:31:59
【问题描述】:

我想使用这个过滤大文件的代码。目前我正在硬编码哈希表的大小,假设输入有 5000 万行。我希望总行数为哈希表大小的 37%。这是目前实现的,因为 0x8000000 的 37% 大约是 5000 万。但是,在实践中,在开始处理之前我不会知道输入的大小。如何修改代码以自动调整哈希表大小,使其大小合适?速度也很重要,因为过滤的目的是节省时间。

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

// Should be 37% occupied with 50m entries
#define TABLE_SIZE 0x8000000
#define MASK (TABLE_SIZE - 1)
#define BUFFER_SIZE 16384
#define END_OF_FILE (-1)
#define DEFAULT_VALUE (-1)

typedef struct Row {
  int32_t a;
  int32_t b;
  int32_t t;
} Row;

int32_t hash(int32_t a) {
  return a * 428916315;
}

void insert(Row * table, Row row) {
  long loc = hash(row.a) & MASK; // Entries are hashed on a
  long inc = 0;
  while (inc <= TABLE_SIZE) {
    loc = (loc + inc) & MASK;
    inc++;
    if (table[loc].a == DEFAULT_VALUE) {
      table[loc] = row;
      break;
    }
  }
}

int readChar(FILE * input, char * buffer, int * pos, int * limit) {
  if (*limit < *pos) {
    return buffer[(*limit)++];
  } else {
    *limit = 0;
    *pos = fread(buffer, sizeof(char), BUFFER_SIZE, input);
    if (*limit < *pos) {
      return buffer[(*limit)++];
    } else return END_OF_FILE;
  }
}

void readAll(char * fileName, Row * table) {
  char* buffer = (char*) malloc(sizeof(char) * BUFFER_SIZE);
  int limit = 0;
  int pos = 0;

  FILE * input = fopen(fileName, "rb");

  int lastRead;
  Row currentRow;
  uint32_t * currentElement = &(currentRow.a);

  // As with the Scala version, we read rows with an FSM. We can
  // roll up some of the code using the `currentElement` pointer
  while (1) {
    switch(lastRead = readChar(input, buffer, &pos, &limit)) {
      case END_OF_FILE:
        fclose(input);
        return;
      case ' ':
        if (currentElement == &(currentRow.a)) currentElement = &(currentRow.b);
        else currentElement = &(currentRow.t);
        break;
      case '\n':
        insert(table, currentRow);
        currentRow.a = 0;
        currentRow.b = 0;
        currentRow.t = 0;
        currentElement = &(currentRow.a);
        break;
      default:
        *currentElement = *currentElement * 10 + (lastRead - '0');
        break;
    }
  }
  //printf("Read %d", lastRead);
}

int main() {
  Row* table = (Row*) malloc(sizeof(Row) * TABLE_SIZE);
  memset(table, 255, sizeof(Row) * TABLE_SIZE);

  readAll("test.file", table);

  // We'll iterate through our hash table inline - passing a callback
  // is trickier in C than in Scala, so we just don't bother
  for (size_t i = 0; i < TABLE_SIZE; i++) {
    Row * this = table + i;
    if (this->a != DEFAULT_VALUE) {
      // Lookup entries `that`, where `that.a == this.b`
      long loc = hash(this->b) & MASK;
      long inc = 0;
      while (inc <= TABLE_SIZE) {
        loc = (loc + inc) & MASK;
        inc++;
        Row * that = table + loc;
        if ((this->b == that->a) && (0 <= that->t - this->t) && (that->t - this->t < 100)) {
          // Conditions are symmetric, so we output both rows
          printf("%d %d %d\n", this->a, this->b, this->t);
          printf("%d %d %d\n", that->a, that->b, that->t);
        }
        else if (that->b == DEFAULT_VALUE) break;
      }
    }
  }

  free(table);
  return 0;
}

【问题讨论】:

  • 您必须重新组织或扩大您的哈希表,例如更大的桶,不时或当您达到某个密度阈值时。实际上,重组几乎就像制作一个新的哈希表并用旧哈希表中的条目填充它。你可能想要newsize = 5*oldsize/4+10;
  • 你没有处理哈希冲突。您可以使表中的每个条目成为Row 的链表。
  • @ericbn 它是(或至少它应该是)开放散列。这应该可以避免您提到的问题。 en.wikipedia.org/wiki/Open_addressing
  • @user2179021,将冲突视为同一个桶中的列表可以使您保持表大小固定并让项目在另一个维度上增长。
  • 类似于 stackoverflow.com/questions/16907423/converting-static-to-dynamic-hash-table/16908837

标签: c hashtable


【解决方案1】:

读取文件的第一个,例如 100 KB,计算其换行符。从中推断以猜测您可能在整个文件中遇到的换行符总数(使用 O(1) 的总大小)。如果输入文件是相当规则的,这将给你一个足够接近的猜测来确定哈希表的大小。

【讨论】:

    猜你喜欢
    • 2014-05-09
    • 2011-04-03
    • 2013-03-29
    • 2015-07-13
    • 1970-01-01
    • 1970-01-01
    • 2011-06-24
    • 2012-04-09
    • 1970-01-01
    相关资源
    最近更新 更多