Сравнение имен - PullRequest
       40

Сравнение имен

2 голосов
/ 16 сентября 2008

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

Если бы в C # был алгоритм, это было бы здорово, но я могу перевести с любого языка.

Ответы [ 7 ]

4 голосов
/ 16 сентября 2008

Похоже, вы ищете фонетические алгоритмы, такие как soundex , NYSIIS или двойной метафон . Первый на самом деле - это , который используют несколько правительственных департаментов, и его тривиально реализовать (многие реализации легко доступны доступны ). Второй - немного более сложный и более точный вариант первого. Последний работает с некоторыми неанглийскими именами и алфавитами.

Расстояние Левенштейна - это определение расстояния между двумя произвольными строками. Он дает вам расстояние 0 между одинаковыми строками и ненулевое между разными строками, что также может быть полезно, если вы решите создать собственный алгоритм.

3 голосов
/ 16 сентября 2008

Левенштейн близко, хотя, возможно, не совсем то, что вы хотите.

2 голосов
/ 16 сентября 2008

Я столкнулся с подобной проблемой и попытался сначала использовать расстояние Левенштейна, но у меня это не получилось. Я придумал алгоритм, который дает вам значение «сходства» между двумя строками (чем выше значение, тем больше одинаковых строк, «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;
    }
}

Расстояние Левенштейна 1 намного проще (адаптировано из 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];
    }
}
0 голосов
/ 16 сентября 2008

В приложении, над которым я работал, поле Фамилия считалось надежным. Таким образом, представлены все все записи с одинаковой фамилией пользователю. Пользователь может сортировать по другим полям для поиска похожих имен. Это решение было достаточно хорошим, чтобы значительно уменьшить проблему пользователей, создающих дубликаты записей.

По сути, похоже, что проблема потребует человеческого суждения.

0 голосов
/ 16 сентября 2008

Второе расстояние до Левенштейна, какой язык вы хотите? Мне удалось довольно легко найти реализацию в C # для codeproject.

0 голосов
/ 16 сентября 2008

Если есть решение этой проблемы, я серьезно сомневаюсь, что это часть ядра C #. Вдобавок ко мне, это потребовало бы базы данных частот первого, среднего и фамилии, а также учет инициалов, как в вашем примере. Это довольно сложная логика, основанная на базе данных.

0 голосов
/ 16 сентября 2008
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...