用于识别字符串集合中的对的最快算法

时间:2018-02-05 21:02:00

标签: c# algorithm performance

我正在寻找最快的算法:

目标:输出一行中找到的对的总次数。各个元素可以是任何给定行上的任何顺序。

INPUT:

a;b;c;d
a;e;f;g
a;b;f;h

输出

a;b = 2
a;c = 1
a;d = 1
a;e = 1
a;f = 2
a;g = 1
b;c = 1
b;d = 1

我正在用C#编程,我有一个嵌套的for循环添加做一个常见的字典字典,其中字符串就像a; b,当发现一个事件时,它会添加到现有的int tally或添加一个新的at tally = 0。

请注意:

a;b = 1
b;a = 1

应该简化为:

a;b = 1

我愿意使用其他语言,输出是一个纯文本文件,我将其提供给Gephi可视化工具。

奖励:非常有兴趣知道这个特定算法的名称,如果它在那里。很确定它是。

String[] data = File.ReadAllLines(@"C:\input.txt");
Dictionary<string, int> ress = new Dictionary<string, int>();

foreach (var line in data)
{
    string[] outStrings = line.Split(';');

    for (int i = 0; i < outStrings.Count(); i++)
    {
        for (int y = 0; y < outStrings.Count(); y++)
        {
            if (outStrings[i] != outStrings[y])
            {
                try
                {
                    if (ress.Any(x => x.Key == outStrings[i] + ";" + outStrings[y]))
                    {
                        ress[outStrings[i] + ";" + outStrings[y]] += 1;
                    }
                    else
                    {
                        ress.Add(outStrings[i] + ";" + outStrings[y], 0);
                    }
                }
                catch (Exception)
                {

                }
            }
        }
    }
}

foreach (var val in ress)
{
    Console.WriteLine(val.Key + "----" + val.Value);
}

2 个答案:

答案 0 :(得分:3)

我认为你的内部循环应该从i + 1开始,而不是再次从0开始,外部循环应该只运行到Length - 1,因为最后一项将在内循环。此外,当您添加新项目时,您应该添加值1,而不是0(因为我们添加它的全部原因是因为我们找到了一个)。

您还可以将密钥存储到字符串中一次,而不是在比较和分配期间进行多个连接,并且可以使用ContainsKey方法确定密钥是否已存在。

此外,您可能需要考虑避免空catch块,除非您确定不关心是否或出现了什么问题。如果我期待一个异常并且知道如何处理它,那么我就会抓住那个例外,否则我只会让它冒出来。

以下是修改代码以查找所有配对及其计数的一种方法:

<强>更新

我添加了一张支票以确保&#34;对#34;密钥总是排序,因此&#34; b; a&#34;成为&#34; a; b&#34;。这不是您的示例数据中的问题,但我将数据扩展为包含b;a;a;b;a;b;a;之类的行。另外,我向StringSplitOptions.RemoveEmptyEntries方法添加了Split,以处理行以;开头或结尾的情况(否则空值会产生像";a"这样的对。)< / p>

private static void Main()
{
    var data = File.ReadAllLines(@"f:\public\temp\temp.txt");
    var pairCount = new Dictionary<string, int>();

    foreach (var line in data)
    {
        var lineItems = line.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);

        for (var outer = 0; outer < lineItems.Length - 1; outer++)
        {
            for (var inner = outer + 1; inner < lineItems.Length; inner++)
            {
                var outerComparedToInner = string.Compare(lineItems[outer], 
                    lineItems[inner], StringComparison.Ordinal);

                // If both items are the same character, ignore them and keep looping
                if (outerComparedToInner == 0) continue;

                // Create the pair such that the lower of the two 
                // values is first, so that "b;a" becomes "a;b"
                var thisPair = outerComparedToInner < 0
                    ? $"{lineItems[outer]};{lineItems[inner]}"
                    : $"{lineItems[inner]};{lineItems[outer]}";

                if (pairCount.ContainsKey(thisPair))
                {
                    pairCount[thisPair]++;
                }
                else
                {
                    pairCount.Add(thisPair, 1);
                }
            }
        }
    }

    Console.WriteLine("Pair\tCount\n----\t-----");

    foreach (var val in pairCount.OrderBy(i => i.Key))
    {
        Console.WriteLine($"{val.Key}\t{val.Value}");
    }

    Console.Write("\nDone!\nPress any key to exit...");
    Console.ReadKey();

}

<强>输出

给定包含样本数据的文件,输出为:

enter image description here

答案 1 :(得分:1)

@mrmcgreg,最后将实现更改为ECLAT algorythm后,所有内容都会在几秒钟而不是几小时内运行。

基本上对于每个唯一标记,跟踪找到这些标记的LINE NUMBERS,并简单地将这对数字列表与组合对相交以获得计数。

 Dictionary<string, List<int>> uniqueTagList = new Dictionary<string, List<int>>();


            foreach (var uniqueTag in uniquetags)
            {
                List<int> lineNumbers = new List<int>();
                foreach (var item in data.Select((value, i) => new { i, value }))
                {
                    var value = item.value;
                    var index = item.i;

                    //split data into tags
                    var tags = item.ToString().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (var tag in tags)
                    {
                        if (uniqueTag == tag)
                        {
                            lineNumbers.Add(index);
                        }
                    } 
                }

                //remove all having support threshold.
                if (lineNumbers.Count > 5)
                {
                    uniqueTagList.Add(uniqueTag, lineNumbers);
                }  
            }