可以对所有的行条使用相同的哈希哈数,但是对每个行条我们需要使用一个独立的桶数组(为什么要用独立的桶呢?其实也能用一个桶,如使用x[ri][hvalue]<vector>=(c1,c2,c3,cn), ),如果检测到r,c已经存在)。所以R/b个行条需要一个R/B X M', M'<=M(需要比较的文档数)。
使用独立的桶是为了后面可以将结果相交,或者相并。
例如:
12行的签名矩阵,分成4个行条,每个行条由3行组成。图中显示可见的行条1中的第2,4列包含的列向量相同,因此他们肯定会被哈希到行条1下的相同桶中。因此,不管这两列在其他3个行条下的结果如何,他们都是一个相似候选对。
在行条1中不相等的两个列仍然还有另外3次机会成为候选对。行条化策略能够使得相似列会比不相似列更有可能成为候选对。
按照如上描述,采用不同的上面的策略可以修改上文代码:
1. 修改getCandidate.需要包含vector和algorithm头文件
//used to get candidate const int B=2; //setN is bucket index vector<int> candidate[B][15]; vector<int> RL[setN]; //candidate[B][15],RL 需要采用可以采用堆栈结构进行排序,每次取最小值 int getSmallest(vector<int>& v) { vector<int>::iterator ite=v.begin(); int smallest = *ite; for(; ite!=v.end();ite++) { if(*ite<smallest) smallest = *ite; } v.erase(remove(v.begin(),v.end(),smallest)); return smallest; } bool isExist(vector<int>& v, int n) { vector<int>::iterator ite=v.begin(); for(; ite!=v.end();ite++) { if(*ite == n) return true; } return false; } void getCandidate(int R)//get candidate for LSH { int row = 0; if(R%B != 0) { return; } row = R/B; for(int i=0;i<B;i++) { for(int m=0;m<setN;m++) { int bucket_index=0; for(int r =2*i;r<(R/B+2*i);r++)//row(0,R/B=2),then 2,4, from 2*i, to R/B+2*i { bucket_index += h1(SIG[r][m]-1); } candidate[i][bucket_index].push_back(m); } for(int m=0;m<15;m++) { if(candidate[i][m].size()>1) { int smallest = getSmallest(candidate[i][m]); vector<int>::iterator ite = candidate[i][m].begin(); for(;ite!= candidate[i][m].end();ite++) { if(!isExist(RL[smallest],*ite)) //get smalleset RL[smallest].push_back(*ite); } } } } }
2. flag=true和false分别反映了采用行条化策略和不采用行条化策略的计算。
/used to get SIM template<int C,int map[][C]> void countForSIM(int R, bool flag=false)//flag used to control if it will use the LSH { reset(x); reset(xpy); for(int r=0;r<R;r++) { for(int m = 0;m<C;m++) { if(flag==true) { if(RL[m].size()==0) { continue; } vector<int>::iterator ite = RL[m].begin(); int j=*ite; for(;ite!= RL[m].end(); ite++) { if(map[r][m]==1 && map[r][j]==1) { x[m][j]++; xpy[m][j]++; } else if(map[r][m]==1 || map[r][j]==1) { xpy[m][j]++; } if(ite!=RL[m].end()) { j=*ite; } } } else { for(int j=m+1;j<C;j++) { if(map[r][m]==1 && map[r][j]==1) { x[m][j]++; xpy[m][j]++; } else if(map[r][m]==1 || map[r][j]==1) { xpy[m][j]++; } } } } } }
3. 测试代码如下:
int main() { cout<<"original set................................."<<endl; printM<setN,matrix>(5); initSIG(); //printM<setN,SIG>(2); inithash(); minHash(setN,4,5); cout<<"hash signature set..........................."<<endl; printM<setN,SIG>(4); countForSIM<setN,matrix>(5); getSIM1(); cout<<"SIM1........................................."<<endl; printMd<setN,SIM1>(setN); getCandidate(4); //cout<<"candidate........................................."<<endl; //printM<setN,candidate>(2); //cout<<"RL........................................."<<endl; //printM<setN,RL>(setN); countForSIM<setN,SIG>(4,true); getSIM2(); cout<<"SIM2........................................."<<endl; printMd<setN,SIM2>(setN); system("pause"); }
如果针对原来的SIM矩阵分析,那么阀值 = (1/2)^(1/2)==0.707, 即最后计算出的SIM是相似性超过70%的。
假定使用b个行条,每个行条由r行组成,并假定某对具体文档之间的Jaccard相似度为s。所以具体某行中两个签名相等的概率为S。
1. 在某个具体的行条中所有行的两个签名相等的概率是s^r。
2. 在某个具体行条中至少有一对签名不相等的概率是1-s^r
3.在任何行条中任意一行的签名对都不相等的概率=每个行条中都至少有一对签名不相等=(1-s^r)^b
4.签名至少在一个行条中全部相等的概率,也即成为候选对的概率为1- (1-s^r)^b
不论常数b,r的取值如何,函数1-(1-s^r)^b的图像为S-曲线(S-curve)。曲线中候选概率1/2处对应的相似度就是所谓的阀值(threshold),他是b和r的函数,近视估计为
(1/b)^(1/r)。
这个LSH技术是一个具体函数族(最小哈希函数族)上的应用例子,这些函数可以组合在一起,如这里的行条化技术来更有效的区分低距离和高距离的对。S-曲线的陡度能够反映从候选对中避免伪正例和伪反例的有效程度。
这里探讨的是除了最小哈希函数之外的其他函数族,他们也同样能够非常高效的产生候选对。他们能够作用于集合空间/Jaccard距离,或者其他类型的空间和距离测度。对于这些函数族来说,需要满足如下的3个条件。
1. 必须更可能选择近距离对而不是远距离对作为候选对
2. 函数之间必须在统计上相互独立,在这个意义上讲,两个或者多个函数的联合概率等于每个函数上独立事件的概率乘积。
3. 必须在以下两个方面拥有很高的效率。
a) 必须在短时间内识别候选对,该时间远低于扫描所有对所花费的时间。(最小哈希函数就具有这个能力,它将集合映射为最小哈希值的时间与数据的规模成正比,而不是与数据集合数目的平方成正比)。由于具有公共值的集合会映射到同一个桶中,因此单个哈希函数产生候选对的时间远远低于集合对的数目。
b) 必须组合在一起可以更好的避免伪正例和伪反例,组合后函数所花费的时间也必须远低于对的数目。
比如行条化技术可以组合一系列的单个哈希函数,每个哈希函数满足条件3(a),但是其本身并不符合我们所期望的S-曲线的性质,但是通过组合这些最小哈希函数,我们却可以得到S-曲线。
函数f会对两个输入项求哈希值,最后判定两个值是否相等。如果f(x)=f(y),来判定“x与y是候选项”,f(x)≠f(y),在没有其他函数来判定x,y时,可以判定他们不是候选对。这种形式的一系列函数集合构成了函数族(function family)。例如在哈希函数族中的每个函数都基于特征矩阵的一个可能的行排列转换而形成,各个行条即使采用一样的哈希函数,这个哈希函数族只包括一个哈希函数,可以让不同行条采用不同的哈希函数,这个哈希函数族便包含b个函数。
令d1<d2是定义在某个距离测度d下的两个距离值。如果一个函数族F中的每一个函数f都满足下列条件,则称其为(d1,d2,p1,p2)敏感函数族:
1. 如果d(x,y)≤d1, 那么f(x)=f(y)的概率至少是p1;
2.如果d(x,y)≥d1, 那么f(x)=f(y)的概率最大是p2;