/// <summary> /// 2つのハッシュ値の距離を計算する。 /// </summary> /// <param name="a">ハッシュ値</param> /// <param name="b">ハッシュ値</param> /// <returns>ハッシュ値の距離</returns> public static int MeasureDistance(WordHash a, WordHash b) { var ab1 = a.val1 & b.val1; var ab2 = a.val2 & b.val2; if (ab1 == 0 && ab2 == 0) { return int.MaxValue; } else { return Math.Max( countBits(a.val1 - ab1) + countBits(a.val2 - ab2), countBits(b.val1 - ab1) + countBits(b.val2 - ab2) ); } }
/// <summary> /// 正しい単語リストの中から、指定した文字列に近い単語を取得する。 /// 専用のハッシュ値を使って単語を絞ってから、レーベンシュタイン距離を使って処理する。 /// </summary> /// <param name="word">文字列</param> /// <param name="candidatesCount">絞る単語の個数の最小値</param> /// <returns></returns> public ProofreaderResult Proofread(string word, int candidatesCount = 5) { // 元から正しい単語ならそのまま返す if (hashes.ContainsKey(word)) { return new ProofreaderResult(word, new List<string> { word }); } // 辞書の各単語とのハッシュ距離を計算する var hash = new WordHash(word); var allWords = hashes.Keys.ToList(); // うーん var hashDists = new int[allWords.Count]; for (var i = 0; i < allWords.Count; ++i) { hashDists[i] = WordHash.MeasureDistance(hash, hashes[allWords[i]]); } var candidates = new List<string>(); for (var dist = 0; candidates.Count < candidatesCount; ++dist) { for (var i = 0; i < allWords.Count; ++i) { if (hashDists[i] == dist) { candidates.Add(allWords[i]); } } } #region debug print if (DebugPrint) { var distList = Enumerable.Range(0, allWords.Count) .Select(i => Tuple.Create(allWords[i], hashDists[i])) .ToList(); distList.Sort((a, b) => a.Item2.CompareTo(b.Item2)); Console.Error.WriteLine("word: {0}", word); foreach (var distPair in distList) { Console.Error.WriteLine("{0}\t{1}", distPair.Item2, distPair.Item1); } } #endregion // 候補の中でレーベンシュタイン距離が最も近いものを返す var min = int.MaxValue; var res = new List<string>(); foreach (var cw in candidates) { var dist = MeasureLevenshtein(word, cw); if (dist < min) { min = dist; res = new List<string> { cw }; } else if (dist == min) { res.Add(cw); } } return new ProofreaderResult(word, res); }