我有一个做了一款移动浏览器的朋友。
他有这样一个需求:当用户输入一个网站的url时候,移动浏览器需要识别这个网址是否是一个恶意网址。另外,他有一个恶意网址库。
也许这样的解决方法有多种。
其中一种就是把恶意网址库放在本地,移动浏览器拿到一个网址的时候就把它与网址库中的每个地址匹配一下,根据匹配与否来判断网址的是否为一个恶意地址。
哦,我忘了补充的情况就是这个网址库中有150万条数据,压缩后23M,如果一个浏览器为了识别恶意网址这么一个功能而附加这么大的库,你会没有用户的。
我刚开始给出的解决方法是bloom filter(bloom过滤器)。关于它的详细机理,吴军先生的《数学之美》中当有提及,我这里只给出一些参数值:数组大小是1500000 * 20 / 8 B(即bitset大小是数据项的20倍),hash function数目为13,误差率为万分之一。我用C++和Java分别实现了这个算法,测试后效果令人满意。数组大小只有4M多,再用zip压缩后大小只有2.8M。4G时代移动浏览器附带一个3M大小的库,个人以为是可以让人接受的。
事情到此为止本该就此结束。朋友又有一个需求:当用户输入一个网址的前面一部分数据库的时候,浏览器要给出相关的最多十个相关网址。
这个网址库当然就更大了,而且又要不断地更新,意味着不能放在本地。但是,每个人浏览的网站一般不会超过一百个吧,刚开始这个库可以为零,随着用户使用次数增多,统计一下缓存在本地就okay啦,这个不需要去服务器拉一大堆网址库下来。再说,真要是匹配不到也无所谓啦。
我想到的算法是trie树。自己实现一个trie树当然是很蠢笨的事情,我去网上搜罗了一番,在stackoverflow上得到一个提示:dawgdic。它也自称是最优秀的trie树,查找速度最快,而且声称的字典库相对来说比二维数组实现的trie树还要节省空间。我在code.google.com上下载完代码后(最新代码是dawgdic-0.4.5.tar.gz,2011年),把它的example看了一遍,有如下功能:
1 根据排列有序的数据,它可以构建出一个非常节省空间的dawg dictionary;
2 它的dawg词典库的每一项可以只有一个key,也可以附带插入其value,即每个数据项是一个key-value对;
3 根据构建好的词典它可以进行kv查询,即给出一个key,返回其value;
4 如果只能给出key的一段前缀,它可以返回所有共同前缀的key,这些结果可以按照字母顺序排列后返回也可以按照value的大小排序后返回;
5 如果只能给出key的一段后缀,它可以返回所有共同后缀的key,这些结果可以按照字母顺序排列后返回也可以按照value的大小排序后返回。
根据以上特性,上面那个需求就稀里哗啦地解决了(^_^)。我们需要利用的特性是1、2和4。dawg字典的key当然是网址的url,其权值当然是浏览次数。由于dawg词典构建好了以后,不能进行modify,而用户对每个网址每一段时间内的浏览次数是变化的,这就需要没过一段时间内对这个dawg dictionary进行重新构建。
其实上面只是简单地分别列举了两个算法的各自应用场景,其实这两个算法的应用范围非常广。如bloom filter就不说了,dawg树就可以用在搜索中的热搜索提示、一些英汉词典的词语搜索和输入法的个性化提示等。
晚上吃完饭,写出此记,对自己最近一段时间的业余研究做一番总结,接着加班。
附带声明:不经本人允许,诸如推酷“www.tuicool.com”这种垃圾抄袭网站不得转载本人的blog。