निकटतम स्ट्रिंग मैच हो रहा है


397

मुझे एक परीक्षण स्ट्रिंग के लिए कई तारों की तुलना करने और स्ट्रिंग को वापस करने की आवश्यकता है जो इसे करीब से देखता है:

TEST STRING: THE BROWN FOX JUMPED OVER THE RED COW

CHOICE A   : THE RED COW JUMPED OVER THE GREEN CHICKEN
CHOICE B   : THE RED COW JUMPED OVER THE RED COW
CHOICE C   : THE RED FOX JUMPED OVER THE BROWN COW

(यदि मैंने इसे सही ढंग से किया है) "टेस्ट स्ट्रॉन्ग" के लिए निकटतम स्ट्रिंग "CHOICE C" होना चाहिए। ऐसा करने का सबसे आसान तरीका क्या है?

मैं इसे VB.net, Lua और जावास्क्रिप्ट सहित कई भाषाओं में लागू करने की योजना बना रहा हूं। इस बिंदु पर, छद्म कोड स्वीकार्य है। यदि आप एक विशिष्ट भाषा के लिए एक उदाहरण प्रदान कर सकते हैं, तो यह भी सराहना की जाती है!


3
एल्गोरिदम जो आम तौर पर इस प्रकार के सामान का काम करते हैं, यह निर्धारित करने पर काम करते हैं कि एक परीक्षण स्ट्रिंग को लक्ष्य स्ट्रिंग में बदलने के लिए कितने परिवर्तन होते हैं। इस प्रकार की एल्गोरिदम इस तरह की स्थिति में बिल्कुल भी अच्छा काम नहीं करती हैं। मुझे लगता है कि इसे बंद करने के लिए एक कंप्यूटर प्राप्त करना बहुत कठिन होगा।
मैट ग्रीर

3
कई भाषाओं में लेवेंसहाइट डिस्टेंस सोर्स कोड: जावा, रूबी, पायथन, पीएचपी, आदि। en.wikibooks.org/wiki/Algorithm_Implementation/Strings/…
joelparkerenderson

9
सामान्य तौर पर, "निकटतम स्ट्रिंग" के रूप में जो गिना जाता है वह उपयोग की गई समानता के माप पर निर्भर करेगा, और संरेखण में अंतराल को पेश करने के लिए उपयोग किए जाने वाले दंड। उदाहरण के लिए, क्या आप "गाय" और "चिकन" को "गाय" और "रेड" से अधिक समान मानते हैं (क्योंकि वे संबंधित अवधारणाएँ हैं), या क्या यह दूसरा तरीका है (क्योंकि "चिकन" में "गाय" से अधिक अक्षर हैं। )? लेकिन एक समानता माप और अंतर दंड को देखते हुए, यह दिखाया जा सकता है कि नीचे दिए गए लेवेन्सहाइट एल्गोरिथ्म की गारंटी आपको निकटतम स्ट्रिंग खोजने के लिए दी गई है। वही नीडलमैन-वून्श और स्मिथ-वाटरमैन (आगे नीचे) का सच है।
स्टेन

चरित्र समूहन, या शब्द समूहन करें। इसे स्कोर दें।
केसी

जवाबों:


952

मुझे लगभग एक साल पहले इस समस्या के साथ प्रस्तुत किया गया था जब यह देखने के लिए आया था कि उपयोगकर्ता विविध जानकारी के डेटाबेस में एक तेल रिग के बारे में जानकारी दर्ज करता है। लक्ष्य कुछ प्रकार की फजी स्ट्रिंग खोज करना था जो सबसे आम तत्वों के साथ डेटाबेस प्रविष्टि की पहचान कर सकता था।

लेवेन्शिन दूरी एल्गोरिथ्म को लागू करने से जुड़े अनुसंधान का एक हिस्सा , जो यह निर्धारित करता है कि एक स्ट्रिंग या वाक्यांश को दूसरे स्ट्रिंग या वाक्यांश में बदलने के लिए कितने परिवर्तन किए जाने चाहिए।

मैं जिस कार्यान्वयन के साथ आया था वह अपेक्षाकृत सरल था, और इसमें दो वाक्यांशों की लंबाई, प्रत्येक वाक्यांश के बीच परिवर्तनों की संख्या, और क्या प्रत्येक शब्द लक्ष्य प्रविष्टि में पाया जा सकता है, की एक भारित तुलना शामिल थी।

लेख एक निजी साइट पर है, इसलिए मैं यहां संबंधित सामग्री को जोड़ने की पूरी कोशिश करूंगा:


फजी स्ट्रिंग मिलान दो शब्दों या वाक्यांशों की समानता का मानव जैसा प्रदर्शन करने की प्रक्रिया है। कई मामलों में, इसमें उन शब्दों या वाक्यांशों की पहचान करना शामिल है जो एक-दूसरे के समान हैं। इस लेख में फजी स्ट्रिंग मिलान समस्या और विभिन्न प्रकार की समस्याओं को हल करने में इसकी उपयोगिता के बारे में इन-हाउस समाधान का वर्णन किया गया है जो हमें उन कार्यों को स्वचालित करने की अनुमति दे सकता है जो पहले थकाऊ उपयोगकर्ता की भागीदारी की आवश्यकता थी।

परिचय

मूल रूप से फजी स्ट्रिंग मिलान करने की आवश्यकता मेक्सिको की खाड़ी के उपकरण के विकास के समय के बारे में आई थी। जो अस्तित्व में था वह मेक्सिको ऑयल रिग्स और प्लेटफ़ॉर्म के ज्ञात खाड़ी का एक डेटाबेस था, और बीमा खरीदने वाले लोग हमें अपनी संपत्ति के बारे में कुछ बुरी तरह से टाइप की गई जानकारी देंगे और हमें इसे ज्ञात प्लेटफार्मों के डेटाबेस से मेल खाना था। जब बहुत कम जानकारी दी गई थी, तो हम जो सबसे अच्छा कर सकते थे, वह एक अंडरराइटर पर भरोसा करने के लिए किया गया था, जिसे वे उचित जानकारी के लिए बुला रहे थे। यह वह जगह है जहां यह स्वचालित समाधान काम में आता है।

मैंने एक दिन फजी स्ट्रिंग मिलान के तरीकों पर शोध किया, और अंततः विकिपीडिया पर बहुत उपयोगी लेवेन्सहाइट दूरी एल्गोरिथ्म पर ठोकर खाई।

