网页去重问题

来源:互联网 发布:seo公式 编辑:程序博客网 时间:2024/04/19 18:53

问题:

假设有10亿网页已经被我们存下来,并提供如下信息:网页全文(即网页的源码)、全文长度、网页正文(即网页中提取的主体文字)、正文长度,以及其他网页提取物等,现在希望去掉其中的重复网页,请提出可行的方案,计算出每个网页对应的重复度,你可以自己对网页重复下定义,也可以提出需要哪些更多的网页提取物来实现更好的去重复方案。


我的思路:

1. 根据网页正文做hash表。

a. 网页量比较大,不能全部出入内存比较,可以通过hash表来存储网页元素的链表,hash值根据网页正文计算,每个网页元素包含一个重复次数、指向网页文件的地址以及网页的长度和正文长度;

b. 将网页读入时,根据网页全文得到hash值,与该值对应链表中的网页元素比较全文长度和正文长度,如果都一样则认定为重复网页,为重复次数加一,并删除这个网页文件;

c. 全部文件经过处理后,只剩下非重复的网页文件,在每个网页元素中都包含了该网页重复的次数。


    准确性分析,根据网页正文计算hash值能够将网页的内容类似的文件放到一起,如果这其中的全文长度和正文长度都一致,那么是重复网页的可能性就比较大。但是这比较依赖hash值是怎么计算出来的,如果内容上的变化能导致hash值的巨大变化,那么重复判断失误的概率就会减小。我想到的一个方法是,统计正文中出现次数最多的3-5个关键字,按照关键字的hash值得到正文的hash值。其实这个hash方法也有问题,如果关键字是比较大众的那种单词,那hash值就没啥意义了,而排除大众词汇又是好大的开销呀。

    性能分析,上面这个方法中,性能的瓶颈就在hash值的计算上,“统计出现次数最多的几个关键字”,用hash+最小堆做的话这个步骤是O(n*m)的复杂度(n是文件的单词个数,m是不重复的单词数),对每个文件都要做一次,这样的复杂度就相当大了,O(N*n*m),总体的复杂度就是O(N*n*m+N*N/s),其中s表示hash表大小,N*N/s表示比较操作的时间复杂度。改进性能的话就要简化hash值计算方法,改不好就可能降低正确性,你有更好的方法吗?


2. 根据正文长度和全文长度做hash表

a. 与上面的方式基本相同,我们也可以用两个长度的计算值做hash值,比如两个长度的加权和、加权乘等等,这里最好使得某一个长度的改变能够剧烈影响hash值的算法,而且两个长度对hash值的影响应该有一定差距,这样减小两组不同长度得到相同的hash值的概率;

b. 在网页元素中我们存储网页的地址、重复次数就够了,在链表中比对网页元素时,可以直接把另个文件的内容放到内存里从头比较,如果发现不同则不是重复网页,可以删除后面这个网页文件,否则为重复计数器加一;

c. 全部文件处理后,只剩下非重复的网页文件,在每个网页元素中都包含了该网页重复的次数。


    正确性分析,这个做法避免了第一个方法中hash计算方法带来的危险,因为在比对网页元素时直接对比文件内容,所以重复判断失误率非常小。

    性能分析,这种做法的性能瓶颈在对比网页元素上,每次对比的复杂度是O(n)(n是文件的单词个数),但是这种比对的次数有限,每个文件只与hash值相同的文件比对,即O(N*n*N/s),其中N是文件数、n是文件单词数、s是hash表的大小,总的复杂度是O(N*1+N*n*N/s)。


    两种方案的比较,准确性而言,第二种更好,效率上看,都基本是N^2级别,当大文件比较多的时候,后一种的效率下降明显,小文件多的时候第一种的效率则更好。这里其实忽略了一个问题,就是读文件带来的消耗,第一个方法中仅在计算hash值的时候读了一次文件,而第二个方法中每个文件要被读取平均(1+N/2s)次,这会导致第二种方法执行起来性能极差。


欢迎踊跃讨论和拍砖!!

原创粉丝点击