【実務・中級編】【レーベンシュタイン距離】VBScript による文字列類似度算出アルゴリズムの実装と表記揺れデータの自動補正 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

序論:なぜ今、VBScriptで「編集距離」を極めるのか

業務自動化の最前線に立つ諸君なら、一度は「データの汚さ」に絶望したことがあるはずだ。「株式会社ABC」と「(株)ABC」、「プロジェクトX」と「プロジェクトX」。これら表記揺れの波に、単純な文字列比較(`A = B`)で挑むのは、あまりに無謀と言わざるを得ない。

昨今のAIブームで、Pythonのライブラリを使えば解決すると安易に考える者が増えた。しかし、我々プロフェッショナルが対峙するのは、Python環境すら構築を許されない制約の厳しいエンタープライズの現場だ。そこで、Windows標準環境のみで動作し、ExcelやVBA、WSH(Windows Script Host)から即座に呼び出せる「レーベンシュタイン距離」の実装こそが、現場を支配する最強の武器となる。

今回は、VBScriptにおける文字列類似度算出の極致を伝授する。単なるアルゴリズムの紹介ではない。メモリ効率、型変換のオーバーヘッド、そして実務に耐えうる堅牢な設計手法をその身に刻んでほしい。

1. レーベンシュタイン距離(編集距離)の本質

レーベンシュタイン距離とは、一つの文字列を別の文字列に変形させるために必要な「挿入」「削除」「置換」の最小回数を指す。

  • 挿入 (Insertion): 文字を追加する
  • 削除 (Deletion): 文字を消す
  • 置換 (Substitution): 文字を書き換える

この回数が少なければ少ないほど、二つの文字列は「似ている」と判断できる。

なぜ再帰(Recursion)を使ってはいけないのか

初心者はよく、数学的定義をそのまま再帰関数で実装しようとする。しかし、VBScriptにおける再帰はスタックオーバーフローのリスクが高く、何より遅い。文字列が長くなるにつれ、計算量は指数関数的に増大する。
我々が採用すべきは、動的計画法(Dynamic Programming)による反復処理だ。2次元配列(行列)を利用し、計算結果をメモリにキャッシュしながら進めるのが、チーフアーキテクトの定石である。

2. 実装:プロフェッショナルが綴る「Levenshtein」

以下に、実務での再利用性を極限まで高めたプロダクションコードを示す。このコードには、VBScript特有の「配列の添え字管理」と「Variant型のオーバーヘッド回避」の知見が凝縮されている。

Option Explicit

‘ ===========================================================================
‘ 関数名: GetLevenshteinDistance
‘ 概要: 2つの文字列間の編集距離(レーベンシュタイン距離)を算出する
‘ 引数: str1, str2 – 比較対象の文字列
‘ 戻り値: 編集距離(Long)
‘ ===========================================================================
Function GetLevenshteinDistance(ByVal str1, ByVal str2)
Dim n, m, i, j, cost
Dim matrix()

‘ NULLチェックおよび空文字処理
str1 = CStr(str1 & “”)
str2 = CStr(str2 & “”)
n = Len(str1)
m = Len(str2)

‘ 片方が空なら、もう片方の長さがそのまま距離になる
If n = 0 Then
GetLevenshteinDistance = m
Exit Function
End If
If m = 0 Then
GetLevenshteinDistance = n
Exit Function
End If

‘ メモリ確保: (n+1) x (m+1) の2次元配列
‘ VBScriptのReDimはコストがかかるため、一度だけ確保する
ReDim matrix(n, m)

‘ 初期化
For i = 0 To n: matrix(i, 0) = i: Next
For j = 0 To m: matrix(0, j) = j: Next

‘ 動的計画法による距離計算
For i = 1 To n
Dim char1: char1 = Mid(str1, i, 1)
For j = 1 To m
‘ 文字が一致していればコストは0、異なれば1
If char1 = Mid(str2, j, 1) Then
cost = 0
Else
cost = 1
End If

‘ 挿入、削除、置換の最小値を選択
‘ matrix(i-1, j) + 1 : 削除
‘ matrix(i, j-1) + 1 : 挿入
‘ matrix(i-1, j-1) + cost : 置換
matrix(i, j) = Min3(matrix(i – 1, j) + 1, _
matrix(i, j – 1) + 1, _
matrix(i – 1, j – 1) + cost)
Next
Next

