检测类似电子邮件地址的最佳方法?

时间:2010-05-11 16:03:18

标签: c# levenshtein-distance

我有一个约20,000个电子邮件地址的列表,其中一些我知道欺诈企图绕过“每个电子邮件1个”限制,例如username1 @ gmail.com,username1a @ gmail.com,username1b @ gmail.com等我想找到类似的电子邮件地址进行评估。目前我正在使用Levenshtein算法检查列表中其他电子邮件的每个电子邮件,并报告编辑距离小于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);
        }
    }
}

编辑:我想要抓住的一些内容如下:

01234567890@gmail.com
0123456789@gmail.com
012345678@gmail.com
01234567@gmail.com
0123456@gmail.com
012345@gmail.com
01234@gmail.com
0123@gmail.com
012@gmail.com

9 个答案:

答案 0 :(得分:9)

您可以首先应用一些优先级来与哪些电子邮件进行比较。

性能限制的一个关键原因是O(n 2 )将每个地址与每个其他电子邮件地址进行比较的性能。 确定优先级是提高此类搜索算法性能的关键。

例如,您可以将所有具有相似长度(+/-某个数量)的电子邮件打包并首先比较该子集。您还可以从电子邮件中删除所有特殊字符(数字,符号),并在减少后找到相同的字符。

您可能还希望从数据中创建一个trie而不是逐行处理它,并使用它来查找共享一组通用后缀/前缀的所有电子邮件,并从该减少中驱动您的比较逻辑。从您提供的示例中,您看起来正在寻找一个地址的一部分可能在另一个地址中显示为子字符串的地址。 Tries(和suffix trees)是执行这些类型搜索的有效数据结构。

优化此算法的另一种可能方法是使用创建电子邮件帐户的日期(假设您知道它)。如果创建了重复的电子邮件,则可能会在很短的时间内创建它们 - 这可能会帮助您减少在查找重复项时要执行的比较次数。

答案 1 :(得分:5)

你可以做一些优化,假设Levenshtein差异是你的瓶颈。

1)当Levenshtein距离为2时,电子邮件的长度将在2个字符之间,因此除非abs(length(email1)-length(email2))&lt; = 2

,否则不要费心去做距离计算

2)同样,距离为2时,不会有超过2个字符的不同,因此您可以在电子邮件中创建字符的HashSets,并使用union的长度减去交集的长度两个。 (我相信这是一个SymmetricExceptWith)如果结果是&gt; 2,跳到下一个比较。

OR

编码您自己的Levenshtein距离算法。如果你只对长度感兴趣&lt; k,您可以优化运行时间。请参阅维基百科页面上的“可能的改进”:http://en.wikipedia.org/wiki/Levenshtein_distance

答案 2 :(得分:2)

您可以添加一些优化:

1)保留已知欺诈的清单并与之进行比较。进入算法后,您可能比点击主列表更快地击中此列表。

2)首先对列表进行排序。它不会花太长时间(相比之下)并且会增加首先匹配字符串前面的机会。首先按域名排序,然后按用户名排序。也许将每个域放在自己的桶中,然后排序并与该域进行比较。

3)考虑一般剥离域名。 spammer3@gmail.com和spammer3@hotmail.com永远不会触发你的旗帜。

答案 3 :(得分:1)

如果你可以定义一个合适的映射到某个k维空间,并在该空间上定义一个合适的范数,这会减少到可以在O(n log n)时间内求解的All Nearest Neighbours Problem

然而,找到这样的映射可能很困难。也许有人会采取这个部分答案并与之一起运行。

答案 4 :(得分:1)

为了完整起见,您还应该考虑以下方面的电子邮件地址的语义:

  1. Gmail将user.nameusername视为相同,因此两者都是属于同一用户的有效电子邮件地址。其他服务也可以这样做。 LBushkin建议删除特殊字符会有所帮助。

  2. 如果用户明智,
  3. Sub-adrressing可能会使您的过滤器绊倒。您需要在比较之前删除子地址数据。

答案 5 :(得分:0)

您可能需要查看完整数据集,以查看有欺骗性电子邮件的帐户之间是否存在其他共性。

我不知道你的应用程序做了什么,但如果还有其他关键点,那么使用它们来过滤你要比较的地址。

答案 6 :(得分:0)

首先将所有内容排序到哈希表中。密钥应该是电子邮件的域名; “gmail.com”。如上所述,从值中删除特殊字符。

然后检查所有gmail.com的对方。这应该很多更快。不要比较长度超过3个字符的东西。

作为第二步,检查所有密钥,并在那里开发分组。 (gmail.com == googlemail.com,例如。)

答案 7 :(得分:0)

我同意其他人关于比较电子邮件地址没有帮助的评论,因为用户还可以创建欺诈性的不同寻址地址。

我认为更好地提供其他解决方案,例如限制每小时/每天可以记下的电子邮件数量,或者您收到的这些地址与发送给用户之间的时间。基本上,以一种每天发送一些邀请函的方式来解决它,但PITA会发送许多邀请函。我猜大多数用户会忘记/放弃这样做,如果他们必须在相对较长的时间内完成它才能获得他们的免费赠品。

答案 8 :(得分:0)

有什么方法可以检查创建电子邮件的人的IP地址。这将是一种确定或至少为您提供有关不同电子邮件地址是否来自同一个人的附加信息的简单方法。