一些测量。我获取了 10MB 的免费电子书文本并计算了三元组频率,生成了一个 24MB 的文件。将它存储在不同的简单 Python 数据结构中需要以 kB 为单位的这么多空间,以运行 ps 的 RSS 来衡量,其中 d 是 dict,keys 和 freqs 是列表,a,b,c,freq 是 trigram 记录的字段:
295760 S. Lott's answer
237984 S. Lott's with keys interned before passing in
203172 [*] d[(a,b,c)] = int(freq)
203156 d[a][b][c] = int(freq)
189132 keys.append((a,b,c)); freqs.append(int(freq))
146132 d[intern(a),intern(b)][intern(c)] = int(freq)
145408 d[intern(a)][intern(b)][intern(c)] = int(freq)
83888 [*] d[a+' '+b+' '+c] = int(freq)
82776 [*] d[(intern(a),intern(b),intern(c))] = int(freq)
68756 keys.append((intern(a),intern(b),intern(c))); freqs.append(int(freq))
60320 keys.append(a+' '+b+' '+c); freqs.append(int(freq))
50556 pair array
48320 squeezed pair array
33024 squeezed single array
标记为 [*] 的条目没有有效的方法来查找一对 (a,b);它们被列出只是因为其他人建议了它们(或它们的变体)。 (我有点厌烦做这个,因为投票最多的答案没有帮助,如表格所示。)
'Pair array' 是我原始答案中的以下方案(“我将从带键的数组开始
是前两个单词..."),其中每对的值表是
表示为单个字符串。 '挤压对阵列'是一样的,
省略等于 1 的频率值(最常见的
案子)。 “压缩单个数组”类似于压缩对数组,但将键和值组合为一个字符串(带有分隔符)。压缩后的单数组代码:
import collections
def build(file):
pairs = collections.defaultdict(list)
for line in file: # N.B. file assumed to be already sorted
a, b, c, freq = line.split()
key = ' '.join((a, b))
pairs[key].append(c + ':' + freq if freq != '1' else c)
out = open('squeezedsinglearrayfile', 'w')
for key in sorted(pairs.keys()):
out.write('%s|%s\n' % (key, ' '.join(pairs[key])))
def load():
return open('squeezedsinglearrayfile').readlines()
if __name__ == '__main__':
build(open('freqs'))
我还没有编写代码来从这个结构中查找值(使用 bisect,如下所述),或者实现下面描述的更高级的压缩结构。
原始答案: 一个简单的排序字符串数组,每个字符串都是用空格分隔的单词连接,使用 bisect 模块搜索,应该值得一试。这节省了指针等空间。由于单词的重复,它仍然浪费空间;有一个标准的技巧可以去除常见的前缀,用另一个级别的索引来取回它们,但这更复杂也更慢。 (想法是以必须顺序扫描的压缩形式存储数组的连续块,以及每个块的随机访问索引。块大到可以压缩,但对于合理的访问时间来说足够小。特定的压缩此处适用的方案:如果连续条目是“hello george”和“hello world”,则将第二个条目改为“6world”。(6 是公共前缀的长度。)或者你可以使用@987654321 @? 无论如何,您可以通过查找全文搜索中使用的字典结构来了解更多信息。)所以具体来说,我将从键为前两个单词的数组开始,并使用其条目列表的并行数组可能的第三个词及其频率。不过,它可能仍然很糟糕——我认为就包括电池的内存效率选项而言,你可能不走运。
此外,为了提高内存效率,不建议使用二叉树结构。例如,this paper 在类似的问题上测试各种数据结构(虽然是一元组而不是三元组),并找到一个哈希表来通过该度量击败所有树结构。
我应该像其他人一样提到,排序后的数组只能用于单词列表,而不是二元组或三元组;然后对于您的“真实”数据结构,无论它是什么,您都使用整数键而不是字符串——词表中的索引。 (但这可以防止您利用除了单词表本身之外的常见前缀。也许我毕竟不应该建议这个。)