在容易和正确的事之间,选择正确的事
案例
前段时间帮助ST学长做NLP的数据收集工作,有一个这样的需求:原始数据清洗, 分词之后,需要确认分词后的单词是否都在词库中。抽象提炼之后的表述是:数据库中有N个样本,现在需要判断M个未知样本是否全部位于数据库之中,列举出不在数据库中的单词。
我的解决方案(easy way)
收集N个样本组成一个列表List_N, 将M个未知样本组成列表List_M。遍历List_M,依次判断M个元素是否位于List_N中。
我的方案存在的问题
从理论上来说,我的方案能完成需求。该方案的时间复杂度是:O(N*M)。但实际情况是:词库太大太大太大了,记得这个数据库是Tencent实验室开源的中文词库,好几个G。当这个N超级之大时,导致两个问题,一是费空间,List_N长度大,占用内存高。二是费时,整个计算过程耗时非常之久。
正确的解决方案(right way)
记得当时过去半个小时了,ST学长来找我,问我统计完没有,我说还在计算中,ST学长很惊讶,为什么计算如此之久,我一通解释,弄清楚情况之后的ST学长对我说,快停下来,用字典或者集合来表示词库, 计算效率会高很多。当换成字典dict表示词库之后,整个计算过程1秒钟就完成,纵享丝滑。。。
究其原因,就是集合的底层是基于红黑树实现的,集合中查找元素的时间复杂度是O(logn), 尤其是这个N非常之大时,整个计算量和列表遍历相比就差了好几个量级。
经验教训
整个处理过程中我有一个巨大的思维惯性,就是默认用自己常用的数据结构List。平常处理文本都不是特别大,无论用那种数据结构就很快,因此就忽略了不同容器的差异,而这些差异在某些场景下会有天差地别的性能表现。用List是一件容易的事(easy way),但此时并不合适,因此每当自己处理问题时应该问一问自己有没有更好的解决方案(right way),求助他人也可,总之,做事情需要在一条正确的路上,即使多花点时间在寻找这条正确的路也值得。