C에서 사전을 구현하는 빠른 방법
C에서 프로그램을 쓰면서 내가 그리워하는 것 중 하나는 사전 데이터 구조다.C에서 구현하는 가장 편리한 방법은 무엇인가?나는 성능을 찾는 것이 아니라 처음부터 코딩하기 쉬운 것을 찾고 있다.나도 일반적이지 않았으면 좋겠어. 뭐랄까.char*
→int
할 이다. 나는 할 수 하지만 나는 그것이 임의의 수의 항목을 저장할 수 있기를 원한다.
이것은 연습으로 더 의도된 것이다.이용 가능한 제3자 도서관이 있다는 것을 알고 있다.하지만 그들이 존재하지 않는다는 것을 잠시 생각해 보라.이런 상황에서 위의 요구 사항을 충족하는 사전을 가장 빨리 구현할 수 있는 방법은 무엇인가?
C 프로그래밍 언어의 6.6절은 간단한 사전(해시테이블) 데이터 구조를 제시한다.나는 유용한 사전 구현이 이것보다 더 간단해질 수 있다고 생각하지 않는다.당신의 편의를 위해 여기에 코드를 재현한다.
struct nlist { /* table entry: */
struct nlist *next; /* next entry in chain */
char *name; /* defined name */
char *defn; /* replacement text */
};
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */
/* hash: form hash value for string s */
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
/* lookup: look for s in hashtab */
struct nlist *lookup(char *s)
{
struct nlist *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np; /* found */
return NULL; /* not found */
}
char *strdup(char *);
/* install: put (name, defn) in hashtab */
struct nlist *install(char *name, char *defn)
{
struct nlist *np;
unsigned hashval;
if ((np = lookup(name)) == NULL) { /* not found */
np = (struct nlist *) malloc(sizeof(*np));
if (np == NULL || (np->name = strdup(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];
hashtab[hashval] = np;
} else /* already there */
free((void *) np->defn); /*free previous defn */
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}
char *strdup(char *s) /* make a duplicate of s */
{
char *p;
p = (char *) malloc(strlen(s)+1); /* +1 for ’\0’ */
if (p != NULL)
strcpy(p, s);
return p;
}
두 문자열의 해시가 충돌할 경우, 두 문자열의 해시가 충돌할 수 있다는 점에 유의하십시오.O(n)
조회 시간의 값을 높여 충돌 가능성을 줄일 수 있다.HASHSIZE
에 대한 데이터 구조에 대한 자세한 내용은 책을 참조하십시오.
가장 빠른 방법은 우타시처럼 이미 존재하는 구현을 사용하는 것이다.
그리고, 만약 당신이 정말로 그것을 직접 코딩하고 싶다면, 그 알고리즘은uthash
검사하여 재사용할 수 있다.BSD 허가증이 있어서 저작권 고지서를 전달하기 위한 요건 외에 당신이 할 수 있는 일에는 거의 제한이 없다.
구현의 용이성을 위해, 배열을 통한 순진하게 검색하는 것을 이기기는 어렵다.일부 오류 점검은 차치하고, 이것은 완전한 구현(테스트되지 않음)이다.
typedef struct dict_entry_s {
const char *key;
int value;
} dict_entry_s;
typedef struct dict_s {
int len;
int cap;
dict_entry_s *entry;
} dict_s, *dict_t;
int dict_find_index(dict_t dict, const char *key) {
for (int i = 0; i < dict->len; i++) {
if (!strcmp(dict->entry[i], key)) {
return i;
}
}
return -1;
}
int dict_find(dict_t dict, const char *key, int def) {
int idx = dict_find_index(dict, key);
return idx == -1 ? def : dict->entry[idx].value;
}
void dict_add(dict_t dict, const char *key, int value) {
int idx = dict_find_index(dict, key);
if (idx != -1) {
dict->entry[idx].value = value;
return;
}
if (dict->len == dict->cap) {
dict->cap *= 2;
dict->entry = realloc(dict->entry, dict->cap * sizeof(dict_entry_s));
}
dict->entry[dict->len].key = strdup(key);
dict->entry[dict->len].value = value;
dict->len++;
}
dict_t dict_new(void) {
dict_s proto = {0, 10, malloc(10 * sizeof(dict_entry_s))};
dict_t d = malloc(sizeof(dict_s));
*d = proto;
return d;
}
void dict_free(dict_t dict) {
for (int i = 0; i < dict->len; i++) {
free(dict->entry[i].key);
}
free(dict->entry);
free(dict);
}
창에서는 사용할 수 없지만 POSIX에 의해 위임되어 Linux/GNU 시스템에서 사용할 수 있는 라이브러리의 hsearch/hreate를 언급하는 사람이 아무도 없다는 것이 놀랍다.
링크는 그 용도를 매우 잘 설명하는 간단하고 완전한 기본적인 예를 가지고 있다.
심지어 나사산 안전 변형도 가지고 있고, 사용하기 쉬우며 성능이 매우 뛰어나다.
GLib and gnulib
이러한 것들은 널리 이용 가능하고 휴대 가능하며 효율적이기 때문에 더 구체적인 요구사항이 없다면 최선의 선택일 것이다.
GNOME 프로젝트에서 https://developer.gnome.org/glib/.https://developer.gnome.org/glib/stable/glib-data-types.html에 문서화된 여러 컨테이너: "해시 테이블" 및 "균형 이진 트리" 포함.라이선스: LGPL
gnulib: GNU 프로젝트에 의해 https://www.gnu.org/software/gnulib/.당신은 당신의 코드에 소스를 복사해야 한다.https://www.gnu.org/software/gnulib/MODULES.html#ansic_ext_container에 문서화된 여러 컨테이너: "rbtree-list", "linkedhash-list" 및 "rbtreehash-list".GPL 면허증.
참고 항목:공통 데이터 구조를 가진 오픈 소스 C 라이브러리가 있는가?
해시테이블은 간단한 "Dictionary"의 전통적인 구현이다.속도나 크기에 신경 쓰지 않는다면 그냥 검색해봐.자유롭게 이용할 수 있는 구현이 많다.
여기 내가 처음 본 것이 있는데, 한눈에 봐도 괜찮아 보인다. (이건 꽤 기본이다.)만약 당신이 정말로 그것이 무제한의 데이터를 저장하기를 원한다면, 당신은 테이블 메모리가 자라면서 "복원"하기 위해 약간의 논리를 추가할 필요가 있을 것이다.)
해싱이 관건이다.이거 검색대와 해싱 키를 사용하는 것 같아.온라인에서 해싱 기능을 많이 찾을 수 있다.
간단한 해시함수와 몇몇 링크된 구조물 리스트를 만드세요, 해시에 따라서, 어떤 링크된 리스트를 에 삽입할 것인지 할당하세요, 또한 그것을 검색하기 위해 해시를 사용하세요.
나는 얼마 전에 간단한 구현을 했다.
...#Define K 16 // 체인 계수 구조 지시{문자 *name; /* 키 이름 */int val; /* 값 */구조체 명령 *다음; /* 링크 필드 */}; 타이핑된 구조 지시문;명령 *table[K]; int 초기화 = 0; 보이드 퍼발(문자 *,int); void init_properties{초기화 = 1;in i;for(i=0;iname = (char *) malloc (stlen(key_name)+1); ptr->>val = sval;strcpy (ptr->name,key_name); ptr->다음 = (지시 *)테이블[hsh]; 테이블[hsh] = ptr; } int getval ( char *key_name ){int hsh = 해시(key_name);명령 *ptr;(ptr = 테이블[hsh]; ptr != (propert *) 0;ptr = (propert *)ptr->다음)if (strcmp (ptr->name,key_name) == 0)ptr->val을 반환한다.반환 -1;}
여기 빠른 구현이 있는데, 나는 그것을 끈에서 '매트릭스'를 얻는 데 사용했다.더 큰 배열을 가질 수 있고 실행 시 값을 변경할 수도 있다.
typedef struct { int** lines; int isDefined; }mat;
mat matA, matB, matC, matD, matE, matF;
/* an auxilary struct to be used in a dictionary */
typedef struct { char* str; mat *matrix; }stringToMat;
/* creating a 'dictionary' for a mat name to its mat. lower case only! */
stringToMat matCases [] =
{
{ "mat_a", &matA },
{ "mat_b", &matB },
{ "mat_c", &matC },
{ "mat_d", &matD },
{ "mat_e", &matE },
{ "mat_f", &matF },
};
mat* getMat(char * str)
{
stringToMat* pCase;
mat * selected = NULL;
if (str != NULL)
{
/* runing on the dictionary to get the mat selected */
for(pCase = matCases; pCase != matCases + sizeof(matCases) / sizeof(matCases[0]); pCase++ )
{
if(!strcmp( pCase->str, str))
selected = (pCase->matrix);
}
if (selected == NULL)
printf("%s is not a valid matrix name\n", str);
}
else
printf("expected matrix name, got NULL\n");
return selected;
}
가장 빠른 방법은 이진 트리를 사용하는 것이다.최악의 경우도 O(logn)에 불과하다.
또한 Google CityHash:
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <byteswap.h>
#include "city.h"
void swap(uint32* a, uint32* b) {
int temp = *a;
*a = *b;
*b = temp;
}
#define PERMUTE3(a, b, c) swap(&a, &b); swap(&a, &c);
// Magic numbers for 32-bit hashing. Copied from Murmur3.
static const uint32 c1 = 0xcc9e2d51;
static const uint32 c2 = 0x1b873593;
static uint32 UNALIGNED_LOAD32(const char *p) {
uint32 result;
memcpy(&result, p, sizeof(result));
return result;
}
static uint32 Fetch32(const char *p) {
return UNALIGNED_LOAD32(p);
}
// A 32-bit to 32-bit integer hash copied from Murmur3.
static uint32 fmix(uint32 h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
static uint32 Rotate32(uint32 val, int shift) {
// Avoid shifting by 32: doing so yields an undefined result.
return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
}
static uint32 Mur(uint32 a, uint32 h) {
// Helper from Murmur3 for combining two 32-bit values.
a *= c1;
a = Rotate32(a, 17);
a *= c2;
h ^= a;
h = Rotate32(h, 19);
return h * 5 + 0xe6546b64;
}
static uint32 Hash32Len13to24(const char *s, size_t len) {
uint32 a = Fetch32(s - 4 + (len >> 1));
uint32 b = Fetch32(s + 4);
uint32 c = Fetch32(s + len - 8);
uint32 d = Fetch32(s + (len >> 1));
uint32 e = Fetch32(s);
uint32 f = Fetch32(s + len - 4);
uint32 h = len;
return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));
}
static uint32 Hash32Len0to4(const char *s, size_t len) {
uint32 b = 0;
uint32 c = 9;
for (size_t i = 0; i < len; i++) {
signed char v = s[i];
b = b * c1 + v;
c ^= b;
}
return fmix(Mur(b, Mur(len, c)));
}
static uint32 Hash32Len5to12(const char *s, size_t len) {
uint32 a = len, b = len * 5, c = 9, d = b;
a += Fetch32(s);
b += Fetch32(s + len - 4);
c += Fetch32(s + ((len >> 1) & 4));
return fmix(Mur(c, Mur(b, Mur(a, d))));
}
uint32 CityHash32(const char *s, size_t len) {
if (len <= 24) {
return len <= 12 ?
(len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) :
Hash32Len13to24(s, len);
}
// len > 24
uint32 h = len, g = c1 * len, f = g;
uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
h ^= a0;
h = Rotate32(h, 19);
h = h * 5 + 0xe6546b64;
h ^= a2;
h = Rotate32(h, 19);
h = h * 5 + 0xe6546b64;
g ^= a1;
g = Rotate32(g, 19);
g = g * 5 + 0xe6546b64;
g ^= a3;
g = Rotate32(g, 19);
g = g * 5 + 0xe6546b64;
f += a4;
f = Rotate32(f, 19);
f = f * 5 + 0xe6546b64;
size_t iters = (len - 1) / 20;
do {
uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2;
uint32 a1 = Fetch32(s + 4);
uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;
uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;
uint32 a4 = Fetch32(s + 16);
h ^= a0;
h = Rotate32(h, 18);
h = h * 5 + 0xe6546b64;
f += a1;
f = Rotate32(f, 19);
f = f * c1;
g += a2;
g = Rotate32(g, 18);
g = g * 5 + 0xe6546b64;
h ^= a3 + a1;
h = Rotate32(h, 19);
h = h * 5 + 0xe6546b64;
g ^= a4;
g = bswap_32(g) * 5;
h += a4 * 5;
h = bswap_32(h);
f += a0;
PERMUTE3(f, h, g);
s += 20;
} while (--iters != 0);
g = Rotate32(g, 11) * c1;
g = Rotate32(g, 17) * c1;
f = Rotate32(f, 11) * c1;
f = Rotate32(f, 17) * c1;
h = Rotate32(h + g, 19);
h = h * 5 + 0xe6546b64;
h = Rotate32(h, 17) * c1;
h = Rotate32(h + f, 19);
h = h * 5 + 0xe6546b64;
h = Rotate32(h, 17) * c1;
return h;
}
참조URL: https://stackoverflow.com/questions/4384359/quick-way-to-implement-dictionary-in-c
'programing' 카테고리의 다른 글
Vue Js 단일 파일 템플릿에서 혼합물을 사용하는 방법? (0) | 2022.05.09 |
---|---|
왜 +++++B가 작동하지 않는가? (0) | 2022.05.09 |
Vuejs 구성 요소 템플릿에는 루트 1개 요소만 필요한가? (0) | 2022.05.09 |
vue.js 다이내믹 구성 요소의 keep-alive와 함께 cytoscape.js를 사용하는 방법? (0) | 2022.05.09 |
vue js에서 숫자와 regex를 사용하여 검증하는 방법 (0) | 2022.05.09 |