大数据集散列与C语言实现
我有大量的值,范围从0到5463458053。对于每个值,我希望映射一个包含字符串的集合,以便操作查找,I。E查找该集中是否存在字符串花费的时间最少。请注意,这组值可能不包含(0-5463458053)中的所有值,但确实包含大量值 我目前的解决方案是散列这些值(0-5463458053之间),对于每个值,都有一个对应于该值的字符串链接列表。每次我想检查给定集合中的字符串时,我都会散列该值(介于0-5463458053之间),获取链表,并遍历它以确定它是否包含上述字符串 虽然这看起来比较容易,但有点费时。你能想出一个更快的解决办法吗?而且,碰撞将是可怕的。它们会导致错误的结果 另一部分是关于在C中实现这一点。我将如何实现这一点 注意:有人建议改用数据库。我想知道这是否有用大数据集散列与C语言实现,c,gcc,data-structures,hash,C,Gcc,Data Structures,Hash,我有大量的值,范围从0到5463458053。对于每个值,我希望映射一个包含字符串的集合,以便操作查找,I。E查找该集中是否存在字符串花费的时间最少。请注意,这组值可能不包含(0-5463458053)中的所有值,但确实包含大量值 我目前的解决方案是散列这些值(0-5463458053之间),对于每个值,都有一个对应于该值的字符串链接列表。每次我想检查给定集合中的字符串时,我都会散列该值(介于0-5463458053之间),获取链表,并遍历它以确定它是否包含上述字符串 虽然这看起来比较容易,但有
我有点担心内存自然会用完。:-) 如果条目从0到N且连续:使用数组。(索引速度够快吗?) 编辑:数字似乎不是连续的。有大量的{key,value}对,其中key是一个大数字(>32位但<64位),value是一组字符串 如果内存可用,则哈希表很容易,如果字符串串不是太大,则可以按顺序检查它们。如果相同的字符串不止一次出现(很多次),您可以枚举这些字符串(将指向它们的指针放在char*数组[]中,并使用该数组中的索引。查找给定字符串的索引可能涉及另一个哈希表) 对于“主”哈希表,条目可能是:
struct entry {
struct entry *next; /* for overflow chain */
unsigned long long key; /* the 33bits number */
struct list *payload;
} entries[big_enough_for_all] ; /* if size is known in advance
, preallocation avoids a lot of malloc overhead */
如果您有足够的内存来存储磁头阵列,您当然可以这样做:
struct entry *heads[SOME_SIZE] = {NULL, };
,否则可以将heads数组与条目数组组合。(就像我在这里做的那样)
处理冲突很容易:当您遍历溢出链时,只需将您的密钥与条目中的密钥进行比较。如果它们不相等:继续走。如果它们相等:找到;现在开始遍历字符串。如果条目从0到N且连续:使用数组。(索引速度够快吗?) 编辑:数字似乎不是连续的。有大量的{key,value}对,其中key是一个大数字(>32位但<64位),value是一组字符串 如果内存可用,则哈希表很容易,如果字符串串不是太大,则可以按顺序检查它们。如果相同的字符串不止一次出现(很多次),您可以枚举这些字符串(将指向它们的指针放在char*数组[]中,并使用该数组中的索引。查找给定字符串的索引可能涉及另一个哈希表) 对于“主”哈希表,条目可能是:
struct entry {
struct entry *next; /* for overflow chain */
unsigned long long key; /* the 33bits number */
struct list *payload;
} entries[big_enough_for_all] ; /* if size is known in advance
, preallocation avoids a lot of malloc overhead */
如果您有足够的内存来存储磁头阵列,您当然可以这样做:
struct entry *heads[SOME_SIZE] = {NULL, };
,否则可以将heads数组与条目数组组合。(就像我在这里做的那样)
处理冲突很容易:当您遍历溢出链时,只需将您的密钥与条目中的密钥进行比较。如果它们不相等:继续走。如果它们相等:找到;现在开始遍历字符串。您可以得到一个哈希集的哈希表。第一个哈希表的键是整数。其中的值是散列集,即键为字符串的散列表 您还可以有一个散列集,其中键是整数和字符串对 <> P>有许多库实现了这些数据结构(C++中,标准库正在实现它们,如<代码> STD::MAP< /Cord>&<代码> STD::SET)。对于C,我想到的是GTK
使用散列技术,内存使用与所考虑的集合(或关系)的大小成比例。例如,您可以接受30%的空率。您可以有一个哈希集的哈希表。第一个哈希表的键是整数。其中的值是散列集,即键为字符串的散列表 您还可以有一个散列集,其中键是整数和字符串对 <> P>有许多库实现了这些数据结构(C++中,标准库正在实现它们,如<代码> STD::MAP< /Cord>&<代码> STD::SET)。对于C,我想到的是GTK 使用散列技术,内存使用与所考虑的集合(或关系)的大小成比例。例如,您可以接受30%的空率。A和实现它的C库可能正是您需要的基础。下面是一段描述它的引文: Judy是一个C库,提供最先进的核心技术 它实现了一个稀疏动态数组。Judy数组被声明 只需使用空指针。Judy阵列仅在 已填充,但可以扩展以利用所有可用内存 如果需要的话。Judy的主要优势是可扩展性、高性能和 内存效率。Judy阵列是可扩展的,可以扩展到 大量元素,仅由机器内存限定。自从 Judy被设计为无界数组,Judy数组的大小为 未预先分配,但随阵列动态增长和收缩 人口Judy将可伸缩性与易用性结合起来。朱迪API 通过简单的插入、检索和删除调用访问 需要大量的编程。不需要进行调整和配置 (事实上甚至不可能)。此外,排序、搜索、计数和 Judy的设计中内置了顺序访问功能 Judy可以在开发人员需要动态大小的阵列时使用, 关联数组或简单易用的界面,不需要 为扩展或con而返工
function iterate_all_sets ()
{
node = first_node_in_tree();
while (node != null) {
current_set = node.set_number;
do_something_with(current_set);
if (cannot increment current_set) {
return;
}
node = lookup_tree_weak(current_set + 1, "");
if (node.set_number == current_set) {
node = successor(node);
}
}
}
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <assert.h>
#include <structure/BAVL.h>
#include <misc/offset.h>
struct value {
uint32_t set_no;
char str[3];
};
struct node {
uint8_t is_used;
struct value val;
BAVLNode tree_node;
};
BAVL tree;
static int value_comparator (void *unused, void *vv1, void *vv2)
{
struct value *v1 = vv1;
struct value *v2 = vv2;
if (v1->set_no < v2->set_no) {
return -1;
}
if (v1->set_no > v2->set_no) {
return 1;
}
int c = strcmp(v1->str, v2->str);
if (c < 0) {
return -1;
}
if (c > 0) {
return 1;
}
return 0;
}
static void random_bytes (unsigned char *out, size_t n)
{
while (n > 0) {
*out = rand();
out++;
n--;
}
}
static void random_value (struct value *out)
{
random_bytes((unsigned char *)&out->set_no, sizeof(out->set_no));
for (size_t i = 0; i < sizeof(out->str) - 1; i++) {
out->str[i] = (uint8_t)32 + (rand() % 94);
}
out->str[sizeof(out->str) - 1] = '\0';
}
static struct node * find_node (const struct value *val)
{
// find AVL tree node with an equal value
BAVLNode *tn = BAVL_LookupExact(&tree, (void *)val);
if (!tn) {
return NULL;
}
// get node pointer from pointer to its value (same as container_of() in Linux kernel)
struct node *n = UPPER_OBJECT(tn, struct node, tree_node);
assert(n->val.set_no == val->set_no);
assert(!strcmp(n->val.str, val->str));
return n;
}
static struct node * lookup_weak (const struct value *v)
{
BAVLNode *tn = BAVL_Lookup(&tree, (void *)v);
if (!tn) {
return NULL;
}
return UPPER_OBJECT(tn, struct node, tree_node);
}
static struct node * first_node (void)
{
BAVLNode *tn = BAVL_GetFirst(&tree);
if (!tn) {
return NULL;
}
return UPPER_OBJECT(tn, struct node, tree_node);
}
static struct node * next_node (struct node *node)
{
BAVLNode *tn = BAVL_GetNext(&tree, &node->tree_node);
if (!tn) {
return NULL;
}
return UPPER_OBJECT(tn, struct node, tree_node);
}
size_t num_found;
static void iterate_all_strings_in_set (uint32_t set_no)
{
struct value v;
v.set_no = set_no;
v.str[0] = '\0';
struct node *n = lookup_weak(&v);
if (!n) {
return;
}
if (n->val.set_no != set_no) {
n = next_node(n);
}
while (n && n->val.set_no == set_no) {
num_found++; // "do_something_with_string"
n = next_node(n);
}
}
static void iterate_all_sets (void)
{
struct node *node = first_node();
while (node) {
uint32_t current_set = node->val.set_no;
iterate_all_strings_in_set(current_set); // "do_something_with_set"
if (current_set == UINT32_MAX) {
return;
}
struct value v;
v.set_no = current_set + 1;
v.str[0] = '\0';
node = lookup_weak(&v);
if (node->val.set_no == current_set) {
node = next_node(node);
}
}
}
int main (int argc, char *argv[])
{
size_t num_nodes = 10000000;
// init AVL tree, using:
// key=(struct node).val,
// comparator=value_comparator
BAVL_Init(&tree, OFFSET_DIFF(struct node, val, tree_node), value_comparator, NULL);
printf("Allocating...\n");
// allocate nodes (missing overflow check...)
struct node *nodes = malloc(num_nodes * sizeof(nodes[0]));
if (!nodes) {
printf("malloc failed!\n");
return 1;
}
printf("Inserting %zu nodes...\n", num_nodes);
size_t num_inserted = 0;
// insert nodes, giving them random values
for (size_t i = 0; i < num_nodes; i++) {
struct node *n = &nodes[i];
// choose random set number and string
random_value(&n->val);
// try inserting into AVL tree
if (!BAVL_Insert(&tree, &n->tree_node, NULL)) {
printf("Insert collision: (%"PRIu32", '%s') already exists!\n", n->val.set_no, n->val.str);
n->is_used = 0;
continue;
}
n->is_used = 1;
num_inserted++;
}
printf("Looking up...\n");
// lookup all those values
for (size_t i = 0; i < num_nodes; i++) {
struct node *n = &nodes[i];
struct node *lookup_n = find_node(&n->val);
if (n->is_used) { // this node is the only one with this value
ASSERT(lookup_n == n)
} else { // this node was an insert collision; some other
// node must have this value
ASSERT(lookup_n != NULL)
ASSERT(lookup_n != n)
}
}
printf("Iterating by sets...\n");
num_found = 0;
iterate_all_sets();
ASSERT(num_found == num_inserted)
printf("Removing all strings...\n");
for (size_t i = 0; i < num_nodes; i++) {
struct node *n = &nodes[i];
if (!n->is_used) { // must not remove it it wasn't inserted
continue;
}
BAVL_Remove(&tree, &n->tree_node);
}
return 0;
}