IKAnalyzer源码分析---4

IKAnalyzer源码分析—歧义词

根据《IKAnalyzer源码分析—1》所示,IKSegmenter的next函数每次处理完一批数据后,会通过IKArbitrator的process函数进行歧义处理。

IKArbitrator::process

    void process(AnalyzeContext context , boolean useSmart){
        QuickSortSet orgLexemes = context.getOrgLexemes();
        Lexeme orgLexeme = orgLexemes.pollFirst();

        LexemePath crossPath = new LexemePath();
        while(orgLexeme != null){
            if(!crossPath.addCrossLexeme(orgLexeme)){
                if(crossPath.size() == 1 || !useSmart){
                    context.addLexemePath(crossPath);
                }else{
                    QuickSortSet.Cell headCell = crossPath.getHead();
                    LexemePath judgeResult = this.judge(headCell, crossPath.getPathLength());
                    context.addLexemePath(judgeResult);
                }

                crossPath = new LexemePath();
                crossPath.addCrossLexeme(orgLexeme);
            }
            orgLexeme = orgLexemes.pollFirst();
        }

        if(crossPath.size() == 1 || !useSmart){
            context.addLexemePath(crossPath);
        }else{
            QuickSortSet.Cell headCell = crossPath.getHead();
            LexemePath judgeResult = this.judge(headCell, crossPath.getPathLength());
            context.addLexemePath(judgeResult);
        }
    }

从AnalyzeContext中获取前面通过Segmenter分析出的词集合orgLexemes,然后遍历orgLexemes,将相交的词依次调用addCrossLexeme添加进crossPath中,如果遇到一个不相交的词,比方说AB、BCD、CD、DE、FG(这里假设的每个英文字符为中文字符),当遇到FG词时,addCrossLexeme返回false,此时,如果该crossPath中只有一个词Lexeme,或者没开启智能分词,则直接将当前LexemePath添加到AnalyzeContext中,否则通过judge函数处理当前LexemePath,并将结果添加到AnalyzeContext。while后的代码用来处理最后一个LexemePath,原理和前面一样。

IKArbitrator::process->judge

    private LexemePath judge(QuickSortSet.Cell lexemeCell , int fullTextLength){

        TreeSet<LexemePath> pathOptions = new TreeSet<LexemePath>();
        LexemePath option = new LexemePath();
        Stack<QuickSortSet.Cell> lexemeStack = this.forwardPath(lexemeCell , option);
        pathOptions.add(option.copy());

        QuickSortSet.Cell c = null;
        while(!lexemeStack.isEmpty()){
            c = lexemeStack.pop();
            this.backPath(c.getLexeme() , option);
            this.forwardPath(c , option);
            pathOptions.add(option.copy());
        }
        return pathOptions.first();

    }

judge首先通过forwardPath遍历LexemePath中的所有Lexeme,将不冲突的词存入option,将有冲突的词存入lexemeStack,然后遍历冲突的词的集合lexemeStack,首先调用backPath从option中移除与lexemeStack中最后一个Lexeme冲突的所有词,再通过forwardPath向前在option中添加与c中的Lexeme不冲突的词,然后将处理完的option添加到待分析的集合pathOptions中。
judge函数最后通过pathOptions的first函数返回最优的option。pathOptions是TreeSet,其内部会调用LexemePath的compareTo函数返回最优的LexemePath,后面来看该函数。

IKArbitrator::process->judge->forwardPath

    private Stack<QuickSortSet.Cell> forwardPath(QuickSortSet.Cell lexemeCell , LexemePath option){
        Stack<QuickSortSet.Cell> conflictStack = new Stack<QuickSortSet.Cell>();
        QuickSortSet.Cell c = lexemeCell;
        while(c != null && c.getLexeme() != null){
            if(!option.addNotCrossLexeme(c.getLexeme())){
                conflictStack.push(c);
            }
            c = c.getNext();
        }
        return conflictStack;
    }

forwardPath遍历一个LexemePath下的所有LexemePath,将不相交的部分通过addNotCrossLexeme函数添加到option中,将相交的部分添加到conflictStack中并返回。例如AB、BC、DE,则option中最终保存AB、DE,conflictStack最终保存BC。