कार्यान्वयन

इसके पीछे के सिद्धांत के बारे में पढ़ने के बाद, मैंने इसे लागू करने और इसे अनुकूलित करने के तरीके ढूंढे। यह मेरा कोड VBA में कैसा दिखता है:

'Calculate the Levenshtein Distance between two strings (the number of insertions,
'deletions, and substitutions needed to transform the first string into the second)
Public Function LevenshteinDistance(ByRef S1 As String, ByVal S2 As String) As Long
    Dim L1 As Long, L2 As Long, D() As Long 'Length of input strings and distance matrix
    Dim i As Long, j As Long, cost As Long 'loop counters and cost of substitution for current letter
    Dim cI As Long, cD As Long, cS As Long 'cost of next Insertion, Deletion and Substitution
    L1 = Len(S1): L2 = Len(S2)
    ReDim D(0 To L1, 0 To L2)
    For i = 0 To L1: D(i, 0) = i: Next i
    For j = 0 To L2: D(0, j) = j: Next j

    For j = 1 To L2
        For i = 1 To L1
            cost = Abs(StrComp(Mid$(S1, i, 1), Mid$(S2, j, 1), vbTextCompare))
            cI = D(i - 1, j) + 1
            cD = D(i, j - 1) + 1
            cS = D(i - 1, j - 1) + cost
            If cI <= cD Then 'Insertion or Substitution
                If cI <= cS Then D(i, j) = cI Else D(i, j) = cS
            Else 'Deletion or Substitution
                If cD <= cS Then D(i, j) = cD Else D(i, j) = cS
            End If
        Next i
    Next j
    LevenshteinDistance = D(L1, L2)
End Function

सरल, शीघ्र और बहुत उपयोगी मीट्रिक। इसका उपयोग करते हुए, मैंने दो तारों की समानता के मूल्यांकन के लिए दो अलग-अलग मीट्रिक बनाए। एक को मैं "valuePhrase" कहता हूं और एक को मैं "valueScript" कहता हूं। valuePhrase दो वाक्यांशों के बीच सिर्फ लेवेंसहाइट दूरी है, और मानदंड अलग-अलग शब्दों में स्ट्रिंग को विभाजित करते हैं, जैसे कि सीमांकक के आधार पर रिक्त स्थान, डैश, और कुछ भी जो आप चाहते हैं, और प्रत्येक शब्द की एक दूसरे से तुलना करते हैं, सबसे छोटा संक्षेप। किसी भी दो शब्दों को जोड़ते हुए लेवेन्सेटिन दूरी। अनिवार्य रूप से, यह मापता है कि क्या एक 'वाक्यांश' में जानकारी वास्तव में दूसरे में निहित है, बस एक शब्द-वार क्रमपरिवर्तन के रूप में। मैंने एक पक्ष परियोजना के रूप में कुछ दिन बिताए हैं जो कि एक सबसे महत्वपूर्ण तरीका है जो कि सीमांकक के आधार पर एक स्ट्रिंग को विभाजित करने के लिए संभव है।

मान, मान, मान और स्प्लिट फ़ंक्शन:

Public Function valuePhrase#(ByRef S1$, ByRef S2$)
    valuePhrase = LevenshteinDistance(S1, S2)
End Function

Public Function valueWords#(ByRef S1$, ByRef S2$)
    Dim wordsS1$(), wordsS2$()
    wordsS1 = SplitMultiDelims(S1, " _-")
    wordsS2 = SplitMultiDelims(S2, " _-")
    Dim word1%, word2%, thisD#, wordbest#
    Dim wordsTotal#
    For word1 = LBound(wordsS1) To UBound(wordsS1)
        wordbest = Len(S2)
        For word2 = LBound(wordsS2) To UBound(wordsS2)
            thisD = LevenshteinDistance(wordsS1(word1), wordsS2(word2))
            If thisD < wordbest Then wordbest = thisD
            If thisD = 0 Then GoTo foundbest
        Next word2
foundbest:
        wordsTotal = wordsTotal + wordbest
    Next word1
    valueWords = wordsTotal
End Function

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' SplitMultiDelims
' This function splits Text into an array of substrings, each substring
' delimited by any character in DelimChars. Only a single character
' may be a delimiter between two substrings, but DelimChars may
' contain any number of delimiter characters. It returns a single element
' array containing all of text if DelimChars is empty, or a 1 or greater
' element array if the Text is successfully split into substrings.
' If IgnoreConsecutiveDelimiters is true, empty array elements will not occur.
' If Limit greater than 0, the function will only split Text into 'Limit'
' array elements or less. The last element will contain the rest of Text.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function SplitMultiDelims(ByRef Text As String, ByRef DelimChars As String, _
        Optional ByVal IgnoreConsecutiveDelimiters As Boolean = False, _
        Optional ByVal Limit As Long = -1) As String()
    Dim ElemStart As Long, N As Long, M As Long, Elements As Long
    Dim lDelims As Long, lText As Long
    Dim Arr() As String

    lText = Len(Text)
    lDelims = Len(DelimChars)
    If lDelims = 0 Or lText = 0 Or Limit = 1 Then
        ReDim Arr(0 To 0)
        Arr(0) = Text
        SplitMultiDelims = Arr
        Exit Function
    End If
    ReDim Arr(0 To IIf(Limit = -1, lText - 1, Limit))

    Elements = 0: ElemStart = 1
    For N = 1 To lText
        If InStr(DelimChars, Mid(Text, N, 1)) Then
            Arr(Elements) = Mid(Text, ElemStart, N - ElemStart)
            If IgnoreConsecutiveDelimiters Then
                If Len(Arr(Elements)) > 0 Then Elements = Elements + 1
            Else
                Elements = Elements + 1
            End If
            ElemStart = N + 1
            If Elements + 1 = Limit Then Exit For
        End If
    Next N
    'Get the last token terminated by the end of the string into the array
    If ElemStart <= lText Then Arr(Elements) = Mid(Text, ElemStart)
    'Since the end of string counts as the terminating delimiter, if the last character
    'was also a delimiter, we treat the two as consecutive, and so ignore the last elemnent
    If IgnoreConsecutiveDelimiters Then If Len(Arr(Elements)) = 0 Then Elements = Elements - 1

    ReDim Preserve Arr(0 To Elements) 'Chop off unused array elements
    SplitMultiDelims = Arr
