Levenshtein distanz

    • VB.NET

    Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von picoflop.

      Levenshtein distanz

      Berechnet das Maß der Ähnlichkeit zweier Strings.
      Mehr Infos zu Levenshtein (nicht: Levenstein) bietet ... Wikipedia ;)

      Funktion:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. ''' <summary>
      2. ''' Berechnet die Levenshtein Distanz zweier Strings.
      3. ''' Dies ist quasi ein Maß der Ähnlichkeit
      4. ''' </summary>
      5. ''' <param name="s">String 1</param>
      6. ''' <param name="t">String 2</param>
      7. ''' <returns>Levenshtein Distanz</returns>
      8. ''' <remarks>C# Sample nach VB portiert
      9. ''' Original: http://www.merriampark.com/ldcsharp.htm
      10. ''' </remarks>
      11. Public Function Levenshtein(ByVal s As String, ByVal t As String) As Integer
      12. Dim n As Integer = s.Length
      13. Dim m As Integer = t.Length
      14. Dim d(n + 1, m + 1) As Integer ' matrix
      15. Dim cost As Integer
      16. ' Step 1
      17. If n = 0 Then Return m
      18. If m = 0 Then Return n
      19. ' Step 2
      20. For i As Integer = 0 To n
      21. d(i, 0) = i
      22. Next
      23. For j As Integer = 0 To m
      24. d(0, j) = j
      25. Next
      26. ' Step 3
      27. For i = 1 To n
      28. For j = 1 To m
      29. ' Kosten für eine Position:
      30. If t(j - 1) = s(i - 1) Then
      31. cost = 0
      32. Else
      33. cost = 1
      34. End If
      35. d(i, j) = System.Math.Min(System.Math.Min(d(i - 1, j) + 1, d(i, j - 1) + 1), d(i - 1, j - 1) + cost)
      36. Next j
      37. Next i
      38. Return d(n, m)
      39. End Function



      Nachtrach:
      Hab mal ein bißchen rumgebastelt und das ganze in ne Klasse gepackt und ne Testfunktion geschrieben. Außerdem hab ich mal Multithreading implementiert.

      Klasse:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Imports System.IO
      2. Imports System.Threading
      3. Public Class Levenshtein
      4. Private Structure ThreadArgs
      5. Dim SearchString As String
      6. Dim SrcArray() As String
      7. Dim startIndex As Integer
      8. Dim endInex As Integer
      9. Dim DstList As List(Of String)
      10. Dim MaxDistanz As Integer
      11. Dim ARE As AutoResetEvent
      12. End Structure
      13. Private Shared WordList() As String
      14. Public Shared Sub DoubleList()
      15. Dim tmp(WordList.Length * 2 - 1) As String
      16. For i = 0 To tmp.Length - 1
      17. tmp(i) = WordList(i Mod WordList.Length)
      18. Next
      19. WordList = tmp
      20. End Sub
      21. Public Shared Function GetWordList(ByVal fname As String) As Integer
      22. Try
      23. WordList = IO.File.ReadAllLines(fname)
      24. Return WordList.Count
      25. Catch ex As Exception
      26. Return -1
      27. End Try
      28. End Function
      29. Public Shared Function GetThreadedLevenshteinList(ByVal s As String, ByVal MaxDistanz As Integer, ByVal NumThreads As Integer) As List(Of String)
      30. Dim l(NumThreads - 1) As List(Of String)
      31. Dim are(NumThreads - 1) As AutoResetEvent
      32. Dim tos(NumThreads - 1) As ThreadArgs
      33. Dim startindex As Integer = 0, numwords As Integer = WordList.Count \ NumThreads
      34. For i As Integer = 0 To NumThreads - 1
      35. l(i) = New List(Of String)
      36. are(i) = New AutoResetEvent(True)
      37. With tos(i)
      38. .SearchString = s
      39. .SrcArray = WordList
      40. .startIndex = startindex
      41. .endInex = Math.Min(.startIndex + numwords - 1, WordList.Length - 1)
      42. startindex += numwords
      43. .MaxDistanz = MaxDistanz
      44. .DstList = l(i)
      45. .ARE = are(i)
      46. .ARE.Reset()
      47. End With
      48. Dim t As New Thread(AddressOf GetLevenshteinListThread)
      49. t.Start(tos(i))
      50. Next
      51. System.Threading.WaitHandle.WaitAll(are)
      52. GetThreadedLevenshteinList = New List(Of String)
      53. For i As Integer = 0 To NumThreads - 1
      54. For Each st As String In tos(i).DstList
      55. GetThreadedLevenshteinList.Add(st)
      56. Next
      57. Next
      58. Return GetThreadedLevenshteinList
      59. End Function
      60. Private Shared Sub GetLevenshteinListThread(ByVal o As Object)
      61. Dim tos As ThreadArgs = DirectCast(o, ThreadArgs)
      62. Dim crntl As Integer
      63. Dim wort As String = String.Empty
      64. For i As Integer = tos.startIndex To tos.endInex
      65. crntl = Distanz(tos.SearchString, tos.SrcArray(i))
      66. If crntl < tos.MaxDistanz Then
      67. tos.DstList.Add(tos.SrcArray(i))
      68. End If
      69. Next
      70. tos.ARE.Set()
      71. End Sub
      72. Public Shared Function GetLevenshteinList(ByVal s As String, ByVal MaxDistanz As Integer) As List(Of String)
      73. Dim crntl As Integer
      74. Dim wort As String = String.Empty
      75. Dim l As New List(Of String)
      76. For Each t As String In WordList
      77. crntl = Distanz(s, t)
      78. If crntl < MaxDistanz Then
      79. l.Add(t)
      80. End If
      81. Next
      82. Return l
      83. End Function
      84. ''' <summary>
      85. ''' Berechnet die Levenshtein Distanz zweier Strings.
      86. ''' Dies ist quasi ein Maß der Ähnlichkeit
      87. ''' </summary>
      88. ''' <param name="s">String 1</param>
      89. ''' <param name="t">String 2</param>
      90. ''' <returns>Levenshtein Distanz</returns>
      91. ''' <remarks>C# Sample nach VB portiert
      92. ''' Original: http://www.merriampark.com/ldcsharp.htm
      93. ''' </remarks>
      94. Public Shared Function Distanz(ByVal s As String, ByVal t As String) As Integer
      95. Dim n As Integer = s.Length
      96. Dim m As Integer = t.Length
      97. Dim d(n + 1, m + 1) As Integer ' matrix
      98. Dim cost As Integer
      99. ' Step 1
      100. If n = 0 Then Return m
      101. If m = 0 Then Return n
      102. ' Step 2
      103. For i As Integer = 0 To n
      104. d(i, 0) = i
      105. Next
      106. For j As Integer = 0 To m
      107. d(0, j) = j
      108. Next
      109. ' Step 3
      110. For i = 1 To n
      111. For j = 1 To m
      112. ' Kosten für eine Position:
      113. If t(j - 1) = s(i - 1) Then
      114. cost = 0
      115. Else
      116. cost = 1
      117. End If
      118. d(i, j) = System.Math.Min(System.Math.Min(d(i - 1, j) + 1, d(i, j - 1) + 1), d(i - 1, j - 1) + cost)
      119. Next j
      120. Next i
      121. Return d(n, m)
      122. End Function
      123. End Class


      Testform:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Option Strict On
      2. Imports System.ComponentModel
      3. Imports System.IO
      4. Public Class Form1
      5. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      6. ' wortliste von: wortschatz.uni-leipzig.de/Papers/top10000de.txt
      7. Levenshtein.GetWordList(Path.Combine(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "top10000de.txt"))
      8. ' liste auf 320.000 aufpusten
      9. For i As Integer = 1 To 5
      10. Levenshtein.DoubleList()
      11. Next
      12. End Sub
      13. Private Delegate Sub dlgSetListbox(ByVal s As String)
      14. Private Sub SetListbox(ByVal s As String)
      15. ListBox1.Items.Add(s)
      16. End Sub
      17. Private Sub RunInOtherThread()
      18. Dim stp As New Stopwatch
      19. Dim l As List(Of String)
      20. stp.Start()
      21. l = Levenshtein.GetThreadedLevenshteinList("Haus", 2, 2)
      22. Dim ms As Long = stp.ElapsedMilliseconds
      23. Dim d As dlgSetListbox = AddressOf SetListbox
      24. ListBox1.Invoke(d, "Threaded: " & ms.ToString)
      25. End Sub
      26. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      27. ' Leider können wir die threaded funktion nicht aus dem Hauptthread aufrufen,
      28. ' weil der STAThread ist und dann geht WaitHandle.WaitAll nicht
      29. ' Also schmeissen wir mal nen dummy thread an ;)
      30. Dim t As New Threading.Thread(AddressOf RunInOtherThread)
      31. t.Start()
      32. End Sub
      33. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
      34. Dim stp As New Stopwatch
      35. Dim l As List(Of String)
      36. stp.Start()
      37. l = Levenshtein.GetLevenshteinList("Haus", 2)
      38. Dim ms As Long = stp.ElapsedMilliseconds
      39. ListBox1.Items.Add("Single: " & ms.ToString)
      40. End Sub
      41. End Class


      Die singlethreaded Methode braucht bei mir (dual-core) im schnitt 1050 ms, die Multithread Methode im Schnitt 700 ms.

      Wenn jemand das ganze evtl mal auf nem i920 mit aktiviertem HT ausprobieren könnte ... ;)
      (PS: Anzahl der threads dann natürlich auf 4 oder 8 setzen!)

      Wortliste runterladen nicht vergessen!

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „picoflop“ ()

      picoflop schrieb:

      Wenn jemand das ganze evtl mal auf nem i920 mit aktiviertem HT ausprobieren könnte ...

      So ganz scherzhaft war das nicht gemeint.
      Wenn jemand das mal auf nem "normalen" Quad (mit 4 Threads) bzw nem Quad+HT laufen lassen könnte und ggfs dann mal die Zeiten postet, fänd' ich das schon ganz interessant.
      push ... ;)

      Hab's jetzt mal einen "normalen" Aufruf (Vergleich zweier worte) ausgestoppt (mit dem HiPerfTimer):

      VB.NET-Quellcode

      1. For i = 1 To 1000
      2. Levenshtein.Distanz("Bahnhofsvorplatzgestaltungsideenwettbewerb", "Raketenabschussrampenreinigungspersonal")
      3. Next

      Braucht auf meinem kröppeligen Turion 1.6 GHz gerade mal rund 0.2 Sekunden. Ein einzelner Aufruf braucht rund 0.2 - 0.3 Millisekunden.

      Boolean schrieb:

      Zeitspanne für 100.000 messbar sein wird..

      1. sind es 320.000 Worte ... ;)
      2. evtl mal den HiPerfTimer (SuFu forum) zum messen nehmen. Der hat ne kleinere Auflösung. Im Zweifel kann man natürlich auch noch me 1-10 Schleife umzu bauen.

      3. Interessant wäre es schon. Insb. mal Werte für 1, 2, 4 und 8 Threads.