Есть ли какой-либо простой алгоритм для определения вероятности того, что 2 имени представляют одного и того же человека?
Я не прошу чего-то такого уровня, который мог бы использовать таможенный отдел. Просто простой алгоритм, который скажет мне, что имя Джеймса Т. Кларка, скорее всего, совпадает с именем Дж. Томас Кларк »или« Джеймс Клерк ».
Если бы в C# был алгоритм, это было бы здорово, но я могу переводить с любого языка.





Левенштейн близко, хотя, возможно, не совсем то, что вам нужно.
Я сомневаюсь, что есть, учитывая даже Таможенный департамент, похоже, не нашел удовлетворительного ответа ...
Если есть решение этой проблемы, я серьезно сомневаюсь, что это часть ядра C#. Сверху моей головы потребуется база данных частот имен, отчеств и фамилий, а также учет инициалов, как в вашем примере. Это довольно сложная логика, основанная на базе данных.
Второй после расстояния Левенштейна, какой язык вы хотите? Мне довольно легко удалось найти реализацию на C# в кодовом проекте.
В приложении, над которым я работал, поле Фамилия считалось надежным. Итак, представил пользователю все записи с одинаковой фамилией. Пользователь мог сортировать по другим полям, чтобы найти похожие имена. Это решение было достаточно хорошим, чтобы значительно снизить количество пользователей, создающих повторяющиеся записи.
В основном похоже, что проблема потребует человеческого суждения.
Похоже, вы ищете фонетические алгоритмы, такие как soundex, NYSIIS или двойной метафон. Первый на самом деле является, который используют несколько правительственных ведомств, и его легко реализовать (многие реализации легко имеется в наличии). Второй - немного более сложный и точный вариант первого. Последний работает с некоторыми неанглийскими именами и алфавитами.
Расстояние Левенштейна - это определение расстояния между двумя произвольными строками. Он дает вам расстояние 0 между идентичными строками и ненулевое значение между разными строками, что также может быть полезно, если вы решите создать собственный алгоритм.
Я столкнулся с подобной проблемой и сначала попытался использовать расстояние Левенштейна, но у меня это не сработало. Я придумал алгоритм, который дает вам значение «сходства» между двумя строками (более высокое значение означает большее количество похожих строк, «1» для идентичных строк). Это значение само по себе не имеет большого значения (если не «1», всегда 0,5 или меньше), но работает достаточно хорошо, когда вы добавляете венгерскую матрицу для поиска совпадающих пар из двух списков строк.
Используйте так:
PartialStringComparer cmp = new PartialStringComparer();
tbResult.Text = cmp.Compare(textBox1.Text, textBox2.Text).ToString();
Код позади:
public class SubstringRange {
string masterString;
public string MasterString {
get { return masterString; }
set { masterString = value; }
}
int start;
public int Start {
get { return start; }
set { start = value; }
}
int end;
public int End {
get { return end; }
set { end = value; }
}
public int Length {
get { return End - Start; }
set { End = Start + value;}
}
public bool IsValid {
get { return MasterString.Length >= End && End >= Start && Start >= 0; }
}
public string Contents {
get {
if (IsValid) {
return MasterString.Substring(Start, Length);
} else {
return "";
}
}
}
public bool OverlapsRange(SubstringRange range) {
return !(End < range.Start || Start > range.End);
}
public bool ContainsRange(SubstringRange range) {
return range.Start >= Start && range.End <= End;
}
public bool ExpandTo(string newContents) {
if (MasterString.Substring(Start).StartsWith(newContents, StringComparison.InvariantCultureIgnoreCase) && newContents.Length > Length) {
Length = newContents.Length;
return true;
} else {
return false;
}
}
}
public class SubstringRangeList: List<SubstringRange> {
string masterString;
public string MasterString {
get { return masterString; }
set { masterString = value; }
}
public SubstringRangeList(string masterString) {
this.MasterString = masterString;
}
public SubstringRange FindString(string s){
foreach(SubstringRange r in this){
if (r.Contents.Equals(s, StringComparison.InvariantCultureIgnoreCase))
return r;
}
return null;
}
public SubstringRange FindSubstring(string s){
foreach(SubstringRange r in this){
if (r.Contents.StartsWith(s, StringComparison.InvariantCultureIgnoreCase))
return r;
}
return null;
}
public bool ContainsRange(SubstringRange range) {
foreach(SubstringRange r in this) {
if (r.ContainsRange(range))
return true;
}
return false;
}
public bool AddSubstring(string substring) {
bool result = false;
foreach(SubstringRange r in this) {
if (r.ExpandTo(substring)) {
result = true;
}
}
if (FindSubstring(substring) == null) {
bool patternfound = true;
int start = 0;
while(patternfound){
patternfound = false;
start = MasterString.IndexOf(substring, start, StringComparison.InvariantCultureIgnoreCase);
patternfound = start != -1;
if (patternfound) {
SubstringRange r = new SubstringRange();
r.MasterString = this.MasterString;
r.Start = start++;
r.Length = substring.Length;
if (!ContainsRange(r)) {
this.Add(r);
result = true;
}
}
}
}
return result;
}
private static bool SubstringRangeMoreThanOneChar(SubstringRange range) {
return range.Length > 1;
}
public float Weight {
get {
if (MasterString.Length == 0 || Count == 0)
return 0;
float numerator = 0;
int denominator = 0;
foreach(SubstringRange r in this.FindAll(SubstringRangeMoreThanOneChar)) {
numerator += r.Length;
denominator++;
}
if (denominator == 0)
return 0;
return numerator / denominator / MasterString.Length;
}
}
public void RemoveOverlappingRanges() {
SubstringRangeList l = new SubstringRangeList(this.MasterString);
l.AddRange(this);//create a copy of this list
foreach(SubstringRange r in l) {
if (this.Contains(r) && this.ContainsRange(r)) {
Remove(r);//try to remove the range
if (!ContainsRange(r)) {//see if the list still contains "superset" of this range
Add(r);//if not, add it back
}
}
}
}
public void AddStringToCompare(string s) {
for(int start = 0; start < s.Length; start++) {
for(int len = 1; start + len <= s.Length; len++) {
string part = s.Substring(start, len);
if (!AddSubstring(part))
break;
}
}
RemoveOverlappingRanges();
}
}
public class PartialStringComparer {
public float Compare(string s1, string s2) {
SubstringRangeList srl1 = new SubstringRangeList(s1);
srl1.AddStringToCompare(s2);
SubstringRangeList srl2 = new SubstringRangeList(s2);
srl2.AddStringToCompare(s1);
return (srl1.Weight + srl2.Weight) / 2;
}
}
Расстояние Левенштейна намного проще (адаптировано из http://www.merriampark.com/ld.htm):
public class Distance {
/// <summary>
/// Compute Levenshtein distance
/// </summary>
/// <param name = "s">String 1</param>
/// <param name = "t">String 2</param>
/// <returns>Distance between the two strings.
/// The larger the number, the bigger the difference.
/// </returns>
public static int LD(string s, string t) {
int n = s.Length; //length of s
int m = t.Length; //length of t
int[,] d = new int[n + 1, m + 1]; // matrix
int cost; // cost
// Step 1
if (n == 0) return m;
if (m == 0) return n;
// Step 2
for(int i = 0; i <= n; d[i, 0] = i++) ;
for(int j = 0; j <= m; d[0, j] = j++) ;
// Step 3
for(int i = 1; i <= n; i++) {
//Step 4
for(int j = 1; j <= m; j++) {
// Step 5
cost = (t.Substring(j - 1, 1) == s.Substring(i - 1, 1) ? 0 : 1);
// Step 6
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);
}
}
// Step 7
return d[n, m];
}
}