End Function

समानता के उपाय

इन दो मैट्रिक्स का उपयोग करना, और एक तिहाई जो केवल दो तारों के बीच की दूरी की गणना करता है, मेरे पास चर की एक श्रृंखला है जिसे मैं सबसे बड़ी संख्या में मैच प्राप्त करने के लिए एक अनुकूलन एल्गोरिथ्म चला सकता हूं। फजी स्ट्रिंग मिलान, अपने आप में, एक फजी विज्ञान है, और इसलिए स्ट्रिंग समानता को मापने के लिए रैखिक स्वतंत्र मैट्रिक्स बनाकर, और तार के एक ज्ञात सेट के साथ हम एक दूसरे से मेल खाना चाहते हैं, हम उन मापदंडों को पा सकते हैं, जो हमारी विशिष्ट शैलियों के लिए हैं तार, सबसे अच्छा फजी मैच परिणाम दे।

प्रारंभ में, मीट्रिक का लक्ष्य एक सटीक मैच के लिए कम खोज मूल्य और तेजी से अनुमत उपायों के लिए खोज मूल्यों में वृद्धि करना था। एक अव्यवहारिक मामले में, यह अच्छी तरह से परिभाषित क्रमपरिवर्तन के एक सेट का उपयोग करके परिभाषित करने के लिए काफी आसान था, और अंतिम सूत्र को इंजीनियरिंग करना जैसे कि उनके पास वांछित के रूप में खोज मूल्यों में वृद्धि हुई थी।

फजी स्ट्रिंग मैचिंग क्रमपरिवर्तन

ऊपर दिए गए स्क्रीनशॉट में, मैंने अपने उत्तराधिकारी को कुछ ऐसा करने के लिए कहा, जिसे मैंने खोज शब्द और परिणाम के बीच मेरे कथित अंतर को बारीकी से महसूस किया। Value Phraseउपरोक्त स्प्रैडशीट में मैंने जो अनुमान लगाया था, वह था =valuePhrase(A2,B2)-0.8*ABS(LEN(B2)-LEN(A2))। मैं दो "वाक्यांशों" की लंबाई में अंतर के 80% द्वारा लेवेनस्टीन दूरी के दंड को प्रभावी ढंग से कम कर रहा था। इस तरह, "वाक्यांश" जिसमें समान लंबाई होती है, पूर्ण दंड भुगतते हैं, लेकिन "वाक्यांश" जिसमें 'अतिरिक्त जानकारी' (लंबी) होती है, लेकिन एक तरफ से अभी भी ज्यादातर उसी वर्ण को साझा करते हैं, एक कम दंड भुगतते हैं। मैंने Value Wordsफ़ंक्शन का उपयोग उस रूप में किया है, और फिर मेरे अंतिम SearchValअनुमान के रूप में परिभाषित किया गया था=MIN(D2,E2)*0.8+MAX(D2,E2)*0.2- एक भारित औसत। दोनों में से जो भी स्कोर कम था उसका वजन 80% और उच्च स्कोर का 20% था। यह सिर्फ एक अनुमान है कि एक अच्छी मैच दर पाने के लिए मेरे उपयोग के मामले में अनुकूल था। ये वज़न कुछ ऐसे हैं कि एक तो अपने परीक्षण डेटा के साथ सबसे अच्छा मैच दर पाने के लिए tweak कर सकते हैं।

फजी स्ट्रिंग मिलान मूल्य वाक्यांश

फजी स्ट्रिंग मिलान मूल्य शब्द

जैसा कि आप देख सकते हैं, अंतिम दो मैट्रिक्स, जो फजी स्ट्रिंग मिलान मैट्रिक्स हैं, पहले से ही तार के लिए कम स्कोर देने की एक प्राकृतिक प्रवृत्ति है जो मिलान (विकर्ण के नीचे) के लिए होती है। यह बहुत अच्छा है।

आवेदन फजी मिलान के अनुकूलन की अनुमति देने के लिए, मैं प्रत्येक मीट्रिक का वजन करता हूं। जैसे, फजी स्ट्रिंग मैच के हर एप्लिकेशन मापदंडों को अलग तरीके से वजन कर सकते हैं। अंतिम स्कोर को परिभाषित करने वाला सूत्र केवल मैट्रिक्स और उनके भार का संयोजन है:

value = Min(phraseWeight*phraseValue, wordsWeight*wordsValue)*minWeight
      + Max(phraseWeight*phraseValue, wordsWeight*wordsValue)*maxWeight
      + lengthWeight*lengthValue

एक अनुकूलन एल्गोरिथ्म का उपयोग करना (तंत्रिका नेटवर्क यहां सबसे अच्छा है क्योंकि यह एक असतत, बहु-विषयक समस्या है), लक्ष्य अब मैचों की संख्या को अधिकतम करने के लिए है। मैंने एक फ़ंक्शन बनाया जो प्रत्येक सेट के सही मैचों की संख्या का एक दूसरे से पता लगाता है, जैसा कि इस अंतिम स्क्रीनशॉट में देखा जा सकता है। एक स्तंभ या पंक्ति को एक बिंदु मिलता है यदि निम्नतम स्कोर को स्ट्रिंग प्रदान किया जाता है जिसे मिलान किया जाना था, और आंशिक अंक दिए गए हैं यदि सबसे कम स्कोर के लिए एक टाई है, और सही मिलान बंधे हुए तार के बीच है। मैंने तब इसे अनुकूलित किया। आप देख सकते हैं कि एक ग्रीन सेल वह कॉलम है जो वर्तमान पंक्ति से सबसे अच्छा मेल खाता है, और सेल के चारों ओर एक नीला वर्ग वह पंक्ति है जो वर्तमान स्तंभ से सर्वोत्तम रूप से मेल खाता है। निचले कोने में स्कोर लगभग सफल मैचों की संख्या है और यही हम अपनी अनुकूलन समस्या को अधिकतम करने के लिए कहते हैं।

फजी स्ट्रिंग मिलान मिलान अनुकूलित मीट्रिक

