Алгоритм сравнения слов
Я делаю инструмент импорта CSV для проекта над которым я работаю. Клиент должен иметь возможность вводить данные в excel, экспортировать их в CSV и загружать их в базу данных. Например, у меня есть эта запись CSV:
1, John Doe, ACME Comapny (the typo is on purpose)
конечно, компании хранятся в отдельной таблице и связаны с внешним ключом, поэтому мне нужно найти правильный идентификатор компании перед вставкой. Я планирую сделать это, сравнив названия компаний в базе данных с именами компаний в CSV. этот сравнение должно возвращать 0, если строки точно такие же, и возвращать некоторое значение, которое становится больше, поскольку строки становятся более разными, но strcmp не сокращает его здесь, потому что:
"Acme Company" и "Acme Comapny" должны иметь очень маленький индекс разницы, но "Компания акме "и" СЭВ Мпняко " должны иметь очень большой индекс разницы Или "Акме Компани" и "Акме комп."должен также иметь небольшой индекс разницы, даже если количество символов отличается. Также, "компания Акме" и "Компания Acme" должна вернуть 0.
поэтому, если клиент делает тип при вводе данных, я могу предложить ему выбрать имя, которое он, скорее всего, хотел бы вставить.
есть ли известный алгоритм для этого, или, может быть, мы можем придумать :) ?
7 ответов
вы, возможно, захотите, чтобы проверить Расстояние Левенштейна в качестве отправной точки. Он будет оценивать "расстояние" между двумя словами.
это так нить о реализации стиля Google " вы имеете в виду...?- система может дать и некоторые идеи.
Я не знаю, на каком языке вы пишете, но если это PHP, вы должны рассмотреть следующие алгоритмы:
Левенштейна(): возвращает минимальное количество символов необходимо заменить, вставить или Удалить для преобразования одной строки в другую.
soundex (): возвращает четырехсимвольный ключ soundex слова, который должен быть таким же, как ключ для любого аналогичного звучания слово.
метафоном(): подобно soundex, и, возможно, более эффективным для вас. Это более точно, чем soundex (), поскольку он знает основные правила английского произношения. В метафон сгенерированные ключи переменной длины.
similar_text (): аналогично levenshtein(), но вместо этого он может возвращать процентное значение.
У меня был некоторый успех с Расстояние Левенштейна, есть еще Soundex.
на каком языке вы исполняете это? мы можем указать на конкретные примеры
Я фактически реализовал аналогичную систему. Я использовал расстояние Левенштейна (как уже предлагали другие плакаты) с некоторыми изменениями. Проблема с немодифицированным расстоянием редактирования (применительно к целым строкам) заключается в том, что оно чувствительно к переупорядочению слов, поэтому "Acme Digital Incorporated World Company" будет плохо соответствовать "Digital Incorporated World Company Acme", и такие переупорядочения были довольно распространены в моих данных.
Я изменил его так, что если изменить расстояние в целом строки были слишком большими, алгоритм вернулся к сопоставлению слов друг с другом, чтобы найти хорошее совпадение слово в слово (квадратичная стоимость, но был отсек, если было слишком много слов, поэтому он работал нормально).
Я взял SoundEx, Levenshtein, PHP similarity и double metaphone и упаковал их в C# в один набор методов расширения на String.
существует несколько алгоритмов для этого, и большинство баз данных даже включают один по умолчанию. На самом деле это довольно распространенная проблема.
Если его только о английских словах, SQL Server, например, включает SOUNDEX, который может использоваться для сравнения на результирующий звук слова.
http://msdn.microsoft.com/en-us/library/aa259235%28SQL.80%29.aspx
я реализую его в PHP, и теперь я пишу фрагмент кода, который разбивает 2 строки на слова и сравнивает каждое из слов из первой строки со словами второй строки с помощью levenshtein и принимает возможные значения lowes. Я отправлю его, когда закончу.
Спасибо большое.
Update: вот что я придумал:
function myLevenshtein( $str1, $str2 )
{
// prepare the words
$words1 = explode( " ", preg_replace( "/\s+/", " ", trim($str1) ) );
$words2 = explode( " ", preg_replace( "/\s+/", " ", trim($str2) ) );
$found = array(); // array that keeps the best matched words so we don't check them again
$score = 0; // total score
// In my case, strings that have different amount of words can be good matches too
// For example, Acme Company and International Acme Company Ltd. are the same thing
// I will just add the wordcount differencre to the total score, and weigh it more later if needed
$wordDiff = count( $words1 ) - count( $words2 );
foreach( $words1 as $word1 )
{
$minlevWord = "";
$minlev = 1000;
$return = 0;
foreach( $words2 as $word2 )
{
$return = 1;
if( in_array( $word2, $found ) )
continue;
$lev = levenshtein( $word1, $word2 );
if( $lev < $minlev )
{
$minlev = $lev;
$minlevWord = $word2;
}
}
if( !$return )
break;
$score += $minlev;
array_push( $found, $minlevWord );
}
return $score + $wordDiff;
}