use encoding "gbk";
#load score哈希,这个Score是自己定的
open(Inscore, "<$ARGV[0]") or die "无法打开信息文件。\n";
%score=();
while(<Inscore>)
{
chomp($_);
if($_ eq "")
{
next;
}
@pair=();
@pair=split("\t",$_);
$score{$pair[0]}->{one}=$pair[1]/$pair[4];#频率
}
close(Inscore);
print "load done......\n";
# 句子 $sentence
# $num当前字在句子中的下标,$n_max字符串最大长度
# @scoretag[$num][$n_max]:记录当前最大分值矩阵
# @father[$num][$n_max]:记录父节点矩阵
if($scoretag[$num][$my_j]==-1)
{
$scoretag[$num][$my_j]=0;
}$n_pinghua_str=0.02;$n_pinghua=0.01;#这个平滑系数,要根据具体问题重新设定
open(Insentence,"<$ARGV[1]") or die "无法打开待分词文件\n";
while(<Insentence>)
{
chomp($_);
@word=split //,$_;
$num=0;
@scoretag=();
@father=();
#初始化第一个字
for($i=0;$i<$n_max;$i++)
{
$scoretag[$num][$i]=0;
$father[$num][$i]=-1;
}
$scoretag[$num][0]=$n_pinghua;#以后为了做平滑,再说平滑系数
if(defined $score{$word[$num]})
{
$scoretag[$num][0]=$score{$word[$num]}->{one};
}
#从第二字开始,viterbi 生成网格
for($num=1;$num<@word;$num++)
{
#my_j=0 代表当前字,=1 为当前字与前一个字,每层有$n_max个选择
for($my_j=0;$my_j<$n_max and $my_j < $num+1 ;$my_j++)
{
#找到父节点所在层,计算最大值,记录父节点
$fatherlevel=$num - $my_j -1;#从$my_j 推算出,父节点应该在$num的哪一层。回退重要下标
$scoretag[$num][$my_j]=-1;
$father[$num][$my_j]=-1;
if($fatherlevel > -1)
{
for($father_j=0;$father_j <$n_max;$father_j++)
{
if($scoretag[$num][$my_j] < $scoretag[$fatherlevel][$father_j])#分数相同取短者
{
$scoretag[$num][$my_j]=$scoretag[$fatherlevel][$father_j];
$father[$num][$my_j]=$father_j;#回退重要下标
}
}
}
if($scoretag[$num][$my_i]==-1)$scoretag[$num][$my_i]=0;
#生成当前字符串
$keyword="";
for($sub=0;$sub<$my_j+1;$sub++)
{
$keyword=$word[$num-$sub].$keyword;
}
#得到当前字符串的发射概率,本算法中暂时没有"转移概率"
if(not defined $score{$keyword})
{
if($my_j ==0)#单字与字符串所给的频次不同,平滑
{
$nowscore_my_j=$n_pinghua;
}else{
$nowscore_my_j=$n_pinghua_str;
}
}
else{
$nowscore_my_j=$score{$keyword}->{one};
}
$scoretag[$num][$my_j]+=$nowscore_my_j;
#print "$my_j\n";
}
}
#回退
#先找到回退入口,即$num-1最后一个字的值,哪个值最大。
$maxfinal=0;
$maxfather=-1;
for($i=0;$i<$n_max;$i++)
{
if($maxfinal < $scoretag[$num-1][$i])
{
$maxfinal = $scoretag[$num-1][$i];
$maxfather=$i;
}
}
#开始回退
$i=$num-1;
$output="";
while($i>-1)
{
$maxnextfather=$father[$i][$maxfather];
for($j=0;$j<$maxfather+1 and $i>-1;$j++)
{
$output=$word[$i].$output;
$i--;
}
if($i >-1)
{
$output=" ".$output;
}
$maxfather=$maxnextfather;
}
print "$output\n";
}
close(Insentence);
#简单的viterbi分词代码,score即要用到的概率、语言模型、自己想什么作为分数就初始化score为什么;
#perl编程,简单版本,分数相加,(暂时没用转移概率矩阵)