एल्गोरिथ्म एक अद्भुत सफलता थी, और समाधान पैरामीटर इस प्रकार की समस्या के बारे में बहुत कुछ कहते हैं। आप देखेंगे कि अनुकूलित स्कोर 44 था, और सबसे अच्छा संभव स्कोर 48 है। अंत में 5 कॉलम डिकॉय हैं, और पंक्ति मानों में इसका कोई मेल नहीं है। जितना अधिक डिकॉय होगा, उतना ही स्वाभाविक रूप से सबसे अच्छा मैच ढूंढना होगा।

इस विशेष मिलान मामले में, स्ट्रिंग्स की लंबाई अप्रासंगिक है, क्योंकि हम ऐसे संक्षिप्तीकरण की उम्मीद कर रहे हैं जो अधिक शब्दों का प्रतिनिधित्व करते हैं, इसलिए लंबाई के लिए इष्टतम वजन -0.3 है, जिसका अर्थ है कि हम उन स्ट्रिंग्स को दंडित नहीं करते हैं जो लंबाई में भिन्न होते हैं। हम इन संक्षिप्ताक्षरों की प्रत्याशा में स्कोर को कम करते हैं, गैर-शब्द मिलानों के लिए आंशिक शब्द मिलानों के लिए और अधिक कमरा देते हैं जिन्हें केवल कम प्रतिस्थापन की आवश्यकता होती है क्योंकि स्ट्रिंग कम होती है।

शब्द का वजन 1.0 है, जबकि वाक्यांश का वजन केवल 0.5 है, जिसका अर्थ है कि हम एक स्ट्रिंग से गायब पूरे शब्दों को दंडित करते हैं और पूरे वाक्यांश को अधिक महत्व देते हैं। यह उपयोगी है क्योंकि इनमें से बहुत सारे तार में एक शब्द सामान्य (पेरिल) है जहां वास्तव में क्या मायने रखता है कि संयोजन (क्षेत्र और जोखिम) बना हुआ है या नहीं।

अंत में, न्यूनतम वजन 10 पर अनुकूलित किया जाता है और अधिकतम वजन 1. पर होता है। इसका क्या मतलब है कि अगर दो अंकों में से सबसे अच्छा (मूल्य वाक्यांश और मूल्य शब्द) बहुत अच्छा नहीं है, तो मैच बहुत दंडित किया जाता है, लेकिन हम डॉन 'दो अंकों में से सबसे खराब दंड। अनिवार्य रूप से, यह या तो आवश्यकता पर जोर देता है तो वैल्यूवार्ड या वैल्यूफेयर की , ताकि अच्छा स्कोर हो, लेकिन दोनों नहीं। एक प्रकार का "हम क्या प्राप्त कर सकते हैं" मानसिकता लें।

यह वास्तव में आकर्षक है कि इन 5 वज़न का अनुकूलित मूल्य फ़ज़ी स्ट्रिंग मिलान के प्रकार के बारे में कहता है। फजी स्ट्रिंग मिलान के पूरी तरह से अलग व्यावहारिक मामलों के लिए, ये पैरामीटर बहुत अलग हैं। मैंने अब तक 3 अलग-अलग अनुप्रयोगों के लिए इसका उपयोग किया है।

अंतिम अनुकूलन में अप्रयुक्त होने के दौरान, एक बेंचमार्किंग शीट स्थापित की गई थी जो तिरछे नीचे सभी सही परिणामों के लिए स्तंभों से मेल खाती है, और उपयोगकर्ता को मापदंडों को बदलने की अनुमति देती है जिस पर स्कोर 0 से भिन्न होता है, और वाक्यांशों के बीच सहज समानताएं नोट करें ( जो सिद्धांत में इस्तेमाल किया जा सकता है परिणामों में झूठी सकारात्मकता की भरपाई करने के लिए)

फजी स्ट्रिंग मैचिंग बेंचमार्क

आगे के अनुप्रयोग

इस समाधान में कहीं भी उपयोग किए जाने की क्षमता है जहां उपयोगकर्ता कंप्यूटर सिस्टम की इच्छा रखता है ताकि स्ट्रिंग के एक सेट में एक स्ट्रिंग की पहचान हो सके जहां कोई पूर्ण मिलान नहीं है। (तार के लिए एक अनुमानित मैच की तरह)।


तो आपको इससे क्या लेना चाहिए, क्या यह है कि आप शायद लेवेन्सहाइट दूरी एल्गोरिथ्म के कार्यान्वयन के साथ-साथ उच्च स्तर के आंकड़ों के संयोजन (दूसरे वाक्यांश में एक वाक्यांश से शब्द, दोनों वाक्यांशों की लंबाई, आदि) का उपयोग करना चाहते हैं। क्योंकि यह निर्णय लेना कि "सबसे अच्छा" मैच कौन सा हैजिस्टिक (फजी) निर्धारण है - आपको समानता निर्धारित करने के लिए आने वाले किसी भी मेट्रिक्स के लिए वजन के एक सेट के साथ आना होगा।

ह्यूरिस्टिक्स और वेट के उपयुक्त सेट के साथ, आप अपने तुलनात्मक कार्यक्रम जल्दी से निर्णय लेंगे जो आपने किया होगा।


13
बोनस: यदि कोई भी अपने वजन वाले अनुमान में अतिरिक्त मैट्रिक्स को शामिल करना चाहता है, (क्योंकि मैंने केवल 3 प्रदान किए हैं जो सभी रैखिक रूप से स्वतंत्र नहीं थे) - यहाँ विकिपीडिया पर एक पूरी सूची है: en.wikipedia.org/wiki/String-metric
Alain

1
यदि S2 में बहुत सारे शब्द हैं (और कई छोटी वस्तुओं का निर्माण आपकी पसंद की भाषा में निषेधात्मक रूप से धीमा नहीं है) एक ट्राइ चीजों को गति दे सकती है। एक ट्राइ का उपयोग करके फास्ट और आसान लेवेन्शिन दूरी कोशिशों के बारे में एक शानदार लेख है।
JanX2

1
@Alain यह एक दिलचस्प तरीका है! मैं सिर्फ आपके विचार (C ++ में) के साथ थोड़ा खेल रहा हूं, लेकिन एक बिंदु को नहीं समझता, का मूल्य valuePhrase। यदि मैं आपके कोड में सही देखता हूं, तो लेवेंसहाइट डिस्टेंस फ़ंक्शन का रिटर्न मान। यह कैसे 'abcd efgh' खोज तालिका में एक डबल / फ्लोट मान है? Levenshtein दूरी एक पूर्णांक मान है और मैं आपके कोड में आगे की गणना नहीं देख सकता जो इसे फ्लोट बनाता है। मुझे क्या याद आती है?
एंड्रियास डब्ल्यू। विलेच

