مكتبة تطبق تشابه سلسلة مختلفة ومقاييس المسافة. يتم حاليًا تنفيذ عشرات من الخوارزميات (بما في ذلك مسافة تحرير Levenshtein والأشقاء، وJaro-Winkler، وأطول تسلسل مشترك، وتشابه جيب التمام، وما إلى ذلك). تحقق من الجدول الملخص أدناه للحصول على القائمة الكاملة...
باستخدام مخضرم:
<dependency>
<groupId>info.debatty</groupId>
<artifactId>java-string-similarity</artifactId>
<version>RELEASE</version>
</dependency>
أو التحقق من الإصدارات.
تتطلب هذه المكتبة Java 8 أو أحدث.
يتم عرض الخصائص الرئيسية لكل خوارزمية تم تنفيذها أدناه. يعطي عمود "التكلفة" تقديرًا للتكلفة الحسابية لحساب التشابه بين سلسلتين بطول m وn على التوالي.
تطبيع؟ | متري؟ | يكتب | يكلف | الاستخدام النموذجي | ||
---|---|---|---|---|---|---|
ليفنشتاين | مسافة | لا | نعم | يا (م * ن) 1 | ||
تطبيع ليفنشتاين | مسافة تشابه | نعم | لا | يا (م * ن) 1 | ||
ليفنشتاين المرجح | مسافة | لا | لا | يا (م * ن) 1 | التعرف الضوئي على الحروف | |
داميرو-ليفنشتاين 3 | مسافة | لا | نعم | يا (م * ن) 1 | ||
محاذاة السلسلة الأمثل 3 | مسافة | لا | لا | يا (م * ن) 1 | ||
جارو وينكلر | تشابه مسافة | نعم | لا | يا (م * ن) | تصحيح الخطأ المطبعي | |
أطول لاحقة مشتركة | مسافة | لا | لا | يا(م*ن) 1,2 | فائدة الفرق، تسوية GIT | |
متري أطول لاحقة مشتركة | مسافة | نعم | نعم | يا(م*ن) 1,2 | ||
ن-جرام | مسافة | نعم | لا | يا (م * ن) | ||
كيو جرام | مسافة | لا | لا | حساب تعريفي | يا (م + ن) | |
تشابه جيب التمام | تشابه مسافة | نعم | لا | حساب تعريفي | يا (م + ن) | |
مؤشر جاكارد | تشابه مسافة | نعم | نعم | تعيين | يا (م + ن) | |
معامل سورنسن-النرد | تشابه مسافة | نعم | لا | تعيين | يا (م + ن) | |
راتكليف-أوبرشيلب | تشابه مسافة | نعم | لا | ؟ |
[1] في هذه المكتبة، يتم حساب مسافة تحرير Levenshtein ومسافة LCS وأشقائهم باستخدام طريقة البرمجة الديناميكية ، والتي تبلغ تكلفتها O(mn). بالنسبة لمسافة ليفنشتاين، تسمى الخوارزمية أحيانًا خوارزمية فاغنر-فيشر ("مشكلة تصحيح السلسلة إلى السلسلة"، 1974). تستخدم الخوارزمية الأصلية مصفوفة بالحجم mxn لتخزين مسافة Levenshtein بين بادئات السلسلة.
إذا كانت الأبجدية محدودة، فمن الممكن استخدام طريقة الروس الأربعة (أرلازاروف وآخرون "حول البناء الاقتصادي للإغلاق المتعدي للرسم البياني الموجه"، 1970) لتسريع الحساب. تم نشر هذا بواسطة Masek في عام 1980 ("خوارزمية أسرع لحوسبة مسافات تحرير السلسلة"). تقوم هذه الطريقة بتقسيم المصفوفة إلى كتل بحجم tx t. يتم حساب كل كتلة محتملة مسبقًا لإنتاج جدول بحث. يمكن بعد ذلك استخدام جدول البحث هذا لحساب تشابه السلسلة (أو المسافة) في O(nm/t). عادةً، يتم اختيار t كـ log(m) إذا كان m > n. وبالتالي فإن تكلفة الحساب الناتجة هي O(mn/log(m)). لم يتم تنفيذ هذه الطريقة (حتى الآن).
[2] في "طول الحد الأقصى للتبعات المشتركة"، اقترح KS Larsen خوارزمية تحسب طول LCS في الوقت O(log(m).log(n)). لكن الخوارزمية تتطلب ذاكرة O(m.n²) وبالتالي لم يتم تنفيذها هنا.
[3] هناك نوعان مختلفان من مسافة سلسلة Damerau-Levenshtein: Damerau-Levenshtein مع التحويلات المجاورة (وتسمى أيضًا أحيانًا مسافة Damerau-Levenshtein غير المقيدة) ومحاذاة السلسلة المثالية (وتسمى أيضًا أحيانًا مسافة التحرير المقيدة). بالنسبة لمحاذاة السلسلة المثالية، لا يمكن تحرير أي سلسلة فرعية أكثر من مرة.
على الرغم من أن الموضوع قد يبدو بسيطًا، إلا أنه توجد الكثير من الخوارزميات المختلفة لقياس تشابه النص أو المسافة. ولذلك تحدد المكتبة بعض الواجهات لتصنيفها.
بشكل عام، الخوارزميات التي تنفذ NormalizedStringSimilarity تنفذ أيضًا NormalizedStringDistance، والتشابه = 1 - المسافة. ولكن هناك بعض الاستثناءات، مثل التشابه والمسافة N-Gram (الكوندراك)...
واجهة MetricStringDistance: بعض المسافات هي في الواقع مسافات مترية، مما يعني التحقق من عدم مساواة المثلث d(x, y) <= d(x,z) + d(z,y). على سبيل المثال، Levenshtein عبارة عن مسافة مترية، لكن NormalizedLevenshtein ليست كذلك.
تعتمد الكثير من خوارزميات البحث وهياكل الفهرسة في الجوار الأقرب على عدم مساواة المثلث. يمكنك التحقق من "بحث التشابه، نهج الفضاء المتري" بواسطة Zezula et al. للمسح. ولا يمكن استخدامها مع مقاييس التشابه غير المترية.
اقرأ Javadoc للحصول على وصف تفصيلي
تعمل بعض الخوارزميات عن طريق تحويل السلاسل إلى مجموعات من n-grams (تسلسلات من n من الأحرف، وتسمى أيضًا أحيانًا k-shingles). التشابه أو المسافة بين السلاسل هو التشابه أو المسافة بين المجموعات.
البعض منهم، مثل الجاكار، يعتبرون الخيوط بمثابة مجموعات من القوباء المنطقية، ولا يأخذون في الاعتبار عدد مرات ظهور كل لوح خشبي. البعض الآخر، مثل تشابه جيب التمام، يعمل باستخدام ما يسمى أحيانًا ملف تعريف السلاسل، والذي يأخذ في الاعتبار عدد مرات ظهور كل لوح خشبي.
بالنسبة لهذه الخوارزميات، هناك حالة استخدام أخرى ممكنة عند التعامل مع مجموعات البيانات الكبيرة:
مسافة Levenshtein بين كلمتين هي الحد الأدنى لعدد تعديلات الحرف الواحد (الإدراج أو الحذف أو الاستبدال) المطلوبة لتغيير كلمة إلى أخرى.
إنها مسافة سلسلة مترية. يستخدم هذا التنفيذ البرمجة الديناميكية (خوارزمية فاغنر-فيشر)، مع صفين فقط من البيانات. وبالتالي فإن متطلبات المساحة هي O(m) وتعمل الخوارزمية في O(mn).
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
Levenshtein l = new Levenshtein ();
System . out . println ( l . distance ( "My string" , "My $tring" ));
System . out . println ( l . distance ( "My string" , "My $tring" ));
System . out . println ( l . distance ( "My string" , "My $tring" ));
}
}
يتم حساب هذه المسافة على أنها مسافة ليفنشتاين مقسومة على طول أطول سلسلة. القيمة الناتجة تكون دائمًا في الفاصل الزمني [0.0 1.0] ولكنها لم تعد مقياسًا بعد الآن!
يتم حساب التشابه على أنه 1 - المسافة الطبيعية.
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
NormalizedLevenshtein l = new NormalizedLevenshtein ();
System . out . println ( l . distance ( "My string" , "My $tring" ));
System . out . println ( l . distance ( "My string" , "My $tring" ));
System . out . println ( l . distance ( "My string" , "My $tring" ));
}
}
تطبيق Levenshtein الذي يسمح بتحديد أوزان مختلفة لبدائل الأحرف المختلفة.
تُستخدم هذه الخوارزمية عادةً لتطبيقات التعرف البصري على الأحرف (OCR). بالنسبة لـ OCR، فإن تكلفة استبدال P وR أقل من تكلفة استبدال P وM على سبيل المثال لأنه من وجهة نظر OCR P تشبه R.
يمكن استخدامه أيضًا للتصحيح التلقائي لكتابة لوحة المفاتيح. هنا تكون تكلفة استبدال E وR أقل على سبيل المثال لأنها تقع بجوار بعضها البعض على لوحة مفاتيح AZERTY أو QWERTY. ومن ثم فإن احتمال أن يكون المستخدم قد أخطأ في كتابة الأحرف أعلى.
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
WeightedLevenshtein wl = new WeightedLevenshtein (
new CharacterSubstitutionInterface () {
public double cost ( char c1 , char c2 ) {
// The cost for substituting 't' and 'r' is considered
// smaller as these 2 are located next to each other
// on a keyboard
if ( c1 == 't' && c2 == 'r' ) {
return 0.5 ;
}
// For most cases, the cost of substituting 2 characters
// is 1.0
return 1.0 ;
}
});
System . out . println ( wl . distance ( "String1" , "Srring2" ));
}
}
على غرار Levenshtein، فإن مسافة Damerau-Levenshtein مع التحويل (وتسمى أحيانًا مسافة Damerau-Levenshtein غير المقيدة) هي الحد الأدنى لعدد العمليات اللازمة لتحويل سلسلة واحدة إلى أخرى، حيث يتم تعريف العملية على أنها إدراج أو حذف أو استبدال لسلسلة. حرف واحد، أو تبديل حرفين متجاورين .
إنه يحترم عدم مساواة المثلث، وبالتالي فهو مسافة مترية.
لا ينبغي الخلط بين هذا وبين مسافة محاذاة السلسلة المثالية، وهي امتداد حيث لا يمكن تحرير أي سلسلة فرعية أكثر من مرة.
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
Damerau d = new Damerau ();
// 1 substitution
System . out . println ( d . distance ( "ABCDEF" , "ABDCEF" ));
// 2 substitutions
System . out . println ( d . distance ( "ABCDEF" , "BACDFE" ));
// 1 deletion
System . out . println ( d . distance ( "ABCDEF" , "ABCDE" ));
System . out . println ( d . distance ( "ABCDEF" , "BCDEF" ));
System . out . println ( d . distance ( "ABCDEF" , "ABCGDEF" ));
// All different
System . out . println ( d . distance ( "ABCDEF" , "POIU" ));
}
}
سوف تنتج:
1.0
2.0
1.0
1.0
1.0
6.0
يحسب متغير محاذاة السلسلة الأمثل لـ Damerau–Levenshtein (يُسمى أحيانًا مسافة التحرير المقيدة) عدد عمليات التحرير اللازمة لجعل السلاسل متساوية بشرط عدم تحرير أي سلسلة فرعية أكثر من مرة ، في حين أن Damerau–Levenshtein الحقيقي لا يقدم مثل هذا تقييد. الفرق عن خوارزمية مسافة Levenshtein هو إضافة تكرار واحد لعمليات النقل.
لاحظ أنه بالنسبة لمسافة محاذاة السلسلة المثالية، فإن عدم مساواة المثلث لا يستمر وبالتالي فهو ليس مقياسًا حقيقيًا.
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
OptimalStringAlignment osa = new OptimalStringAlignment ();
System . out . println ( osa . distance ( "CA" , "ABC" ));;
}
}
سوف تنتج:
3.0
Jaro-Winkler هي مسافة تحرير سلسلة تم تطويرها في مجال ربط السجلات (اكتشاف التكرارات) (Winkler، 1990). تم تصميم مقياس المسافة Jaro – Winkler وهو مناسب بشكل أفضل للسلاسل القصيرة مثل أسماء الأشخاص، ولاكتشاف الأخطاء المطبعية في النقل.
يحسب Jaro-Winkler التشابه بين سلسلتين، وتقع القيمة التي تم إرجاعها في الفاصل الزمني [0.0، 1.0]. إنه (تقريبًا) شكل مختلف من Damerau-Levenshtein، حيث يعتبر تبديل حرفين قريبين أقل أهمية من تبديل حرفين بعيدين عن بعضهما البعض. يعاقب جارو وينكلر على الإضافات أو التبديلات التي لا يمكن التعبير عنها على أنها تحويلات.
يتم حساب المسافة على أنها 1 - تشابه جارو وينكلر.
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
JaroWinkler jw = new JaroWinkler ();
// substitution of s and t
System . out . println ( jw . similarity ( "My string" , "My tsring" ));
// substitution of s and n
System . out . println ( jw . similarity ( "My string" , "My ntrisg" ));
}
}
سوف تنتج:
0.9740740656852722
0.8962963223457336
تتمثل مشكلة أطول تسلسل فرعي مشترك (LCS) في العثور على أطول تسلسل فرعي مشترك بين تسلسلين (أو أكثر). وهو يختلف عن مشاكل العثور على سلاسل فرعية مشتركة: على عكس السلاسل الفرعية، لا يلزم أن تشغل التسلسلات الفرعية مواقع متتالية داخل التسلسلات الأصلية.
يتم استخدامه بواسطة الأداة المساعدة diff، بواسطة Git للتوفيق بين التغييرات المتعددة، وما إلى ذلك.
مسافة LCS بين السلاسل X (من الطول n) وY (من الطول m) هي n + m - 2 |LCS(X, Y)| الحد الأدنى = 0 الحد الأقصى = ن + م
مسافة LCS تعادل مسافة Levenshtein عندما يُسمح فقط بالإدراج والحذف (بدون استبدال)، أو عندما تكون تكلفة الاستبدال ضعف تكلفة الإدراج أو الحذف.
تطبق هذه الفئة منهج البرمجة الديناميكية، الذي يتطلب مساحة O(mn)، وتكلفة حسابية O(mn).
في "طول الحد الأقصى للتبعات المشتركة"، اقترح KS Larsen خوارزمية تحسب طول LCS في الوقت O(log(m).log(n)). لكن الخوارزمية تتطلب ذاكرة O(m.n²) وبالتالي لم يتم تنفيذها هنا.
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
LongestCommonSubsequence lcs = new LongestCommonSubsequence ();
// Will produce 4.0
System . out . println ( lcs . distance ( "AGCAT" , "GAC" ));
// Will produce 1.0
System . out . println ( lcs . distance ( "AGCAT" , "AGCT" ));
}
}
مقياس المسافة بناءً على أطول تسلسل مشترك، من ملاحظات "قياس السلسلة المستند إلى LCS" بقلم دانييل باكيلوند. http://heim.ifi.uio.no/~danielry/StringMetric.pdf
يتم حساب المسافة بالقيمة 1 - |LCS(s1, s2)| / الحد الأقصى(|s1|, |s2|)
public class MyApp {
public static void main ( String [] args ) {
info . debatty . java . stringsimilarity . MetricLCS lcs =
new info . debatty . java . stringsimilarity . MetricLCS ();
String s1 = "ABCDEFG" ;
String s2 = "ABCDEFHJKL" ;
// LCS: ABCDEF => length = 6
// longest = s2 => length = 10
// => 1 - 6/10 = 0.4
System . out . println ( lcs . distance ( s1 , s2 ));
// LCS: ABDF => length = 4
// longest = ABDEF => length = 5
// => 1 - 4 / 5 = 0.2
System . out . println ( lcs . distance ( "ABDEF" , "ABDIF" ));
}
}
مسافة N-Gram المعيارية كما حددها Kondrak، "تشابه N-Gram والمسافة"، معالجة السلسلة واسترجاع المعلومات، ملاحظات المحاضرة في علوم الكمبيوتر المجلد 3772، 2005، الصفحات من 115 إلى 126.
http://webdocs.cs.ualberta.ca/~kondrak/papers/spire05.pdf
تستخدم الخوارزمية إضافة حرف خاص 'n' لزيادة وزن الأحرف الأولى. يتم تحقيق التطبيع عن طريق قسمة مجموع نقاط التشابه على الطول الأصلي لأطول كلمة.
في الورقة، يحدد كوندراك أيضًا مقياس التشابه، والذي لم يتم تنفيذه (بعد).
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
// produces 0.583333
NGram twogram = new NGram ( 2 );
System . out . println ( twogram . distance ( "ABCD" , "ABTUIO" ));
// produces 0.97222
String s1 = "Adobe CreativeSuite 5 Master Collection from cheap 4zp" ;
String s2 = "Adobe CreativeSuite 5 Master Collection from cheap d1x" ;
NGram ngram = new NGram ( 4 );
System . out . println ( ngram . distance ( s1 , s2 ));
}
}
تعمل بعض الخوارزميات عن طريق تحويل السلاسل إلى مجموعات من n-grams (تسلسلات من n من الأحرف، وتسمى أيضًا أحيانًا k-shingles). التشابه أو المسافة بين السلاسل هو التشابه أو المسافة بين المجموعات.
يتم التحكم في تكلفة حساب أوجه التشابه والمسافات هذه بشكل أساسي بواسطة k-shingling (تحويل السلاسل إلى تسلسلات من أحرف k). لذلك هناك عادةً حالتان لاستخدام هذه الخوارزميات:
حساب المسافة بين السلاسل مباشرة:
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
QGram dig = new QGram ( 2 );
// AB BC CD CE
// 1 1 1 0
// 1 1 0 1
// Total: 2
System . out . println ( dig . distance ( "ABCD" , "ABCE" ));
}
}
أو، بالنسبة لمجموعات البيانات الكبيرة، قم بحساب ملف تعريف جميع السلاسل مسبقًا. يمكن بعد ذلك حساب التشابه بين الملفات الشخصية:
import info . debatty . java . stringsimilarity . KShingling ;
import info . debatty . java . stringsimilarity . StringProfile ;
/**
* Example of computing cosine similarity with pre-computed profiles.
*/
public class PrecomputedCosine {
public static void main ( String [] args ) throws Exception {
String s1 = "My first string" ;
String s2 = "My other string..." ;
// Let's work with sequences of 2 characters...
Cosine cosine = new Cosine ( 2 );
// Pre-compute the profile of strings
Map < String , Integer > profile1 = cosine . getProfile ( s1 );
Map < String , Integer > profile2 = cosine . getProfile ( s2 );
// Prints 0.516185
System . out . println ( cosine . similarity ( profile1 , profile2 ));
}
}
انتبه، هذا يعمل فقط إذا تم استخدام نفس كائن KShingling لتحليل جميع سلاسل الإدخال!
مسافة Q-gram، كما حددها Ukkonen في "المطابقة التقريبية للسلسلة مع Q-grams والتطابقات القصوى" http://www.sciencedirect.com/science/article/pii/0304397592901434
يتم تعريف المسافة بين سلسلتين على أنها معيار L1 للاختلاف في ملفات التعريف الخاصة بهما (عدد تكرارات كل n-gram): SUM( |V1_i - V2_i| ). مسافة Q-gram هي الحد الأدنى لمسافة Levenshtein، ولكن يمكن حسابها بـ O(m + n)، حيث يتطلب Levenshtein O(mn)
التشابه بين السلسلتين هو جيب تمام الزاوية بين تمثيل هذين المتجهين، ويتم حسابه على أنه V1 . V2 / (|V1| * |V2|)
يتم حساب المسافة على أنها 1 - تشابه جيب التمام.
مثل مسافة Q-Gram، يتم تحويل سلاسل الإدخال أولاً إلى مجموعات من n-gram (تسلسلات من n من الأحرف، وتسمى أيضًا k-shingles)، ولكن هذه المرة لا يتم أخذ أصل كل n-gram في الاعتبار. كل سلسلة إدخال هي ببساطة مجموعة من n-grams. يتم بعد ذلك حساب مؤشر Jaccard كـ |V1 inter V2| / |V1 الاتحاد V2|.
يتم حساب المسافة على أنها 1 - التشابه. مؤشر Jaccard هو مسافة مترية.
مشابه لمؤشر Jaccard، ولكن هذه المرة يتم حساب التشابه كـ 2 * |V1 inter V2| / (|V1| + |V2|).
يتم حساب المسافة على أنها 1 - التشابه.
يعد التعرف على أنماط Ratcliff/Obershelp، والمعروف أيضًا باسم مطابقة أنماط الجشطالت، خوارزمية مطابقة للسلسلة لتحديد التشابه بين سلسلتين. تم تطويره في عام 1983 من قبل جون دبليو راتكليف وجون أ. أوبرشيلب ونشر في مجلة دكتور دوب في يوليو 1988.
يحسب Ratcliff/Obershelp التشابه بين سلسلتين، وتقع القيمة التي تم إرجاعها في الفاصل الزمني [0.0، 1.0].
يتم حساب المسافة على أنها 1 - تشابه Ratcliff/Obershelp.
import info . debatty . java . stringsimilarity .*;
public class MyApp {
public static void main ( String [] args ) {
RatcliffObershelp ro = new RatcliffObershelp ();
// substitution of s and t
System . out . println ( ro . similarity ( "My string" , "My tsring" ));
// substitution of s and n
System . out . println ( ro . similarity ( "My string" , "My ntrisg" ));
}
}
سوف تنتج:
0.8888888888888888
0.7777777777777778
SIFT4 هي خوارزمية مسافة سلسلة للأغراض العامة مستوحاة من JaroWinkler وLongest Common Subsequence. لقد تم تطويره لإنتاج مقياس للمسافة يتطابق قدر الإمكان مع الإدراك البشري لمسافة السلسلة. ومن ثم فهو يأخذ في الاعتبار عناصر مثل استبدال الأحرف، ومسافة الأحرف، وأطول تسلسل مشترك وما إلى ذلك. وقد تم تطويره باستخدام الاختبار التجريبي، وبدون خلفية نظرية.
import info.debatty.java.stringsimilarity.experimental.Sift4;
public class MyApp {
public static void main(String[] args) {
String s1 = "This is the first string";
String s2 = "And this is another string";
Sift4 sift4 = new Sift4();
sift4.setMaxOffset(5);
double expResult = 11.0;
double result = sift4.distance(s1, s2);
assertEquals(expResult, result, 0.0);
}
}
استخدم تشابه سلسلة Java في مشروعك وتريد أن يتم ذكره هنا؟ لا تتردد في مراسلتي!