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

C# 检测类似电子邮件地址的最佳方法?,c#,levenshtein-distance,C#,Levenshtein Distance,我有一个大约20000个电子邮件地址的列表,其中一些我知道是欺诈性的试图绕过“每封电子邮件1个”的限制,例如username1@gmail.com, username1a@gmail.com, username1b@gmail.com等。我想找到类似的电子邮件地址进行评估。目前,我正在使用Levenshtein算法检查列表中的每封电子邮件,并报告编辑距离小于2的任何电子邮件。然而,这是非常缓慢的。有没有更有效的方法 我现在使用的测试代码是: using System; using System.

我有一个大约20000个电子邮件地址的列表,其中一些我知道是欺诈性的试图绕过“每封电子邮件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);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.IO;
使用系统线程;
名称空间LevenshteinAnalyzer
{
班级计划
{
常量字符串输入文件=@“C:\INPUT.txt”;
常量字符串输出文件=@“C:\OUTPUT.txt”;
静态void Main(字符串[]参数)
{
var inputWords=File.ReadAllLines(输入文件);
var outputWords=新分类数据集();
for(变量i=0;i如果(距离)您可以从应用一些优先顺序开始,对哪些电子邮件进行比较。

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

例如,你可以将所有长度相近(+/-某个数量)的电子邮件放入桶中,然后首先比较该子集。你还可以从电子邮件中去掉所有特殊字符(数字、符号),然后找出那些在缩减后相同的字符

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


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

您可以添加一些优化:

1) 保留一个已知欺诈的列表,并与第一个列表进行比较。在你开始使用算法后,你可能会比你点击主列表更快地点击这个列表

2) 首先对列表进行排序。这不会花费太长的时间(比较而言),并且会增加首先匹配字符串前面的机会。先按域名排序,然后按用户名排序。可能会将每个域放在自己的bucket中,然后进行排序,并与该域进行比较


P>3)考虑一般拆分域。spammer3@gmail.com及spammer3@hotmail.com永远不会触发您的标志。

如果您可以定义到某个k维空间的适当映射,以及该空间上的适当范数,这将减少到可以在O(n logn)时间内解决的


然而,找到这样一个映射可能很困难。也许有人会接受这个部分答案并使用它运行。

好吧,假设Levenshtein差异是您的瓶颈,您可以进行一些优化

1) Levenshtein距离为2时,电子邮件之间的长度将在2个字符以内,因此不必费心进行距离计算,除非
abs(length(email1)-length(email2))
2跳到下一个比较


代码自己的LevsTein距离算法。如果你只对长度< P>。为了完整性,你也应该考虑电子邮件地址的语义,即:

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

  • 如果用户明智地选择,可能会导致筛选器跳闸。您可能希望在比较之前删除子地址数据


  • 您可能希望查看完整的数据集,以查看伪造电子邮件的帐户之间是否存在其他共性


    我不知道你的应用程序是做什么的,但是如果还有其他关键点,那么使用这些关键点来筛选你要比较的地址。

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

    然后把所有的gmail.com文件互相核对一下。这应该会快得多。不要比较长度超过3个字符的文件


    第二步,检查所有密钥,并在其中进行分组。(例如,gmail.com==googlemail.com。)

    我同意其他人关于比较电子邮件地址没有帮助的评论,因为用户也可以创建外观不相似的假地址。