1
@ AndreasW.Wylach महान अवलोकन। मैंने जो वीबीए दिखाया वह सिर्फ लेवेंशेटिन की दूरी की गणना करने के लिए था, लेकिन मैंने अपनी स्प्रैडशीट =valuePhrase(A2,B2)-0.8*ABS(LEN(B2)-LEN(A2))में जिस हेयुरिस्टिक का इस्तेमाल किया था, इसलिए मैं लेवेनस्टीन की दूरी को दो "वाक्यांशों" की लंबाई में 80% के अंतर से कम कर रहा था। इस तरह, "वाक्यांश" जिसमें समान लंबाई होती है, पूर्ण दंड भुगतते हैं, लेकिन "वाक्यांश" जिसमें 'अतिरिक्त जानकारी' (लंबी) होती है, लेकिन एक तरफ से अभी भी ज्यादातर वही वर्ण साझा होते हैं जो एक कम दंड भुगतते हैं।
एलेन

1
@ मेरे सवाल पर वापस पाने के लिए धन्यवाद, मैं इसकी सराहना करता हूं। आपकी व्याख्या अब चीजों को स्पष्ट करती है। इस बीच मैंने एक value_phrase पद्धति लागू की, जो किसी वाक्यांश के टोकन का थोड़ा और अधिक विश्लेषण करने में थोड़ा गहरा हो जाता है, जो कि वाक्यांश टोकन के आदेश / स्थिति, गैर-क्वेरी टोकन अनुक्रम है और यह कुछ होने पर थोड़ा अधिक अस्पष्टता स्वीकार करता है "abcd" की तुलना में "acbd"। मुहावरा_समर्थन स्कोर की प्रवृत्ति आपकी बराबरी करती है, लेकिन यहाँ और वहाँ थोड़ा कम मिलता है। एक बार फिर, महान कसरत और इसने मुझे फजी सर्च एल्गोरिदम के लिए प्रेरणा दी!
एंड्रियास डब्ल्यू। विलेच

88

यह समस्या हर समय जैव सूचना विज्ञान में बदल जाती है। ऊपर स्वीकार किए गए उत्तर (जो कि जिस तरह से महान थे) को जैव सूचना विज्ञान में सुईलेमैन-वुन्श (दो तारों की तुलना करें) और स्मिथ-वाटरमैन (लंबी स्ट्रिंग में एक अनुमानित विकल्प खोजने वाले) एल्गोरिदम के रूप में जाना जाता है। वे शानदार काम करते हैं और दशकों से वर्कहॉर्स हैं।

लेकिन क्या होगा अगर आपके पास तुलना करने के लिए एक लाख तार हैं? यह एक खरब जोड़ीदार तुलना है, जिनमें से प्रत्येक हे (n * m) है! आधुनिक डीएनए सीक्वेंसर आसानी से एक अरब लघु डीएनए अनुक्रम उत्पन्न करते हैं , प्रत्येक में लगभग 200 डीएनए "अक्षर" लंबे होते हैं। आमतौर पर, हम ऐसे प्रत्येक स्ट्रिंग के लिए, मानव जीनोम (3 बिलियन अक्षरों) के खिलाफ सबसे अच्छा मैच ढूंढना चाहते हैं। स्पष्ट रूप से, नीडलमैन-वून्श एल्गोरिथ्म और उसके रिश्तेदार नहीं करेंगे।

यह तथाकथित "संरेखण समस्या" सक्रिय अनुसंधान का एक क्षेत्र है। सबसे लोकप्रिय एल्गोरिदम वर्तमान में उचित हार्डवेयर (कहते हैं, आठ कोर और 32 जीबी रैम) पर कुछ ही घंटों में 1 बिलियन शॉर्ट स्ट्रिंग्स और मानव जीनोम के बीच अनुभवहीन मैच खोजने में सक्षम हैं।

इनमें से अधिकांश एल्गोरिदम कम सटीक मिलान (बीज) खोजकर और फिर धीमे एल्गोरिथ्म (उदाहरण के लिए, स्मिथ-वाटरमैन) का उपयोग करके पूर्ण स्ट्रिंग में विस्तारित होकर काम करते हैं। इसका कारण यह है कि हम वास्तव में केवल कुछ करीबी मैचों में रुचि रखते हैं, इसलिए यह 99.9 से छुटकारा पाने के लिए भुगतान करता है ... ऐसे जोड़े का% जिनके पास कुछ भी नहीं है।

सटीक मैचों को खोजने से कैसे अक्षम मैचों को खोजने में मदद मिलती है ? ठीक है, मान लें कि हम क्वेरी और लक्ष्य के बीच केवल एक ही अंतर रखते हैं। यह देखना आसान है कि क्वेरी के दाएं या बाएं आधे हिस्से में यह अंतर होना चाहिए, और इसलिए अन्य आधा बिल्कुल मेल खाना चाहिए। इस विचार को कई बेमेल तक बढ़ाया जा सकता है और यह ELAND का आधार है एल्गोरिथ्म है जिसे आमतौर पर डीएनए सीक्वेंसर के साथ प्रयोग किया जाता है।

सटीक स्ट्रिंग मिलान करने के लिए कई बहुत अच्छे एल्गोरिदम हैं। लंबाई 200 की एक क्वेरी स्ट्रिंग, और 3 बिलियन (मानव जीनोम) की एक लक्ष्य स्ट्रिंग को देखते हुए, हम लक्ष्य में किसी भी जगह को ढूंढना चाहते हैं जहां लंबाई k का एक विकल्प हो जो क्वेरी के एक विकल्प से मेल खाता हो। एक सरल दृष्टिकोण लक्ष्य को अनुक्रमित करके शुरू करना है: सभी के-लंबे सबस्ट्रिंग लें, उन्हें एक सरणी में रखें और उन्हें सॉर्ट करें। फिर क्वेरी के प्रत्येक के-लॉन्ग सबस्ट्रिंग को लें और सॉर्ट किए गए इंडेक्स को खोजें। सॉर्ट और सर्च ओ (लॉग एन) समय में किया जा सकता है।

