这是我目前正在进行的研究项目之一。需求几乎完全符合您的要求,我们已经开发出了不错的算法来解决该问题。
输入
输入的是无休止的英语单词或短语(我们将其称为tokens
)。
输出
- 到目前为止输出我们看到的前N个令牌(来自我们看到的所有令牌!)
- 在历史窗口(例如前一天或上周)中输出前N个令牌。
这项研究的应用是在Twitter或Facebook中找到热门话题或话题趋势。我们有一个在网站上进行爬网的爬网程序,该爬网程序生成单词流,这些单词流将输入到系统中。然后,系统将整体上或历史上输出最高频率的单词或短语。想象一下,在过去的几周中,“世界杯”一词在Twitter上出现了很多次。“章鱼保罗”也是如此。:)
串成整数
系统对每个单词都有一个整数ID。尽管Internet上几乎有无限可能的单词,但是在积累了大量单词之后,找到新单词的可能性越来越低。我们已经找到了400万个不同的单词,并为每个单词分配了唯一的ID。整个数据集可以作为哈希表加载到内存中,大约消耗300MB内存。(我们已经实现了自己的哈希表。Java的实现占用了大量内存开销)
然后,每个短语都可以标识为整数数组。
这很重要,因为对整数的排序和比较比对字符串的排序和比较快得多。
存档数据
系统为每个令牌保留存档数据。基本上是成对的(Token, Frequency)
。但是,存储数据的表非常庞大,以至于我们必须对表进行物理分区。一旦分区方案基于令牌的ngram。如果令牌是一个单词,则为1克。如果令牌是两个单词的短语,则为2gram。这样下去。大约4克时,我们有10亿条记录,表大小约为60GB。
处理传入流
系统将吸收传入的句子,直到内存被充分利用为止(是的,我们需要一个MemoryManager)。在提取了N个句子并将其存储在内存中之后,系统会暂停,并开始将每个句子标记为单词和短语。每个标记(单词或短语)都会被计数。
对于频繁使用的令牌,它们始终保留在内存中。对于不经常使用的令牌,它们将根据ID进行排序(请记住,我们将String转换为整数数组),然后序列化为磁盘文件。
(但是,对于您的问题,由于您仅在计算单词,因此您只能将所有单词-频率映射表都放在内存中。经过精心设计的数据结构将仅消耗300MB的内存来存储400万个单词。一些提示:使用ASCII字符可以代表字符串),这是可以接受的。
同时,一旦找到系统生成的任何磁盘文件,然后将其合并,将激活另一个进程。由于磁盘文件已排序,因此合并将采用类似合并排序的类似过程。由于我们要避免过多的随机磁盘搜寻,因此在这里也需要注意一些设计。这样做的目的是避免同时读取(合并进程)/写入(系统输出),并让合并进程从一个磁盘读取,同时写入另一个磁盘。这类似于实现锁定。
一天的结束
一天结束时,系统将在内存中存储很多频繁使用的令牌,并且频率存储在其他磁盘文件中(并且对每个文件进行了排序),其他许多不经常使用的令牌也存储在磁盘文件中。
系统将内存映射刷新到磁盘文件中(对它进行排序)。现在,问题就变成了合并一组已排序的磁盘文件。使用类似的过程,我们最终将得到一个排序后的磁盘文件。
然后,最后的任务是将排序后的磁盘文件合并到存档数据库中。取决于存档数据库的大小,如果足够大,该算法的工作原理如下:
for each record in sorted disk file
update archive database by increasing frequency
if rowcount == 0 then put the record into a list
end for
for each record in the list of having rowcount == 0
insert into archive database
end for
直觉是,一段时间后,插入次数会越来越少。越来越多的操作将仅在更新中进行。并且此更新不会受到索引的惩罚。
希望整个解释对您有所帮助。:)
what is the most frequent item in the subsequence [2; 2; 3; 3; 3; 4; 4; 4; 4; 5; 5] of your sequence?