【问题标题】:Dictionary Trie far too large字典 Trie 太大
【发布时间】:2013-06-06 18:25:58
【问题描述】:

我为字典查找类构建了一个 trie。它似乎工作正常,除了特里相当大。似乎大约是 80 MB,从我读过的内容来看,它应该只有 5 MB 大。我不确定是什么让 trie 气球达到 80 MB,但一旦加载它就会运行得非常快。

Trie 类

public class Trie {


private TrieNode root = new TrieNode();
public const int ASCIIA = 97;

public TrieNode Insert(string word) {

    char[] charArray = word.ToLower().ToCharArray();
    TrieNode node = root;

    foreach (char character in charArray) {
        node = Insert(character, node);

    }

    node.IsEnd = true;
    return root;
}

private TrieNode Insert(char character, TrieNode node) {
    if (node.Contains(character)) {
        return node.GetChild(character);
    } else {
        int number = System.Convert.ToByte(character) - TrieNode.ASCIIA;
        TrieNode treeNode = new TrieNode();
        node.nodes[number] = treeNode;
        treeNode.Value = number;
        return treeNode;
    }

}

TrieNode 类:

public class TrieNode {

public TrieNode[] nodes;
public bool IsEnd {get; set;}
public int Value {get; set;}
public const int ASCIIA = 97;
public const int ENGL = 26;

public TrieNode() {
    nodes = new TrieNode[ENGL]; 
}

public bool Contains(char character) {
    if (character == 0) 
        return false;

    int number = System.Convert.ToByte(character) - ASCIIA;

    if (number > ENGL)
        return false;

    return (nodes[number] != null);
}


public bool Contains(int character) {

    if (character == 0) 
        return false;

    return (nodes[character] != null);
}

public TrieNode GetChild(char character) {
    int number = System.Convert.ToByte(character) - ASCIIA;
    return nodes[number];
}

public TrieNode GetChild(int character) {
    return nodes[character];
}

然后使用包含 170,000 个单词的字典来 Gen the trie:

    string[] lines = fileTXT.Split("\n"[0]);
for (int i = 0; i < data.Length;i++) {
        trieDict.Insert(data[i]);
}

【问题讨论】:

  • 您如何确定它占用了 80MB?如果您在 TaskManager 中查看,可能只是进程使用了​​多少(并且没有打扰垃圾收集),而不是 Trie 使用了多少。
  • 我正在使用分析器,但我承认我不是 100% 了解这些东西的工作原理。有没有办法可以准确地确定它使用了多少空间?我构建了多个尝试,它们似乎总是占用 80 mb 并且从不 GC
  • 你的 Trie 没有优化...所以是的,它会占用大量的内存。您可以通过查找跨单词重复的后缀,然后只保留所有前缀指向的副本来减少内存使用量。 Here是基本思路。

标签: c# dictionary trie


【解决方案1】:
  1. 问题是您正在使用包含 26 个项目的子节点数组。其中大部分是空的。平均而言,基于 32 位或 64 位机器,每个节点将需要 26*4 或 26*8 字节。
  2. 您在构造函数中初始化子节点,这意味着,即使您的节点是叶节点,您仍然分配 26*BYTES,这完全没用。只有在需要存储子项时才分配数组。 TRIE 中的叶子节点不需要子数组。
  3. 要进一步减小大小,您可以简单地使用按位 Trie,它只需要两个节点,但是,它会增加计算时间并降低性能。 CPU 使用逐位 trie 来识别要执行的机器指令。
  4. 您可以使用字典而不是数组,它不会分配所有 26 个字母,如此答案 How to create a trie in c# 中所述。您还可以减少默认容量。

【讨论】:

    【解决方案2】:

    您可以做的一件事是将 TrieNode 制作成一个结构,然后避免在初始化后对其进行修改...但是您可能还想做一个内存转储并检查内存,因为它可能不会占用您那么多的空间想想...在任务管理器中报告的进程内存不是您的应用程序使用的内存,而是.NET 运行时为您的应用程序保留的内存保留

    【讨论】:

    • Struct 停留在 Stack,你会遇到 Stack Overflow。
    • 结构并不总是在堆栈上。
    【解决方案3】:

    在从大字典创建 trie 时,我遇到了完全相同的问题。所以我用这些词构建了一个DAWG(有向无环词图),它占用的空间非常小(甚至比我的字典还少),保持了与 trie 相同的性能,甚至可能更快。它的工作原理是识别单词中的常见后缀和前缀,并从中制作一个有限自动机。如果您的字典是静态的,您可以创建 DAWG 并将其保存到磁盘,然后您可以在应用程序中轻松加载它(它是使用整数数组实现的)。 Here 是一个实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-21
      • 1970-01-01
      • 2014-02-20
      • 2020-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-18
      相关资源
      最近更新 更多