लेकिन भंडारण एक समस्या हो सकती है। 3 बिलियन लेटर लक्ष्य के एक इंडेक्स को 3 बिलियन पॉइंटर्स और 3 बिलियन के-लॉन्ग शब्द रखने की आवश्यकता होगी। यह कई टन से कम गीगाबाइट रैम से कम में फिट करना मुश्किल होगा। लेकिन आश्चर्यजनक रूप से हम बर्स-व्हीलर ट्रांसफ़ॉर्म का उपयोग करके सूचकांक को बहुत कम कर सकते हैं , और यह अभी भी कुशलतापूर्वक क्वेरी योग्य होगा। मानव जीनोम का एक सूचकांक 4 जीबी रैम से कम में फिट हो सकता है। यह विचार बोवी और बीडब्ल्यूए जैसे लोकप्रिय अनुक्रम संरेखकों का आधार है ।

वैकल्पिक रूप से, हम एक प्रत्यय सरणी का उपयोग कर सकते हैं , जो केवल बिंदुओं को संग्रहीत करता है, फिर भी लक्ष्य स्ट्रिंग में सभी प्रत्ययों के एक साथ सूचकांक का प्रतिनिधित्व करता है (अनिवार्य रूप से, कश्मीर के सभी संभावित मूल्यों के लिए एक साथ सूचकांक; वही बर्स-व्हीलर ट्रांसफॉर्म का सच है। )। यदि हम 32-बिट पॉइंटर्स का उपयोग करते हैं तो मानव जीनोम का एक प्रत्यय सरणी इंडेक्स 12 जीबी रैम लेगा।

ऊपर दिए गए लिंक में जानकारी का खजाना है और प्राथमिक शोध पत्रों के लिंक हैं। ELAND लिंक एक पीडीएफ में जाता है जिसमें उपयोगी आंकड़े शामिल होते हैं, जिसमें शामिल अवधारणाएं होती हैं, और यह दिखाता है कि सम्मिलन और विलोपन से कैसे निपटें।

अंत में, जबकि इन एल्गोरिदम ने मूल रूप से एकल मानव जीनोम (एक अरब छोटे तार) अनुक्रमण की समस्या को हल किया है, डीएनए अनुक्रमण तकनीक मूर के कानून की तुलना में भी तेजी से सुधार करती है, और हम तेजी से खरब-पत्र डेटासेट के पास जा रहे हैं। उदाहरण के लिए, वर्तमान में 10,000 कशेरुक प्रजातियों के जीनोम का अनुक्रम करने के लिए परियोजनाएं चल रही हैं , प्रत्येक एक अरब पत्र लंबा या तो। स्वाभाविक रूप से, हम डेटा पर युग्मन अक्षम्य स्ट्रिंग करना चाहते हैं ...


3
वास्तव में अच्छा रन-डाउन। सुधारों की एक जोड़ी: शिशुओं को छांटना ओ (एन) कम से कम लेता है, ओ नहीं (लॉग एन)। और चूंकि O (log n) खोज वास्तव में बहुत धीमी गति से चल रही है, इसलिए आप सामान्य रूप से O (1) लुकअप (q-gram index) प्राप्त करने के लिए एक अतिरिक्त तालिका बनाएंगे। इसके अलावा, मुझे यकीन नहीं है कि आप इसे प्रत्यय सरणी से अलग क्यों मानते हैं - यह सिर्फ उत्तरार्द्ध का एक अनुकूलन है, नहीं (प्रत्यय के बजाय निश्चित लंबाई के शिशुओं को छांटना क्योंकि हमें वास्तव में निश्चित लंबाई से अधिक की आवश्यकता नहीं है)।
कोनराड रुडोल्फ

1
इसके अलावा, इन एल्गोरिदम अभी भी डे नोवो अनुक्रमण के लिए अव्यावहारिक हैं । उन्होंने मानव जीनोम की अनुक्रमणिका को केवल अनिद्रा के रूप में हल किया है क्योंकि हमारे पास एक संदर्भ अनुक्रम है जिसका उपयोग मानचित्र के खिलाफ किया जा सकता है। लेकिन डे नोवो असेंबली के लिए अन्य एल्गोरिदम की आवश्यकता होती है (ठीक है, कुछ एलाइनर हैं जो मैपिंग पर आधारित हैं, लेकिन एक साथ कंटेस्टेंट को सिलाई करना एक पूरी 'नोटेर समस्या' है)। अंत में, बेशर्म प्लग: मेरी स्नातक थीसिस में एलएंड एल्गोरिथ्म का विस्तृत विवरण शामिल है।
कोनराड रुडोल्फ

1
धन्यवाद। मैंने त्रुटि को संपादित किया। फिक्स्ड-लेंथ एरे का वर्णन करके मैंने जो शुरुआत की, वह इसलिए थी क्योंकि इसे समझना आसान है। प्रत्यय सरणियों और बीडब्ल्यूटी को पकड़ना थोड़ा कठिन है, लेकिन वास्तव में हम कभी-कभी कश्मीर के विभिन्न मूल्यों के साथ एक सूचकांक का उपयोग करना चाहते हैं। उदाहरण के लिए, स्टार कुशलता से spliced संरेखण खोजने के लिए प्रत्यय सरणियों का उपयोग करता है । यह निश्चित रूप से जीनोम को आरएनए को संरेखित करने के लिए उपयोगी है।
Sten L

30

मुझे लगता है कि चुनाव B टेस्ट स्ट्रिंग के करीब है, क्योंकि यह मूल स्ट्रिंग होने से केवल 4 अक्षर (और 2 डिलीट) है। जबकि आप सी को करीब से देखते हैं क्योंकि इसमें भूरा और लाल दोनों शामिल हैं। हालाँकि, यह एक बड़ी दूरी को संपादित करेगा।

एक एल्गोरिथ्म है जिसे लेवेन्शिन डिस्टेंस कहा जाता है जो दो इनपुटों के बीच की दूरी को संपादित करता है।

यहाँ उस एल्गोरिथ्म के लिए एक उपकरण है।

  1. दरें 15 की दूरी के रूप में पसंद करती हैं।
  2. दरें 6 की दूरी के रूप में बी पसंद करती हैं।
  3. 9 की दूरी के रूप में दरें पसंद सी।

