现在的手机早已经实现垃圾短信过滤和骚扰电话拦截功能,今天总结一下到底是用什么样的数据结构和算法实现这样的功能。
基于黑名单的过滤器
我们可以在手机上维护一个骚扰电话号码和垃圾短信发送号码的黑名单。每次收到短信或者接到来电的时候,都去黑名单进行电话号码匹配,以便识别骚扰电话和垃圾短信。
那么,用什么样的数据结构存储黑名单的电话号码呢。如果数据量少的话,可以用散列表,二叉树,跳表等动态数据结构存储数据。举例:每个号码当做一个字符串,平均长度为 16 个字节,那存储 50 万个电话号码,大约需要 10MB 的内存空间。这样的消耗,对于内存有限的手机来说,也是可以接受的。
如果号码有 500 万个,那么大约需要 100MB 的存储空间,这就比较浪费空间了,接下来继续优化。
优化一:利用上一篇总结的布隆过滤器来实现,就比较节省空间了。存储 500 万个电话号码,将位图设置为 5000 万,那就是需要 5000 万个二进制位,换算成字节,不到 7MB 的存储空间。这样的方式,就相当节省内存空间了。
优化二:采用时间换空间的思路。我们将黑名单的数据维护在云端。那么每次验证的时候只需把电话号码发往云端,交给服务器验证,手机端只负责接受结果,完全不再占用手机的内存空间。但是,这种方式也有自己的弊端。首先必须要在联网的状态下才能进行电话号码验证。其次网络通信需要耗费时间,如果碰上网络延迟,那么处理速度就会更慢。
基于规则的过滤器
我们自己设定验证垃圾短信的规则,然后通过规则的匹配,确定该短信是否是垃圾短信。
举例规则:
1:短信包含非法词汇。比如淫秽,反动等等词汇。
2:短信发送号码为非正常手机号码。比如:+3766378
3:短信中包含链接,手机号,微信号等。
4:符合已知的垃圾短信模版。垃圾短信通常会群发,我们可以从这些垃圾短信抽象成模版,用这个模版去验证其他短信是否为垃圾短信。
如何判断短信是垃圾短信呢?如果单纯的判断短信只要符合上面的一条规则就是垃圾短信,太过武断。所以,我们将判断的依据设置为满足上述条件的任意两条或者是三条。另外,我们可以给每一条规则设置权重分数。用接受到的短信与规则匹配,计算出得分,设置分值的阈值,满足一定的得分就判定为垃圾短信。
上述思路简单,但是实现起来的细节,却比较复杂。比如:如何定义非法词汇。
非法词汇的定义,单凭人脑去总结,太不现实。所以,我们需要借助统计,计算出哪些词汇是非法词汇。统计的前提是,我们有足够的样本来做分析。
假如我们的样本是 1000 万条短信。对这些短信内容进行分词处理,计算出某个词汇在垃圾短信中出现的频率,以及在正常短信中出现的频率。如果该词在垃圾短信中出现的频率元大于在正常短信中出现的频率,那就判定该词为非法词汇,当做判定垃圾短信的参照。
基于概率统计的过滤器
基于规则的垃圾短信过滤很直观,容易理解,但是也有自己的局限性。一:规则的设置容易受到设置人的思维局限,可能过于简单。二:垃圾短信发送者会针对这些规则改变短信结构,绕开你的验证规则。基于上述两个局限性,下面总结一种更加高级的过滤方式,基于概率统计的过滤方式。
举例:记录事件 A 是“小明不去上学”,事件 B 是“下雨了”。
在 10 天当中小明有 3 天没去上学,P(A) = 3/10。
在 10 天当中有 4 天下雨,P(B) = 4/10。
4 天下雨天中,小明有 2 天没有上学,所以下雨天不上学的概率是:P(A|B) = 2/4。
3 天没有上学中,有 2 天下雨,所以不上学中下雨天的概率是:P(B|A) = 2/3。
上述四个概率值的关系如下:P(A|B) =( P(B|A) * P(A) ) / P(B),这个关系就是朴素贝叶斯算法。了解了朴素贝叶斯算法,那么就将其用到垃圾短信过滤中。
对于垃圾短信的认定,我们抽象出一些特征项,以便计算机能理解其为垃圾短信。通过对短信分成 n 个单词,构造出某条短信的特征项,这 n 个单词就代表这个短信。区别于规则过滤方法(短信要么是垃圾短信要么是正常短信),我们用概率的大小来表示一天短信是垃圾短信的可信度。用公式表示这个概率如下:
P(短信是垃圾短信|W1,W2,W3...Wn同时出现在一条短信中)
概率公式写出来了,但是实际情况中,我们无法准确计算出 P(W1,W2,W3…Wn同时出现在一条短信中) 的概率。因为,首先样本数据肯定不够大,其次即便样本数据够大,但是样本中也不会出现太多同时包含 W1,W2,W3…Wn 这些单词的短信。所以,样本不满足,那么我们就没法通过推理公式实际操作了。
这个时候,就用到了贝叶斯算法。首先,我们用贝叶斯算法分解概率公式如下:
P(短信是垃圾短信|W1,W2,W3...Wn同时出现在一条短信中)=
(P(W1,W2,W3...Wn同时出现在一条短信中/短信是垃圾短信) * P(短信是垃圾短信))/P(W1,W2,W3...Wn同时出现在一条短信中)
P(W1,W2,W3…Wn同时出现在一条短信中/短信是垃圾短信) 这个概率的值我们一样无法通过样本计算。但是我们可以基于概率规则来计算:P(A*B) = P(A)*P(B).
基于上述规则,我们可以把 P(W1,W2,W3…Wn同时出现在一条短信中/短信是垃圾短信) 的计算分解成下面的样子:
P(W1,W2,W3...Wn同时出现在一条短信中/短信是垃圾短信) =
P(W1同时出现在一条短信中/短信是垃圾短信) *
P(W2同时出现在一条短信中/短信是垃圾短信) *
P(W3同时出现在一条短信中/短信是垃圾短信) *
...
P(Wn同时出现在一条短信中/短信是垃圾短信)
P(短信是垃圾短信) 这个概率我们可以求出来。
P(W1,W2,W3…Wn同时出现在一条短信中) 这个概率不好通过样本获取,其实我们根本不用计算这个概率。
通过朴素贝叶斯算法可以判断一条短信是垃圾短信的概率,当我们无法计算出这个概率的时候,我们换一种方式进行垃圾短信概率的推测。假设某条短信通过贝叶斯算法得出的概率值为 P1,另外一条得出的概率位 P2,如果 P2 是 P1的十倍,那么我们是不是更有理由相信 P2 是垃圾短信的可能性更好。基于这个思路,我们通过对比朴素贝叶斯求出来的值,来判定某条短信是垃圾短信的概率。把 P1 与 P2 的表达式相除,P(W1,W2,W3…Wn同时出现在一条短信中)这个因子会互相抵消,所以我们就不需要计算该值,也能判断垃圾短信的概率了。
总结
黑名单过滤方法中,用到了布隆过滤器,但是布隆过滤器会有误判的情况。为了减少误判带来的错误估计,我们可以采用上述三种方法一同验证骚扰电话和垃圾短信,降低误判带来的风险。
本文创作灵感来源于 极客时间 王争老师的《数据结构与算法之美》课程,通过课后反思以及借鉴各位学友的发言总结,现整理出自己的知识架构,以便日后温故知新,查漏补缺。
初入算法学习,必是步履蹒跚,一路磕磕绊绊跌跌撞撞。看不懂别慌,也别忙着总结,先读五遍文章先,无他,唯手熟尔~
与诸君共勉