IKArbitrator::process->judge->backPath

    private void backPath(Lexeme l  , LexemePath option){
        while(option.checkCross(l)){
            option.removeTail();
        }   
    }

backPath从后往前将与参数Lexeme冲突的词从option中移除。

下面回头重点看下LexemePath的compareTo函数,该函数表示IKAnalyzer歧义处理的准则。

LexemePath::compareTo

    public int compareTo(LexemePath o) {
        if(this.payloadLength > o.payloadLength){
            return -1;
        }else if(this.payloadLength < o.payloadLength){
            return 1;
        }else{
            if(this.size() < o.size()){
                return -1;
            }else if (this.size() > o.size()){
                return 1;
            }else{
                if(this.getPathLength() >  o.getPathLength()){
                    return -1;
                }else if(this.getPathLength() <  o.getPathLength()){
                    return 1;
                }else {
                    if(this.pathEnd > o.pathEnd){
                        return -1;
                    }else if(pathEnd < o.pathEnd){
                        return 1;
                    }else{
                        if(this.getXWeight() > o.getXWeight()){
                            return -1;
                        }else if(this.getXWeight() < o.getXWeight()){
                            return 1;
                        }else {
                            if(this.getPWeight() > o.getPWeight()){
                                return -1;
                            }else if(this.getPWeight() < o.getPWeight()){
                                return 1;
                            }   
                        }
                    }
                }
            }
        }
        return 0;
    }

payloadLength表示LexemePath中的所有Lexeme覆盖的长度,越大越好。
例如第一个LexemePath包含“中华人民”一个Lexeme,第二个LexemePath包含“华人”,则第一个LexemePath的payloadLength为4,第二个LexemePath的payloadLength为2,此时认为第一个LexemePath优于第二个LexemePath。

size函数表示LexemePath中的词元个数,越小越好。
例如第一个LexemePath包含“中华人民”一个Lexeme,第二个LexemePath包含“中华”、“人民”两个Lexeme,则第一个LexemePath的size为1,第二个LexemePath的size为2,此时认为第一个LexemePath优于第二个LexemePath。

pathEnd表示最后的结束位置,假设payloadLength和size都相等,pathEnd位置越靠后越好。
例如第一个LexemePath包含“中华人民”一个Lexeme,第二个LexemePath包含“华人民族”,则第一个LexemePath的pathEnd为4,第二个LexemePath的pathEnd为5,此时认为第一个LexemePath优于第二个LexemePath。

getXWeight返回所有词长的乘积,如下所示,

    int getXWeight(){
        int product = 1;
        Cell c = this.getHead();
        while( c != null && c.getLexeme() != null){
            product *= c.getLexeme().getLength();
            c = c.getNext();
        }
        return product;
    }

词长的乘积可以理解为面积,说明词长越平均,面积越大,因此越好。
例如假设payloadLength、size和pathEnd都相等,第一个LexemePath包含“中华”“人民”“族”,第二个LexemePath包含“中”“华”“人民族”,则第一个LexemePath的getXWeight为2*2*1=4,第二个LexemePath的getXWeight为1*1*3=3,此时认为第一个LexemePath优于第二个LexemePath。

getPWeight返回词元位置权重,如下所示,

    int getPWeight(){
        int pWeight = 0;
        int p = 0;
        Cell c = this.getHead();
        while( c != null && c.getLexeme() != null){
            p++;
            pWeight += p * c.getLexeme().getLength() ;
            c = c.getNext();
        }
        return pWeight;     
    }

getPWeight表示词元位置和词元长度的乘积,越大越好,通俗理解,就是越往后的词越长就越好。
例如假设payloadLength、size、pathEnd和getXWeight都相等,第一个LexemePath包含“中华”“人民”“族”,第二个LexemePath包含“中”“华人”“民族”,则第一个LexemePath的getPWeight为1*2+2*2+3*1=9,第二个LexemePath的getPWeight为1*1+2*2+3*2=10,此时认为第二个LexemePath优于第一个LexemePath。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值