संपादित करें: क्षमा करें, मैं लेवेंसहाइट टूल में तार मिलाता रहता हूं। उत्तर सही करने के लिए अद्यतन किया गया।


2
ठीक है, मुझे लगता है कि यह सच है। मैं इस पर एक नज़र डालूँगा। मुझे व्यक्तिगत रूप से परवाह नहीं है कि यह लक्ष्य के कितना करीब है जब तक कि यह काफी करीब है। पूर्णता की कोई आवश्यकता नहीं;) आपके लिए अंक जब तक कि मैं आपके उत्तर के परिणामों को सत्यापित नहीं कर सकता :)
Freesnöw

18

लुआ कार्यान्वयन, पोस्टर के लिए:

function levenshtein_distance(str1, str2)
    local len1, len2 = #str1, #str2
    local char1, char2, distance = {}, {}, {}
    str1:gsub('.', function (c) table.insert(char1, c) end)
    str2:gsub('.', function (c) table.insert(char2, c) end)
    for i = 0, len1 do distance[i] = {} end
    for i = 0, len1 do distance[i][0] = i end
    for i = 0, len2 do distance[0][i] = i end
    for i = 1, len1 do
        for j = 1, len2 do
            distance[i][j] = math.min(
                distance[i-1][j  ] + 1,
                distance[i  ][j-1] + 1,
                distance[i-1][j-1] + (char1[i] == char2[j] and 0 or 1)
                )
        end
    end
    return distance[len1][len2]
end

14

आपको इस ब्लॉग पोस्ट में रुचि हो सकती है।

http://seatgeek.com/blog/dev/fuzzywuzzy-fuzzy-string-matching-in-python

Fuzzywuzzy एक पायथन लाइब्रेरी है जो स्ट्रिंग मिलान के लिए लेवेंसहाइट दूरी जैसे आसान दूरी के उपाय प्रदान करती है। यह मानक पुस्तकालय में डिफ्लिब के शीर्ष पर बनाया गया है और यदि उपलब्ध हो तो सी कार्यान्वयन पायथन-लेवेंसहाइट का उपयोग करेगा।

http://pypi.python.org/pypi/python-Levenshtein/


इसे पढ़ने वाले अन्य लोगों के लिए, फजीविज्जी वास्तव में एलेन के अद्भुत पोस्ट में बहुत सारे विचारों को लागू करता है। यदि आप वास्तव में उन विचारों में से कुछ का उपयोग करना चाह रहे हैं तो इसकी शुरुआत करने के लिए एक शानदार जगह है।
ग्रेगरी एरेनीस

12

आपको यह लाइब्रेरी मददगार लग सकती है! http://code.google.com/p/google-diff-match-patch/

यह वर्तमान में Java, JavaScript, Dart, C ++, C #, Objective C, Lua और Python में उपलब्ध है

यह बहुत अच्छी तरह से काम करता है। मैं इसे अपने लुआ परियोजनाओं के एक जोड़े में उपयोग करता हूं।

और मुझे नहीं लगता कि इसे अन्य भाषाओं में पोर्ट करना बहुत मुश्किल होगा!


2

यदि आप एक खोज इंजन के संदर्भ में ऐसा कर रहे हैं या किसी डेटाबेस के खिलाफ सामने हैं, तो आप Apache Solr जैसे टूल का उपयोग करने पर विचार कर सकते हैं, जैसे कि ComplexPhraseQueryParser प्लगइन। यह संयोजन आपको प्रासंगिकता द्वारा छाँटे गए परिणामों के साथ स्ट्रिंग्स के एक सूचकांक के खिलाफ खोज करने की अनुमति देता है, जैसा कि लेवेंसहाइट दूरी द्वारा निर्धारित किया गया है।

जब हम आने वाली क्वेरी में एक या अधिक टाइपोस हो सकते हैं, तो हम इसे कलाकारों और गीतों के बड़े संग्रह के खिलाफ उपयोग कर रहे हैं, और यह बहुत अच्छी तरह से काम कर रहा है (और संग्रह के लाखों लोगों में तेजी से विचार कर रहा है)।

इसके अतिरिक्त, सोलर के साथ, आप JSON के माध्यम से मांग पर सूचकांक के खिलाफ खोज कर सकते हैं, इसलिए आपको उन विभिन्न भाषाओं के बीच समाधान को पुनः प्राप्त नहीं करना पड़ेगा जिन्हें आप देख रहे हैं।


1

इन प्रकार के एल्गोरिदम के लिए एक बहुत, बहुत अच्छा संसाधन सिमेट्रिक्स है: http://sourceforge.net/projects/slmetmet/

