C++ 计算给定单词在超过10亿单词的文本语料库中出现的次数

C++ 计算给定单词在超过10亿单词的文本语料库中出现的次数,c++,algorithm,data-structures,C++,Algorithm,Data Structures,我正在设计一个程序,用户输入一个单词,该程序确定该单词在文本语料库中出现了多少次。现在文本语料库太大,无法放入内存,为了优化内存,我决定使用磁盘数据结构。我想使用哈希表进行插入和搜索,但不知道如何为磁盘设计它。如何设计密钥,以便在磁盘上查找密钥的值时需要恒定的时间。我是否应该为特定的密钥子集创建单独的文件,以便查找为O(1)?我知道存在B树,但如何为这样的应用程序设计这样的哈希表?提前感谢您的回答 使用trie怎么样?您将创建一个包含相同记录(一组整数索引,每个字母一个)的文件,这些记录被视为一

我正在设计一个程序,用户输入一个单词,该程序确定该单词在文本语料库中出现了多少次。现在文本语料库太大,无法放入内存,为了优化内存,我决定使用磁盘数据结构。我想使用哈希表进行插入和搜索,但不知道如何为磁盘设计它。如何设计密钥,以便在磁盘上查找密钥的值时需要恒定的时间。我是否应该为特定的密钥子集创建单独的文件,以便查找为O(1)?我知道存在B树,但如何为这样的应用程序设计这样的哈希表?提前感谢您的回答

使用trie怎么样?您将创建一个包含相同记录(一组整数索引,每个字母一个)的文件,这些记录被视为一个大数组,因此可以访问radom。您将需要一次处理一个节点,因此无需担心RAM空间。这需要大量的空间,但实现起来很容易。

因为,存储10亿个令牌所需的实际内存占用可能会非常小:自然语言(以及许多其他东西)紧随其后,这基本上意味着你最常用的单词将远比第二常用的单词更常见,这比第三最常见的更常见,以此类推。因此,这10亿个代币中有很大一部分是a和,假设你是为英语这么做的:

换句话说,只需先尝试使用
未排序的_映射
,看看它是如何工作的

实验:内存中的实际大小 因为,我决定看看一个
未排序的_映射
是否可以容纳所有需要的类型及其计数。首先,我使用Python获得了中唯一单词的数量:

这给了我49815个独特单词的结果。然后,我创建了一个带有49815个键的
未排序的_映射
,然后通过修改以下内容来估计其大小:

#包括
#包括
#包括
#包括
使用名称空间std;
//使用uint_least32_t进行令牌计数,因为uint_least16_t对于计数频率来说可能有点太窄
typedef无序映射标记frequencymap;
静态大小\u t估计内存(const-TokenFrequencyMap和map)
{
size_t entrySize=sizeof(TokenFrequencyMap::key_type)+sizeof(TokenFrequencyMap::mapped_type)+sizeof(void*);
尺寸=尺寸(空*);
size\u t adminSize=3*sizeof(void*)+sizeof(TokenFrequencyMap::size\u类型);
返回adminSize+map.size()*entrySize+map.bucket\u count()*bucketSize;
}
int main()
{
constexpr TokenFrequencyMap::size_type vocabSize=49815;
TokenFrequencyMap计数;
计数。保留(vocabSize);
对于(TokenFrequencyMap::size_type i=0;icout这是否能在2MB内存需求范围内完成取决于语料库中不同单词的数量。如果您使用前面答案中提到的棕色语料库,您有:

49,815 words at 8.075 characters average length = 402,256 bytes
49,815 counts at 4 bytes per count = 199,260 bytes
如果要将所有字符打包到一个字符数组中,以便按顺序搜索,则需要添加另一个49815 nul终止符。结构如下:

word,\0,count,word,\0,count . . .
这将需要总共651331个字节,所以您至少知道您的原始数据可以放在内存中

你可以创造性地在这个数组中添加一个带有49815个指针的排序索引。这将花费你另外199260字节,并提供O(log2(n))查找。考虑到键的数量很少,这将是非常糟糕的快速查找。不是常量,但非常好,适合小于1兆字节

如果需要恒定的查找时间,可以为键生成一个索引。然后用指针数组替换我上面提到的排序索引。不需要存储键。最小完美哈希生成一个从0到n的数字;称之为
k
。您可以转到数组中的
k
第个索引来检索指针,
p
>进入平面阵列

生成哈希函数不应该花费太长时间。在中,作者声称他在大约2.5秒内创建了一个100000字的最小完美has函数。您可以在预处理期间构建它,也可以让程序在启动时计算它


所有这些都应该在一兆字节的空间内,并且应该比标准映射执行得更快,因为它保证不会发生冲突。因此,没有一个bucket包含多个值。内存分配开销也被最小化,因为只有两个分配:一个用于原始数据数组,另一个用于索引数组。

Read sin从缓冲的
std::ifstream
中提取单词,并具有
std::map
。构建一个哈希映射,其中单词是键,计数是存储值。这应该很容易在大多数现代计算机的内存中找到,因为单词的数量可能只有几千个(即使它们的计数很高)。数据结构不会存储在RAM上吗?我如何使用磁盘内存实现这一点?@physio为什么不使用磁盘内存?创建一个可重新读取的索引文件?语料库中的单词数是多少?下限(“至少n个单词”)也会很有帮助。每层可以使用两到三个字母来减少步骤数。很有趣,但是…看起来您在实验中使用了字符串“1”到“49815”作为单词。因此字符串的最大大小是五个字符(加上终止符)。这可能意味着“文本”直接在字符串对象中,给定小字符串优化。我怀疑真实语料库中唯一单词的平均长度会更长,从而使用更多堆。事实上,棕色语料库中唯一标记的平均长度是
49,815 words at 8.075 characters average length = 402,256 bytes
49,815 counts at 4 bytes per count = 199,260 bytes
word,\0,count,word,\0,count . . .