【问题标题】:Erlang: What is most-wrong with this trie implementation?Erlang:这个 trie 实现最错误的是什么?
【发布时间】:2011-01-04 09:15:14
【问题描述】:

在假期里,我的家人喜欢玩 Boggle。问题是,我在 Boggle 上很糟糕。所以我做了任何优秀程序员都会做的事:写一个程序来为我玩。

算法的核心是一个简单的prefix trie,其中每个节点都是一个dict,对下一个字母的引用。

这是trie:add 的实现:

添加([],特里)->
    dict:store(stop, true, Trie);

添加([Ch|Rest],特里)->
    % setdefault(键,默认值,字典)->
    % case dict:find(Key, Dict) of
    % { ok, Val } -> { 字典, Val }
    % 错误 -> { dict:new(), Default }
    %     结尾。
    { NewTrie, SubTrie } = setdefault(Ch, dict:new(), Trie),
    NewSubTrie = add(Rest, SubTrie),
    dict:store(Ch, NewSubTrie, NewTrie).

您可以在此处查看其余部分以及如何使用它的示例(在底部):

http://gist.github.com/263513

现在,这是我在 Erlang 中的第一个严肃程序,我知道它可能有很多问题……但我最关心的是它使用 800 兆字节的 RAM

那么,我做错了什么?我怎样才能让它少一点错误?

【问题讨论】:

  • 哈。几年前我在 PHP 中做过。
  • 你输入的单词列表有多大?
  • 我的单词列表是 200,000 个单词(或 2.5 兆)。

标签: erlang trie


【解决方案1】:

您可以通过简单地将单词存储在 ets 表中来实现此功能:

% create table; add words
> ets:new(words, [named_table, set]).
> ets:insert(words, [{"zed"}]).  
> ets:insert(words, [{"zebra"}]).    

% check if word exists
> ets:lookup(words, "zed").          
[{"zed"}]

% check if "ze" has a continuation among the words
78> ets:match(words, {"ze" ++ '$1'}).
[["d"],["bra"]]

如果 trie 是必须的,但您可以使用非功能性方法,那么您可以尝试 digraphs,正如 Paul 已经建议的那样。

如果您想保持功能,您可以通过使用内存较少的结构(例如proplists)或记录(例如-record(node, {a,b,....,x,y,z}))来节省一些内存字节。

【讨论】:

  • 好吧,所以我一直在修改 ets,但我遇到了“错误论点”的问题。也许你知道一个简单的解决方案?问题在这里:stackoverflow.com/questions/1964990/…
  • 好吧,我也修改了 proplist 的实现......它遇到了一个导致 shell 挂起的问题。我在这里问过这个问题:stackoverflow.com/questions/1982257/…(ps:感谢您的所有帮助 - 非常感谢)。
【解决方案2】:

我不记得 dict 需要多少内存,但让我们估计一下。你有 2.5e6 个字符和 2e5 个单词。如果您的 trie 根本没有共享,那么字典中将需要 2.7e6 个关联(每个字符和每个“停止”符号一个)。一个简单的纯功能 dict 表示每个关联可能有 4 个单词——它可能会更少,但我试图获得一个上限。在 64 位机器上,这需要 8*4*270 万字节,即 86 兆字节。这只是你 800M 的十分之一,所以这里肯定有问题。

更新: dict.erl 表示带有哈希表的字典;当你有很多非常小的字典时,这意味着很多开销,就像你一样。我会尝试更改您的代码以使用 proplists 模块,它应该与我上面的计算相匹配。

【讨论】:

  • 要检查 dict() 构造占用多少内存,请调用:erts_debug:flat_size(dict:new())。它返回 46 个字,在 32 位系统上为 184 字节,在 64 位系统上为 368 字节。
  • 感谢您的建议...虽然我遇到了一个奇怪的问题,即当我创建基于 proplist 的特里时,shell 挂起,我在这里问过这个问题:stackoverflow.com/questions/1982257/… - 可以您是否有机会提供任何见解?
【解决方案3】:

解决问题的另一种方法是查看单词列表,看看是否可以从骰子中构造单词。这样你只需要很少的 RAM,而且编码可能更有趣。 (优化和并发)

【讨论】:

    【解决方案4】:

    查看DAWGs。它们比尝试更紧凑。

    【讨论】:

      【解决方案5】:

      我不知道你的算法,但如果你要存储这么多数据,也许你应该考虑使用 Erlang 的内置有向图库来表示你的 trie,而不是这么多的字典。 http://www.erlang.org/doc/man/digraph.html

      【讨论】:

      • 酷 - 我会调查的。谢谢。
      【解决方案6】:

      如果所有单词都是英文,大小写无关,所有字符都可以用 1 到 26 的数字编码(实际上,在 Erlang 中它们从 97 到 122 的数字),保留 0 表示停止。所以你也可以使用array module

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-04
        • 1970-01-01
        • 2017-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-09
        • 1970-01-01
        相关资源
        最近更新 更多