दुर्भाग्य से बहुत सारी डॉक्युमेंटेशन वाली भयानक वेबसाइट चली गई है :( यदि यह फिर से वापस आती है, तो इसका पिछला पता यह था: http://www.dcs.shef.ac.uk/~sam/simmetrics.html

वोइला ("वेकबैक मशीन" के सौजन्य से): http://web.archive.org/web/20081230184321/http://www.dcs.shef.ac.uk/~sam/simmetrics.html

आप कोड स्रोत का अध्ययन कर सकते हैं, इस प्रकार की तुलना के लिए दर्जनों एल्गोरिदम हैं, प्रत्येक एक अलग व्यापार-बंद के साथ। कार्यान्वयन जावा में हैं।


1

पाठ के एक बड़े सेट को कुशल तरीके से क्वेरी करने के लिए आप एडिट डिस्टेंस / प्रीफिक्स एडिट डिस्टेंस की अवधारणा का उपयोग कर सकते हैं।

दूरी ईडी (x, y) संपादित करें: टर्म x से टर्म y तक प्राप्त करने के लिए ट्रांसफ़ॉर्म की न्यूनतम संख्या

लेकिन प्रत्येक शब्द और क्वेरी पाठ के बीच ईडी की गणना संसाधन और समय गहन है। इसलिए पहले प्रत्येक पद के लिए ईडी की गणना करने के बजाय हम क्यूग्राम इंडेक्स नामक तकनीक का उपयोग करके संभावित मिलान शब्द निकाल सकते हैं। और फिर उन चयनित शर्तों पर ईडी गणना लागू करें।

Qgram इंडेक्स तकनीक का एक फायदा यह फज़ी सर्च के लिए है।

QGram अनुक्रमणिका को अनुकूलित करने के लिए एक संभावित दृष्टिकोण Qgram का उपयोग करके एक औंधा सूचकांक का निर्माण होता है। वहाँ हम उन सभी शब्दों को संग्रहीत करते हैं जिनमें विशेष Qgram होते हैं, उस Qgram के अंतर्गत। (पूर्ण स्ट्रिंग को संग्रहीत करने के बजाय आप प्रत्येक स्ट्रिंग के लिए अद्वितीय ID का उपयोग कर सकते हैं)। आप इसके लिए जावा में ट्री मैप डेटा संरचना का उपयोग कर सकते हैं। शर्तों के भंडारण पर एक छोटा उदाहरण निम्नलिखित है

col: col mbia, col ombo, gan col a, ta col ama

फिर जब क्वेरी करते हैं, तो हम क्वेरी टेक्स्ट और उपलब्ध शर्तों के बीच सामान्य Qgrams की संख्या की गणना करते हैं।

Example: x = HILLARY, y = HILARI(query term)
Qgrams
$$HILLARY$$ -> $$H, $HI, HIL, ILL, LLA, LAR, ARY, RY$, Y$$
$$HILARI$$ -> $$H, $HI, HIL, ILA, LAR, ARI, RI$, I$$
number of q-grams in common = 4

सामान्य संख्या में क्यू-ग्राम की संख्या = 4।

उच्च संख्या में सामान्य Qgram की शर्तों के लिए, हम ED / PED की गणना क्वेरी शब्द के विरुद्ध करते हैं और फिर अंतिम उपयोगकर्ता को यह शब्द सुझाते हैं।

आप निम्नलिखित परियोजना में इस सिद्धांत का कार्यान्वयन पा सकते हैं ("QGramIndex.java" देखें)। आप कोई भी प्रश्न पूछ सकते हैं। https://github.com/Bhashitha-Gamage/City_Search

एडिट डिस्टेंस, प्रीफिक्स एडिट डिस्टेंस क्यूग्राम इंडेक्स के बारे में अधिक अध्ययन करने के लिए, प्रो। डॉ। हन्नाह बॉटम का पूरा वीडियो देखें https://www.youtube.com/embed/6pUg2wmGJRo (पाठ 20:06 से शुरू होता है)


1

यदि इनपुट डेटा बहुत बड़ा है (तो लाखों स्ट्रिंग्स कहें) इसे लागू करना कठिन है। मैंने इसे हल करने के लिए लोचदार खोज का उपयोग किया।

त्वरित शुरुआत: https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/elasticsearch-net.html

बस सभी इनपुट डेटा को डीबी में डालें और आप किसी भी एडिट दूरी के आधार पर किसी भी स्ट्रिंग को जल्दी से खोज सकते हैं। यहाँ एक C # स्निपेट दिया गया है जो आपको एडिट डिस्टेंस द्वारा छांटे गए परिणामों की सूची देगा (छोटे से अधिक)

var res = client.Search<ClassName>(s => s
    .Query(q => q
    .Match(m => m
        .Field(f => f.VariableName)
        .Query("SAMPLE QUERY")
        .Fuzziness(Fuzziness.EditDistance(5))
    )
));

आप किस पुस्तकालय का उपयोग कर रहे हैं? मददगार बनने के लिए कुछ और जानकारी की आवश्यकता है।
बेट

0

यहां आपके पास दिए गए शब्दों के बीच की दूरी की गणना के लिए एक गोलंग POC हो सकता है। आप धुन कर सकते हैं minDistanceऔर differenceअन्य क्षेत्रों के लिए।

खेल का मैदान: https://play.golang.org/p/NtrBzLdC3rE

package main

import (
    "errors"
    "fmt"
    "log"
    "math"
    "strings"
)

var data string = `THE RED COW JUMPED OVER THE GREEN CHICKEN-THE RED COW JUMPED OVER THE RED COW-THE RED FOX JUMPED OVER THE BROWN COW`

const minDistance float64 = 2
const difference float64 = 1

type word struct {
    data    string
    letters map[rune]int
}

type words struct {
    words []word
}

// Print prettify the data present in word
func (w word) Print() {
    var (
        lenght int
        c      int
        i      int
        key    rune
    )
    fmt.Printf("Data: %s\n", w.data)
    lenght = len(w.letters) - 1
    c = 0
    for key, i = range w.letters {
        fmt.Printf("%s:%d", string(key), i)
        if c != lenght {
            fmt.Printf(" | ")
        }
        c++
    }
    fmt.Printf("\n")
}

func (ws words) fuzzySearch(data string) ([]word, error) {
    var (
        w      word
        err    error
        founds []word
    )
    w, err = initWord(data)
    if err != nil {
        log.Printf("Errors: %s\n", err.Error())
        return nil, err
    }
    // Iterating all the words
    for i := range ws.words {
        letters := ws.words[i].letters
        //
        var similar float64 = 0
        // Iterating the letters of the input data
        for key := range w.letters {
            if val, ok := letters[key]; ok {
                if math.Abs(float64(val-w.letters[key])) <= minDistance {
                    similar += float64(val)
                }
            }
        }

        lenSimilarity := math.Abs(similar - float64(len(data)-strings.Count(data, " ")))
        log.Printf("Comparing %s with %s i've found %f similar letter, with weight %f", data, ws.words[i].data, similar, lenSimilarity)
        if lenSimilarity <= difference {
            founds = append(founds, ws.words[i])
        }
    }

    if len(founds) == 0 {
        return nil, errors.New("no similar found for data: " + data)
    }

    return founds, nil
}

func initWords(data []string) []word {
    var (
        err   error
        words []word
        word  word
    )
    for i := range data {
        word, err = initWord(data[i])
        if err != nil {
            log.Printf("Error in index [%d] for data: %s", i, data[i])
        } else {
            words = append(words, word)
        }
    }
    return words

}

func initWord(data string) (word, error) {
    var word word

    word.data = data
    word.letters = make(map[rune]int)
    for _, r := range data {
        if r != 32 { // avoid to save the whitespace
            word.letters[r]++
        }

    }
    return word, nil
}
func main() {
    var ws words
    words := initWords(strings.Split(data, "-"))
    for i := range words {
        words[i].Print()
    }
    ws.words = words

    solution, _ := ws.fuzzySearch("THE BROWN FOX JUMPED OVER THE RED COW")
    fmt.Println("Possible solutions: ", solution)

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.