Лучший способ обнаружить похожие адреса электронной почты?

У меня есть список примерно из 20 000 адресов электронной почты, некоторые из которых, как я знаю, являются мошенническими попытками обойти ограничение «1 на адрес электронной почты», например, [email protected], [email protected], username1b@gmail. com и т. д. Я хочу найти похожие адреса электронной почты для оценки. В настоящее время я использую алгоритм Левенштейна, чтобы сверять каждое электронное письмо с другими в списке и сообщать о любом с расстоянием редактирования менее 2. Однако это очень медленно. Есть ли более эффективный подход?

Тестовый код, который я сейчас использую:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

namespace LevenshteinAnalyzer
{
    class Program
    {
        const string INPUT_FILE = @"C:\Input.txt";
        const string OUTPUT_FILE = @"C:\Output.txt";

        static void Main(string[] args)
        {
            var inputWords = File.ReadAllLines(INPUT_FILE);
            var outputWords = new SortedSet<string>();

            for (var i = 0; i < inputWords.Length; i++)
            {
                if (i % 100 == 0) 
                    Console.WriteLine("Processing record #" + i);

                var word1 = inputWords[i].ToLower();
                for (var n = i + 1; n < inputWords.Length; n++)
                {
                    if (i == n) continue;
                    var word2 = inputWords[n].ToLower();

                    if (word1 == word2) continue;
                    if (outputWords.Contains(word1)) continue;
                    if (outputWords.Contains(word2)) continue;
                    var distance = LevenshteinAlgorithm.Compute(word1, word2);

                    if (distance <= 2)
                    {
                        outputWords.Add(word1);
                        outputWords.Add(word2);
                    }
                }
            }

            File.WriteAllLines(OUTPUT_FILE, outputWords.ToArray());
            Console.WriteLine("Found {0} words", outputWords.Count);
        }
    }
}

Редактировать: кое-что из того, что я пытаюсь уловить, выглядит так:

[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
01234@ gmail.com
[email protected]
[email protected]


person Chris    schedule 11.05.2010    source источник
comment
Что произойдет, если у вас есть два одинаковых электронных письма, которые принадлежат двум разным людям?   -  person JYelton    schedule 11.05.2010
comment
Вот почему я предоставляю список человеку для оценки после...   -  person Chris    schedule 11.05.2010
comment
Если вы запускаете код только тогда, когда люди пытаются создать новую учетную запись, это будет стоить вам O(n), а не O(n^2).   -  person Travis Gockel    schedule 11.05.2010
comment
@Travis, к сожалению, это нужно делать постфактум, а не в режиме реального времени.   -  person Chris    schedule 11.05.2010
comment
Не беспокойтесь. Как только пользователи поймут, что вы ищете похожие адреса, они создадут другие. Вы можете получить миллион от mailinator.com (и многих псевдонимов, которые он поддерживает).   -  person Samuel Neff    schedule 11.05.2010
comment
Я знаю, что это не тот ответ, который вы ищете, но мне было любопытно, можете ли вы также использовать их IP-адрес, чтобы сузить круг записей для сравнения. Я думаю, что кто-то, отправляющий несколько запросов, вероятно, будет делать все одновременно с одной и той же машины (IP).   -  person Zachary    schedule 11.05.2010
comment
Вы должны быть осторожны с такими вещами, как Gmail. Все следующие адреса относятся к одному и тому же почтовому ящику: [email protected] [email protected] [email protected] [email protected]   -  person YotaXP    schedule 11.05.2010
comment
Согласитесь с Сэмом, вы мало что можете с этим поделать. Я могу зарегистрировать [email protected] и [email protected], но ваша система их не получит. Лучше забанить все адреса mailinator (@mailinator.com + еще 10 или около того доменов, которые они предоставляют...) и, возможно, адреса slopsbox тоже (они предоставляют добрых 100 разных доменов, так что это немного дольше). что вы будете блокировать законных пользователей, например. -не такой уж странный- сценарий, где [email protected] действительно отличается от [email protected] Даже если вы передадите список человеку впоследствии, как он может отличить?   -  person nico    schedule 11.05.2010
comment
Эй, ребята, спасибо за вклад, правда, но я не ищу 100% решение. Мы просто пытаемся поймать идиотов, которые просто изменяют свои адреса электронной почты одной буквой или цифрой, чтобы получить несколько записей в системе. Если кто-то хочет пройти через время, чтобы фактически создать несколько значительно различающихся адресов электронной почты, у них больше возможностей.   -  person Chris    schedule 11.05.2010
comment
Что, по словам профилировщика, является узким местом?   -  person Eric Lippert    schedule 11.05.2010
comment
@ Эрик, алгоритм Левенштейна, что неудивительно, поскольку это действительно единственное, что делает этот код.   -  person Chris    schedule 11.05.2010


Ответы (9)


Можно начать с определения приоритета того, какие электронные письма сравнивать друг с другом.

Основной причиной ограничений производительности является производительность O(n2) при сравнении каждого адреса с любым другим адресом электронной почты. Расстановка приоритетов – это ключ к повышению производительности алгоритмов поиска такого типа.

Например, вы можете разделить все электронные письма одинаковой длины (+/- некоторая сумма) и сначала сравнить это подмножество. Вы также можете удалить все специальные символы (цифры, символы) из электронных писем и найти те, которые идентичны после этого сокращения.

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

Другой возможный способ оптимизировать этот алгоритм — использовать дату создания учетной записи электронной почты (при условии, что вы ее знаете). Если создаются дубликаты электронных писем, они, скорее всего, будут создаваться в течение короткого периода времени друг за другом — это может помочь вам сократить количество сравнений, выполняемых при поиске дубликатов.

person LBushkin    schedule 11.05.2010

Ну, вы можете сделать некоторые оптимизации, предполагая, что разница Левенштейна является вашим узким местом.

1) При расстоянии Левенштейна, равном 2, электронные письма будут находиться в пределах 2 символов друг от друга, поэтому не беспокойтесь о вычислениях расстояния, если только abs(length(email1)-length(email2)) ‹= 2

