如何使我的哈希算法更快
How to make my hashing algorithm faster
我的问题与 CS50 pset5 中的任务有关。对于那些对此一无所知的人,我会尽力解释。没什么特别的。我只需要创建一个函数来接收字典文件(之前写过,该文件中的所有单词都是大写的),其中包含超过 20K 个单词,并以某种方式对它们进行排序。我做了简单而天真的算法,构建哈希-table,它根据单词的第一个字母对单词进行排序。而且我已经通过了 CS50 的所有检查,所以我的程序运行良好。但与课程相比 - 它太慢了。人员的执行时间是0.1s,而我的是5.0s - 7.0s。我可以在此代码中改进什么以使其更快?还是我应该彻底改变一切?我没有优化经验,`因为刚开始学习。向你们中的任何一个人学习都会很棒 =) 提前致谢!
// Some constant values, which are declared before the function
#define LENGTH 46
#define ALPHALENGTH 26
/* Definition of node struct. Nothing special, in fact =) */
typedef struct node {
char word[LENGTH +1];
struct node *next;
} node;
node *hashTable[ALPHALENGTH];
bool load(const char *dictionary) {
FILE *f = fopen(dictionary, "r");
if (f == NULL) {
return false;
}
char word[LENGTH + 1];
int hash = 0;
for (int i = 0; i < ALPHALENGTH; i++) {
hashTable[i] = NULL;
}
// 46 - is LENGTH, but for some reason it is impossible
// to put variable`s name between quotation marks
while (fscanf(f, "%46s", word) == 1) {
// make every letter lowercase to get result from 0 - 25
hash = tolower(word[0]) - 'a';
node *new_node = malloc(sizeof(node));
strcpy(new_node->word, word);
// check if element is first in the list
if (hashTable[hash] == NULL) {
new_node->next = NULL;
hashTable[hash] = new_node;
} else {
node *ptr = hashTable[hash];
do {
if (ptr->next == NULL) {
break;
}
ptr = ptr->next;
} while (true);
ptr->next = new_node;
new_node->next = NULL;
}
}
fclose(f);
return true;
}
你的问题不在于你的哈希函数;是你的散列 table 太小了。
从事物的声音来看,您有大约 26 个哈希桶用于 20,000 多个单词。这会在每个桶中放置 750 到 1000 个单词。 (在某些情况下可能更多,因为您使用的哈希函数不统一。例如,以 x
或 q
开头的单词很少。)
尝试将哈希 table 扩展到 1000 个条目(例如),以便每个桶中大约有 20 个条目。您将需要一个新的哈希函数来执行此操作;任何东西都可以工作,但要工作得很好,它需要生成不超过 table 大小的值。 (例如,将所有字母的值加在一起是行不通的,因为它几乎永远不会达到 1000。)
问题是不在你的散列函数中,也不在你的散列大小table中,它在您的列表管理:您将单词附加到相应列表的方法的复杂度为 O(N2).
顺便说一句,您的散列函数 不是用于散列,而是用于调度。您仅在每个单词的第一个字母上 排序 table ,使具有相同首字母的单词保持相同的顺序。如果您打算对字典进行完全排序,您仍然需要对每个列表进行排序。
通过在列表前添加列表并在解析阶段结束时反转列表,您可以在保持相同语义的同时显着提高性能。
对于包含 20,000 个单词的词典,下面的代码运行速度 50 倍(正如 CS50 网站所预期的那样):
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LENGTH 46
#define ALPHALENGTH 26
typedef struct node {
struct node *next;
char word[LENGTH +1];
} node;
node *hashTable[ALPHALENGTH];
bool load(const char *dictionary) {
FILE *f = fopen(dictionary, "r");
if (f == NULL) {
return false;
}
char word[LENGTH + 1];
int hash = 0;
for (int i = 0; i < ALPHALENGTH; i++) {
hashTable[i] = NULL;
}
while (fscanf(f, "%46s", word) == 1) {
node *new_node = malloc(sizeof(node));
if (new_node == NULL)
return false;
// make every letter lowercase to get result from 0 - 25
hash = tolower(word[0]) - 'a';
strcpy(new_node->word, word);
/* prepending to the list */
new_node->next = hashTable[hash];
hashTable[hash] = new_node;
}
for (int i = 0; i < ALPHALENGTH; i++) {
node *n, *prev, *next;
/* reverse list */
for (prev = NULL, n = hashTable[i]; n != NULL; ) {
next = n->next;
n->next = prev;
prev = n;
n = next;
}
hashTable[i] = prev;
}
fclose(f);
return true;
}
void save(void) {
for (int i = 0; i < ALPHALENGTH; i++) {
for (node *n = hashTable[i]; n != NULL; n = n->next) {
puts(n->word);
}
}
}
int main(int argc, char *argv[]) {
if (argc > 1) {
if (load(argv[1]))
save();
}
}
将 fscanf()
更改为更简单的 fgets()
可能会提供边际性能改进,但代价是字典格式的语义更受限制。
我的问题与 CS50 pset5 中的任务有关。对于那些对此一无所知的人,我会尽力解释。没什么特别的。我只需要创建一个函数来接收字典文件(之前写过,该文件中的所有单词都是大写的),其中包含超过 20K 个单词,并以某种方式对它们进行排序。我做了简单而天真的算法,构建哈希-table,它根据单词的第一个字母对单词进行排序。而且我已经通过了 CS50 的所有检查,所以我的程序运行良好。但与课程相比 - 它太慢了。人员的执行时间是0.1s,而我的是5.0s - 7.0s。我可以在此代码中改进什么以使其更快?还是我应该彻底改变一切?我没有优化经验,`因为刚开始学习。向你们中的任何一个人学习都会很棒 =) 提前致谢!
// Some constant values, which are declared before the function
#define LENGTH 46
#define ALPHALENGTH 26
/* Definition of node struct. Nothing special, in fact =) */
typedef struct node {
char word[LENGTH +1];
struct node *next;
} node;
node *hashTable[ALPHALENGTH];
bool load(const char *dictionary) {
FILE *f = fopen(dictionary, "r");
if (f == NULL) {
return false;
}
char word[LENGTH + 1];
int hash = 0;
for (int i = 0; i < ALPHALENGTH; i++) {
hashTable[i] = NULL;
}
// 46 - is LENGTH, but for some reason it is impossible
// to put variable`s name between quotation marks
while (fscanf(f, "%46s", word) == 1) {
// make every letter lowercase to get result from 0 - 25
hash = tolower(word[0]) - 'a';
node *new_node = malloc(sizeof(node));
strcpy(new_node->word, word);
// check if element is first in the list
if (hashTable[hash] == NULL) {
new_node->next = NULL;
hashTable[hash] = new_node;
} else {
node *ptr = hashTable[hash];
do {
if (ptr->next == NULL) {
break;
}
ptr = ptr->next;
} while (true);
ptr->next = new_node;
new_node->next = NULL;
}
}
fclose(f);
return true;
}
你的问题不在于你的哈希函数;是你的散列 table 太小了。
从事物的声音来看,您有大约 26 个哈希桶用于 20,000 多个单词。这会在每个桶中放置 750 到 1000 个单词。 (在某些情况下可能更多,因为您使用的哈希函数不统一。例如,以 x
或 q
开头的单词很少。)
尝试将哈希 table 扩展到 1000 个条目(例如),以便每个桶中大约有 20 个条目。您将需要一个新的哈希函数来执行此操作;任何东西都可以工作,但要工作得很好,它需要生成不超过 table 大小的值。 (例如,将所有字母的值加在一起是行不通的,因为它几乎永远不会达到 1000。)
问题是不在你的散列函数中,也不在你的散列大小table中,它在您的列表管理:您将单词附加到相应列表的方法的复杂度为 O(N2).
顺便说一句,您的散列函数 不是用于散列,而是用于调度。您仅在每个单词的第一个字母上 排序 table ,使具有相同首字母的单词保持相同的顺序。如果您打算对字典进行完全排序,您仍然需要对每个列表进行排序。
通过在列表前添加列表并在解析阶段结束时反转列表,您可以在保持相同语义的同时显着提高性能。
对于包含 20,000 个单词的词典,下面的代码运行速度 50 倍(正如 CS50 网站所预期的那样):
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LENGTH 46
#define ALPHALENGTH 26
typedef struct node {
struct node *next;
char word[LENGTH +1];
} node;
node *hashTable[ALPHALENGTH];
bool load(const char *dictionary) {
FILE *f = fopen(dictionary, "r");
if (f == NULL) {
return false;
}
char word[LENGTH + 1];
int hash = 0;
for (int i = 0; i < ALPHALENGTH; i++) {
hashTable[i] = NULL;
}
while (fscanf(f, "%46s", word) == 1) {
node *new_node = malloc(sizeof(node));
if (new_node == NULL)
return false;
// make every letter lowercase to get result from 0 - 25
hash = tolower(word[0]) - 'a';
strcpy(new_node->word, word);
/* prepending to the list */
new_node->next = hashTable[hash];
hashTable[hash] = new_node;
}
for (int i = 0; i < ALPHALENGTH; i++) {
node *n, *prev, *next;
/* reverse list */
for (prev = NULL, n = hashTable[i]; n != NULL; ) {
next = n->next;
n->next = prev;
prev = n;
n = next;
}
hashTable[i] = prev;
}
fclose(f);
return true;
}
void save(void) {
for (int i = 0; i < ALPHALENGTH; i++) {
for (node *n = hashTable[i]; n != NULL; n = n->next) {
puts(n->word);
}
}
}
int main(int argc, char *argv[]) {
if (argc > 1) {
if (load(argv[1]))
save();
}
}
将 fscanf()
更改为更简单的 fgets()
可能会提供边际性能改进,但代价是字典格式的语义更受限制。