GetLevenshteinDistance = matrix(n, m)
End Function

‘ 3つの値から最小値を返す内部関数
Private Function Min3(a, b, c)
Dim minVal
minVal = a
If b < minVal Then minVal = b If c < minVal Then minVal = c Min3 = minVal End Function ' =========================================================================== ' 関数名: GetSimilarity ' 概要: レーベンシュタイン距離を元に、0.0~1.0の類似度を算出する ' =========================================================================== Function GetSimilarity(str1, str2) Dim dist, maxLen dist = GetLevenshteinDistance(str1, str2) maxLen = Len(str1) If Len(str2) > maxLen Then maxLen = Len(str2)

If maxLen = 0 Then
GetSimilarity = 1.0
Else
‘ 1 – (距離 / 最大長) で類似度を正規化
GetSimilarity = 1.0 – (dist / maxLen)
End If
End Function

3. 運用:表記揺れ補正ツールへの応用

このアルゴリズムを単体で動かしても意味はない。現場では、数千件の「マスターデータ」に対して、入力された「汚いデータ」をぶつけ、最も類似度が高いものを抽出するロジックが必要だ。

実務での注意点:正規化(Normalization)

レーベンシュタイン距離を計算する前に、必ず「前処理」を行え。これだけで精度は劇的に変わる。

1. 全角・半角の統一: `StrConv`(VBA環境)や、正規表現による変換。
2. 空白の除去: `Replace(str, ” “, “”)` による徹底したトリミング。
3. 大文字・小文字の統一: `UCase()` を通す。

大量データ処理のパフォーマンス・チップス

数万件のデータ同士を突き合わせる場合、2次元配列の生成を繰り返すのはVBScriptのメモリ管理にとって酷だ。

  • 閾値(Threshold)の設定: 類似度が0.7以下のものは途中で計算を打ち切る、あるいは文字列の長さの差が極端に大きいものは計算対象から外すといった、枝刈りのロジックを組み込むべきだ。
  • インメモリ処理: CSVやExcelから読み込む際、一度配列(Array)に展開してからループ処理を行え。セルへのアクセスやファイルI/Oをループ内で発生させるのは、三流のすることだ。

4. データベース連携・ファイル出力の堅牢設計

自動補正した結果をデータベースやCSVに書き戻す際、最も恐れるべきは「誤補正のサイレント発生」である。

  • 確信度の記録: 自動補正した際、`GetSimilarity` で得られたスコアも一緒に記録せよ。
  • フラグ管理: 類似度が0.95以上のものは「自動確定」、0.8以上0.95未満は「要目視確認」、それ以下は「不一致」としてステータスを分ける設計にせよ。

‘ 実装イメージ:マッチングエンジンのメインループ
Dim targetName, masterName, score
targetName = “株式会社アイウエオ” ‘ 汚い入力データ

‘ 事前正規化(例:半角を全角に、空白除去などが必要)
targetName = NormalizeString(targetName)

Dim bestMatch, maxScore: maxScore = 0
For Each masterName In masterList ‘ masterListはあらかじめ取得した配列
score = GetSimilarity(targetName, masterName)
If score > maxScore Then
maxScore = score
bestMatch = masterName
End If
Next

If maxScore > 0.9 Then
WScript.Echo “高精度一致: ” & bestMatch & ” (Score: ” & maxScore & “)”
ElseIf maxScore > 0.7 Then
WScript.Echo “要確認: ” & bestMatch & ” (Score: ” & maxScore & “)”
End If

結論:コードの背後にある「思想」を操れ

レーベンシュタイン距離の実装は、単なるプログラミングスキルの誇示ではない。それは、「不完全な現実世界のデータ」と「厳密なシステムの論理」を繋ぐ架け橋である。

今回提供したコードは、VBScriptという枯れた言語において、最大限のパフォーマンスと保守性を発揮するように設計してある。諸君はこのコードを土台に、現場の泥臭い課題をエレガントに解決してほしい。

道具に使われるな。道具を使いこなし、システムを、そして業務を掌握せよ。それがチーフアーキテクトとしての私の願いである。

タイトルとURLをコピーしました