拼写纠正

一直以来用google reader订阅了大量的东西,加星了很多,但有些没怎么认真看过,这几天翻了翻以前的加星,发现一篇讲拼写纠正的文章讲的非常犀利,就像google里那样能够快速准确的纠正拼写。而且作者用python写的代码,只用了21行就完成了。

http://norvig.com/spell-correct.html 原文章在这里

具体的东西可以去看英文原文,这里简单描述下犀利的思想。

我们纠正单词的目的就是希望这样一个条件概率达到最大值P(我们猜测用户想输的单词|用户数的单词),于是我们有
P(我们猜测用户想输的单词|用户数的单词)=P(我们猜测用户想输的单词∩用户数的单词)/P(用户数的单词)
由贝叶斯公式变形可得=P(用户数的单词|我们猜测用户想输的单词)*P(我们猜测用户想输的单词)/P(用户数的单词)
注意到P(用户数的单词)是一定的(因为用户已经输入了嘛)
最后=P(用户数的单词|我们猜测用户想输的单词)*P(我们猜测用户想输的单词)

为什么要这样变化呢,注意到原问题的概率我们很难入手,而转变问题后,问题变得容易处理。

P(我们猜测用户想输的单词)就可以认为是字典里单词出现的频率,而P(用户数的单词|我们猜测用户想输的单词)这一项我们认为对于和原单词的编辑距离(通过增删改或者改变相邻字母的顺序的最少步骤把一个单词变为另一个叫编辑距离)越小的概率越大,为了简化问题作者只考虑了编辑距离是1和2的,因为这已经覆盖了绝大部分的错误。

最后做了个主观臆断,就是说如果用户输的是正确的单词就认为用户没有出错,如果可以经过编辑距离1的变化成正确单词,那么从中找一个能够变成的,正确的,在字典里出现频率最高的单词,如果找不到距离为1的,再找距离为2的,再找不到就认为用户输入了正确单词(比如可能是专有名词或人名等而在字典里找不到)。

就这样通过简短的代码,利用了贝叶斯定理神奇的解决了这个问题。

然后学习了别人的写法自己写了个php版本的,网址是http://spell.isnowfy.com/

  1. <?php
  2. class Corrector{
  3.      private static $NWORDS;
  4.     static function words($text){
  5.          $matches = preg_match_all("/[a-z]+/", strtolower($text), $output);
  6.          return $output[0];
  7.         }
  8.     static function train($text){
  9.          foreach($text as $word)
  10.          @$model[$word] += 1;
  11.          return $model;
  12.         }
  13.     static function read(){
  14.          if(!file_exists('serialized_dictionary.txt')){
  15.              $NWORDS = train(words(file_get_contents("big.txt")));
  16.              $fp = fopen("serialized_dictionary.txt", "w+");
  17.              fwrite($fp, serialize($NWORDS));
  18.              fclose($fp);
  19.              }else
  20.              $NWORDS = unserialize(file_get_contents("serialized_dictionary.txt"));
  21.          return $NWORDS;
  22.         }
  23.     static function edits1($word){
  24.          $alphabet = 'abcdefghijklmnopqrstuvwxyz';
  25.          $alphabet = str_split($alphabet);
  26.          $n = strlen($word);
  27.          $edits = array();
  28.          for($i = 0;$i < $n;$i++){
  29.              $edits[] = substr($word, 0, $i) . substr($word, $i + 1); //delete
  30.              if($i < $n-1)
  31.                  $edits[] = substr($word, 0, $i) . $word[$i + 1] . $word[$i] . substr($word, $i + 2); //transposes
  32.              foreach($alphabet as $c){
  33.                  $edits[] = substr($word, 0, $i) . $c . substr($word, $i + 1); //replaces
  34.                  $edits[] = substr($word, 0, $i) . $c . substr($word, $i); //insert
  35.                  }
  36.              }
  37.          foreach($alphabet as $c)
  38.          $edits[] = substr($word, 0, $n) . $c; //insert n+1
  39.          return $edits;
  40.         }
  41.     static function known($words){
  42.          $known = array();
  43.          foreach($words as $w)
  44.          if(array_key_exists($w, self :: $NWORDS))
  45.              $known[] = $w;
  46.          return $known;
  47.         }
  48.     static function known_edits2($word){
  49.          $known = array();
  50.          foreach(self :: edits1($word) as $e1)
  51.          foreach(self :: edits1($e1) as $e2)
  52.          if(array_key_exists($e2, self :: $NWORDS))
  53.              $known[] = $e2;
  54.          return $known;
  55.         }
  56.     static function getmax($words){
  57.          $max = 0;
  58.         $ret = "";
  59.          foreach($words as $w){
  60.              if(($temp = self :: $NWORDS[$w]) > $max){
  61.                  $max = $temp;
  62.                  $ret = $w;
  63.                  }
  64.              }
  65.          return $ret;
  66.         }
  67.     static function correct($word){
  68.          if(empty(self :: $NWORDS))
  69.              self :: $NWORDS = self :: read();
  70.          $word = strtolower(trim($word));
  71.          if(empty($word))
  72.              return;
  73.          if(self :: known(array($word)))
  74.              return $word;
  75.          elseif(($temp = self :: known(self :: edits1($word))))
  76.              return self :: getmax($temp);
  77.          elseif(($temp = self :: known_edits2($word)))
  78.              return self :: getmax($temp);
  79.          else
  80.              return $word;
  81.         }
  82.     }
  83. ?>
您可能喜欢:
我猜您可能还喜欢:
, , , ,

《拼写纠正》有 8 条评论

  1. map4567 | #1

    google reader是什么?

  2. 很棒的blog orz
    贝叶斯原理的应用 如果记录用户后一次的输入用作以后的纠错可不可行

  3. aeroz | #3

    serialized_dictionary.txt和big.txt能否发邮箱,谢谢!

Trackbacks/Pingbacks:

  1. Thought this was cool: md5到md5破解的一些科普 « CWYAlpha
  2. 字符编码和中文乱码小叙 | 吃杂烩

发表评论