2) Опять же, при расстоянии 2 не будет отличаться более чем на 2 символа, поэтому вы можете создавать HashSet символов в электронных письмах и брать длину объединения за вычетом длины пересечения двух . (Я полагаю, что это SymmetricExceptWith). Если результат > 2, перейдите к следующему сравнению.

OR

Напишите свой собственный алгоритм расстояния Левенштейна. Если вас интересуют только длины ‹ k, вы можете оптимизировать время выполнения. См. «Возможные улучшения» на странице Википедии: http://en.wikipedia.org/wiki/Levenshtein_distance.

person Jeff B    schedule 11.05.2010

Вы можете добавить несколько оптимизаций:

1) Составьте список известных случаев мошенничества и сравните с ним в первую очередь. После того, как вы начнете свой алгоритм, вы сможете попасть в этот список быстрее, чем в основной список.

2) Сначала отсортируйте список. Это не займет много времени (для сравнения) и повысит вероятность совпадения первой части строки. Сначала сортируйте по доменному имени, а затем по имени пользователя. Возможно, поместите каждый домен в свою корзину, затем отсортируйте и сравните с этим доменом.

3) Рассмотрим зачистку домена вообще. [email protected] и [email protected] никогда не вызовут срабатывание вашего флага.

person corsiKa    schedule 11.05.2010
comment
На самом деле я уже делаю 1 и 2. 3 кажется более точным, чем производительным. - person Chris; 11.05.2010
comment
Вы также делаете toLower очень часто. Рассмотрите возможность выполнения toLower для всего массива перед началом, чтобы не делать это каждый раз. - person corsiKa; 11.05.2010

Если вы можете определить подходящее отображение в некоторое k-мерное пространство и подходящую норму в этом пространстве, это сводится к Проблема всех ближайших соседей, которую можно решить за время O(n log n).

Однако найти такое отображение может быть непросто. Может быть, кто-то возьмет этот частичный ответ и побежит с ним.

person Thomas    schedule 11.05.2010

Просто для полноты вы также должны учитывать семантику адресов электронной почты с точки зрения:

  1. Gmail рассматривает user.name и username как одно и то же, поэтому оба являются действительными адресами электронной почты, принадлежащими одному и тому же пользователю. Это могут делать и другие сервисы. Предложение LBushkin убрать специальные символы поможет здесь.

  2. Подадресация может привести к срабатыванию вашего фильтра, если пользователи догадаются об этом. . Вы хотели бы удалить данные подадреса перед сравнением.

person roguesys    schedule 11.05.2010
comment
Я бы сказал (используя gmail в качестве моего примера), проанализируйте адрес или знак + и удалите все от + до @, затем удалите ., а затем извлеките все дубликаты. Очевидно, вы не можете работать в обратном направлении от этого, поэтому он превратится в кортеж, где первый — уникальный ключ, второй — исходный, третий — измененный, а затем работает со списком третьего порядкового номера кортежа. Idk, я бы подошел к этому так, но на основе потенциальных схем дополнительной адресации для каждого доступного провайдера. ~~ Конечно, пользователи своего собственного домена могут довольно легко создать любой адрес, какой захотят, так что... - person jcolebrand; 09.06.2010

Возможно, вы захотите просмотреть полный набор данных, чтобы увидеть, есть ли другие общие черты между учетными записями, которые подделывали электронные письма.

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

person NotMe    schedule 11.05.2010

Сначала отсортируйте все в хеш-таблицу. Ключ должен быть доменным именем электронной почты; "gmail.com". Удалите специальные символы из значений, как было сказано выше.

Затем проверьте все адреса gmail.com друг против друга. Это должно быть намного быстрее. Не сравнивайте вещи, длина которых отличается более чем на 3 символа.

В качестве второго шага сверяйте все ключи друг с другом и разрабатывайте там группы. (например, gmail.com == googlemail.com.)

person Dean J    schedule 11.05.2010

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

Я думаю, что лучше использовать другие решения, такие как ограничение количества электронных писем, которые вы можете записывать в час/день, или время между получением этих адресов вами и их отправкой пользователям. По сути, сделайте так, чтобы было удобно отправлять несколько приглашений в день, а PITA — много. Я предполагаю, что большинство пользователей забыли бы/отказались бы делать это, если бы им пришлось делать это в течение относительно длительного периода времени, чтобы получить свои халявы.

person Francisco Noriega    schedule 11.05.2010

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

person Andrew Campion    schedule 09.06.2010