快速count()两个字符串数组的交集

时间:2012-12-16 10:46:53

标签: c# optimization array-intersect

我需要计算与两个大字符串数组相交的元素数量,并且速度非常快。

我使用以下代码:

arr1[i].Intersect(arr2[j]).Count()

对于CPU时间,VS Profiler指示

  • System.Linq.Enumerable.Count()
  • 中的85.1%
  • System.Linq.Enumerable.Intersect()
  • 中的0.3%

不幸的是,完成所有工作可能需要数小时。

如何更快地完成?

5 个答案:

答案 0 :(得分:3)

您可以将HashSetarr2

一起使用
HashSet<string> arr2Set = new HashSet<string>(arr2);
arr1.Where(x=>arr2Set.Contains(x)).Count();
              ------------------
                      |
                      |->HashSet's contains method executes quickly using hash-based lookup..

不考虑从arr2arr2Set的转换,这应该是O(n)

答案 1 :(得分:1)

我怀疑探查器显示Count中消耗的时间的原因是,这是实际枚举集合的地方(Intersect是懒惰评估的,并且在您需要之前不会运行结果)。

我相信Intersect应该有一些内部优化来使这个速度相当快,但你可以尝试使用HashSet<string>,这样你就可以确保可以在不搜索每个元素的内部数组的情况下进行交叉:

HashSet<string> set = new HashSet<string>(arr1);
set.IntersectWith(arr2);
int count = set.Count;

答案 2 :(得分:1)

Hmmm Intersect可能是N ^ 2

使其快速快速排序两个阵列。而不是遍历两个数组。计算交叉点。

懒得测试它会有多快但是应该是O(nlogn + n)

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

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            const int arrsize = 1000000;
            Random rnd = new Random(42);
            string[] arr1 = new string[arrsize];
            string[] arr2 = new string[arrsize];
            for (int i = 0; i < arrsize; i++)
            {
                arr1[i] = rnd.Next().ToString();
                arr2[i] = rnd.Next().ToString();
            }
            {
                var stamp = (System.Diagnostics.Stopwatch.GetTimestamp());
                arr1.Intersect(arr2).Count();
                Console.WriteLine("array" + (System.Diagnostics.Stopwatch.GetTimestamp() - stamp));
            }

        {

            HashSet<string> set = new HashSet<string>(arr1);
            var stamp = (System.Diagnostics.Stopwatch.GetTimestamp());
            set.IntersectWith(arr2);
            int count = set.Count;
            Console.WriteLine("HashSet" + (System.Diagnostics.Stopwatch.GetTimestamp() - stamp));
        }
            {
               var stamp = (System.Diagnostics.Stopwatch.GetTimestamp());
                HashSet<string> set = new HashSet<string>(arr1);
                set.IntersectWith(arr2);
                int count = set.Count;
                Console.WriteLine("HashSet + new" + (System.Diagnostics.Stopwatch.GetTimestamp() - stamp));
            }

            {
                var stamp = (System.Diagnostics.Stopwatch.GetTimestamp());
                SortedSet<string> set = new SortedSet<string>(arr1);
                set.IntersectWith(arr2);
                int count = set.Count;
                Console.WriteLine("SortedSet +new " + (System.Diagnostics.Stopwatch.GetTimestamp() - stamp));
            }

            {

                SortedSet<string> set = new SortedSet<string>(arr1);
                var stamp = (System.Diagnostics.Stopwatch.GetTimestamp());
                set.IntersectWith(arr2);
                int count = set.Count;
                Console.WriteLine("SortedSet without new " + (System.Diagnostics.Stopwatch.GetTimestamp() - stamp));
            }
        }
    }
}

结果

阵列914,637

HashSet 816,119

HashSet + new 1,150,978

SortedSet + new 16,173,836

SortedSet没有新的7,946,709

所以似乎最好的方法是保留一个准备好的哈希集。

答案 3 :(得分:0)

当您使用集合时,您的复杂性将为O((n log n)*(m log m))左右,

我认为这里应该更快,但我不确定它现在是否是((n log n)+(m log m))

possible would be 
var Set1 = arr1[i].Distinct().ToArray(); // if necessary, if arr1 or arr2 could be not distinct
var Set2 = arr2[j].Distinct().ToArray();  

nCount = Set1.Count() + Set2.Count() - Set1.Append(Set2).Distinct().Count();

答案 4 :(得分:0)

使用较小的数组构建 HashSet ,然后遍历较大的数组,如果项目存在于hashset中,则递增计数器。