在假期,我的家人喜欢玩Boggle。问题是,我在Boggle很糟糕。所以我做了任何优秀的程序员会做的事情:编写了一个程序来为我效力。
算法的核心是一个简单的prefix trie,其中每个节点都是dict
对下一个字母的引用。
这是trie:add
实施:
add([], Trie) -> dict:store(stop, true, Trie); add([Ch|Rest], Trie) -> % setdefault(Key, Default, Dict) -> % case dict:find(Key, Dict) of % { ok, Val } -> { Dict, Val } % error -> { dict:new(), Default } % end. { NewTrie, SubTrie } = setdefault(Ch, dict:new(), Trie), NewSubTrie = add(Rest, SubTrie), dict:store(Ch, NewSubTrie, NewTrie).
你可以看到其余部分,以及它的使用方法(在底部),这里:
现在,这是我在Erlang中的第一个认真的程序,我知道它可能有一些问题...但我最关心的是它使用 800兆字节的RAM 。
那么,我做错了什么?我怎么能让它少一点错呢?
答案 0 :(得分:4)
您可以通过简单地将单词存储在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是必须的,但你可以使用非功能性的方法,那么你可以尝试digraph,就像Paul已经建议的那样。
如果要保持功能,可以使用内存较少的结构(例如proplist)或记录(例如-record(node, {a,b,....,x,y,z})
)来节省一些内存字节。
答案 1 :(得分:4)
我不记得dict占用了多少内存,但让我们估计一下。你有2.5e6个字符和2e5个单词。如果你的trie根本没有共享,那将需要2.7e6个关联(每个字符一个,每个'stop'符号)。一个简单的纯功能dict表示可能每个关联4个单词 - 它可能更少,但我试图得到一个上限。在64位计算机上,这需要8 * 4 * 270万字节,或86兆字节。这只是你的800M的十分之一,所以这里肯定是错的。
更新: dict.erl代表带有哈希表的dicts;当你有很多非常小的词汇时,这意味着很多开销。我尝试更改您的代码以使用proplists模块,该模块应与我上面的计算相匹配。
答案 2 :(得分:2)
解决问题的另一种方法是通过单词列表查看单词是否可以从骰子构建。这样你就需要很少的内存,编码可能会更有趣。 (优化和并发)
答案 3 :(得分:2)
查看DAWG。它们比尝试更紧凑。
答案 4 :(得分:1)
我不知道你的算法,但是如果你要存储那么多数据,也许你应该考虑使用Erlang的内置有向图库代表你的trie,而不是那么多的dicts。 http://www.erlang.org/doc/man/digraph.html
答案 5 :(得分:1)
如果所有单词都是英文,并且大小写无关紧要,则所有字符都可以用1到26之间的数字编码(事实上,在Erlang中, 数字从97到122 ),保留0表示停止。所以你也可以使用array
module。