C# 用c语言进行海量数字比较#

C# 用c语言进行海量数字比较#,c#,C#,数字集的比较太慢。有什么更有效的方法来解决这个问题 我有两组,每组大约有500万套,每组有6个数字,每个数字在1到100之间。集合和组未排序和重复 下面是一个例子 No. Group A Group B 1 {1,2,3,4,5,6} {6,2,4,87,53,12} 2 {2,3,4,5,6,8} {43,6,78,23,96,24} 3 {45,23,57,79,23,76}

数字集的比较太慢。有什么更有效的方法来解决这个问题

我有两组,每组大约有500万套,每组有6个数字,每个数字在1到100之间。集合和组未排序和重复

下面是一个例子

No.     Group A                 Group B
1       {1,2,3,4,5,6}           {6,2,4,87,53,12}
2       {2,3,4,5,6,8}           {43,6,78,23,96,24}
3       {45,23,57,79,23,76}     {12,1,90,3,2,23}
4       {3,5,85,24,78,90}       {12,65,78,9,23,13}
        ...                     ...
我的目标是比较两组,并根据笔记本电脑上5小时内的最大公共元素计数对A组进行分类

在本例中,A组的1号和B组的3号有3个公共元素(1,2,3)。 此外,A组的2号和B组的3号有2个公共元素(2,3)。因此,我将A组分类如下

No.     Group A             Maximum Common Element Count
1       {1,2,3,4,5,6}           3
2       {2,3,4,5,6,8}           3
3       {45,23,57,79,23,76}     1
4       {3,5,85,24,78,90}       2
        ... 
我的方法是比较每个集合和数字,所以复杂性是A组计数*B组计数*6*6。所以需要很多时间

Dictionary<int, List<int>> Classified = new Dictionary<int, List<int>>();
foreach (List<int> setA in GroupA)
{
    int maxcount = 0;
    foreach (List<int> setB in GroupB)
    {
        int count = 0; 
        foreach(int elementA in setA)
        {
            foreach(int elementB in setB)
            {
                if (elementA == elementB) count++;
            }
        }
        if (count > maxcount) maxcount = count;
    }
    Classified.Add(maxcount, setA);
}
字典分类=新字典();
foreach(在GroupA中列出setA)
{
int maxcount=0;
foreach(列出B组中的挫折)
{
整数计数=0;
foreach(setA中的int elementA)
{
foreach(setB中的int elementB)
{
if(elementA==elementB)count++;
}
}
如果(计数>最大计数)最大计数=计数;
}
添加(最大计数,setA);
}

我能想到的最快的方法是:

由于所有数字都来自有限的范围(1-100),因此可以将每个集合表示为一个100位二进制数
,其中
dn
等于1,如果
n
在集合中

然后比较两个集合意味着两个二进制表示上的二进制
,并计算集合位()


除此之外,此任务还可以并行化(您的输入是不可变的,因此非常简单)。

您必须使用较小的集合对其进行基准测试,但由于您必须进行
5E6*5E6=25E12
比较,您不妨先对
5E6+5E6=10E6
集合的内容进行排序

然后,set-to-set比较变得非常快,因为在每次比较中,只要在比较的第一个方面达到最大值,就可以停止比较。通过比较,每套设备节省的成本微乎其微,但累计起来要节省数万亿倍

你还可以更进一步,按最低分录和最高分录对两组500万人进行索引。您将进一步显著减少比较的数量。最后,这只是
100*100'=10000=1E4
不同的集合。您将永远不必将以12为最高数字的集合与以13或更多开始的集合进行比较。有效地避免了大量的工作

在我看来,这是对大量数据进行排序,但它与实际的集对集比较的数量相比显得有些苍白。在这里,您消除了所有0的工作,并且在进行比较时,如果条件合适,您可以提前中止

正如其他人所说,并行化


注:
5E6=5*10^6=5000000
25E12=25*10^12=25*1000000000000

我会使用以下方法:

foreach (List<int> setA in GroupA)
{
    int maxcount = GroupB.Max(x => x.Sum(y => setA.Contains(y) ? 1 : 0));
    Classified.Add(maxcount, setA);
}
foreach(在GroupA中列出setA)
{
int maxcount=GroupB.Max(x=>x.Sum(y=>setA.Contains(y)?1:0));
添加(最大计数,setA);
}

您提出的任何算法的时间复杂度都将是相同的顺序。hashset可能会快一点,但如果是这样的话,就不会太快了——36个直接列表比较与12个hashset查找的开销不会明显更高,如果有的话,但您必须进行基准测试。预排序可能会有所帮助,因为每个集合都会被比较数百万次。仅供参考,for循环比列表上的foreach循环快,而数组比列表快(for和foreach在数组上的性能相同),这可能会带来相当大的性能差异。如果
No.
列是连续的,那么我会使用数组来代替字典。数组查找比字典查找快一个数量级

我认为,除了并行化之外,您一般都在尽可能快地完成这项工作,通过上面的微优化可以获得一些小的收益


当前算法离目标执行时间有多远?

以下是我的尝试-使用a并预先计算每个集合的范围,以避免像
{1,2,3,4,5,6}
{7,8,9,10,11,12}
这样的集对集比较(如's'所指出)

对我来说(使用随机集运行),它使原始代码的速度提高了130倍。你刚才提到的

现在执行时间超过3天,所以其他人说我需要并行化

问题本身就是

我的目标是比较两组,并根据笔记本电脑上5小时内的最大公共元素计数对A组进行分类

因此,假设注释意味着数据的执行时间超过3天(72小时),但您希望在5小时内完成,那么您只需要将速度提高14倍


框架 我创建了一些类来运行这些基准测试:

  • Range
    -获取一些
    int
    值,并跟踪最小值和最大值

    public class Range
    {
        private readonly int _min;
        private readonly int _max;
    
        public Range(IReadOnlyCollection<int> values)
        {
            _min = values.Min();
            _max = values.Max();
        }
    
        public int Min { get { return _min; } }
        public int Max { get { return _max; } }
    
        public bool Intersects(Range other)
        {
            if ( _min < other._max )
                return false;
    
            if ( _max > other._min )
                return false;
    
            return true;
        }
    }
    
  • 使用
    SetWithRange.Random
    生成结果,如下所示:

    const int groupCount = 10000;
    const int setSize = 6;
    
    var range = new Range(new[] { 1, 100 });
    var generator = new Random();
    
    var groupA = Enumerable.Range(0, groupCount)
        .Select(i => SetWithRange.Random(generator, setSize, range))
        .ToList();
    
    var groupB = Enumerable.Range(0, groupCount)
        .Select(i => SetWithRange.Random(generator, setSize, range))
        .ToList();
    
下面给出的时间是在我的机器上平均运行三次x64版本构建

对于所有情况,我生成了10000个随机集的组,然后使用

timefor5000000=timeFor10000/10000/10000*5000000*5000000
=时间10000*250000

结果
  • 四个
    foreach
    块:

    平均时间=48628ms;500万台的预计时间=3377小时

    var result = new Dictionary<SetWithRange, int>();
    foreach ( var setA in groupA )
    {
        int maxcount = 0;
        foreach ( var setB in groupB )
        {
            int count = 0;
            foreach ( var elementA in setA )
            {
                foreach ( int elementB in setB )
                {
                    if ( elementA == elementB )
                        count++;
                }
            }
            if ( count > maxcount ) maxcount = count;
        }
        result.Add(setA, maxcount);
    }
    
  • 使用
    HashSet
    并添加
    范围
    仅检查相交的集合:

    平均时间=
    var result = new Dictionary<SetWithRange, int>();
    foreach ( var setA in groupA )
    {
        int maxcount = 0;
        foreach ( var setB in groupB )
        {
            int count = 0;
            foreach ( var elementA in setA )
            {
                foreach ( int elementB in setB )
                {
                    if ( elementA == elementB )
                        count++;
                }
            }
            if ( count > maxcount ) maxcount = count;
        }
        result.Add(setA, maxcount);
    }
    
    var result = new Dictionary<SetWithRange, int>();
    Parallel.ForEach(groupA, setA =>
    {
        int maxcount = 0;
        foreach ( var setB in groupB )
        {
            int count = 0;
            foreach ( var elementA in setA )
            {
                foreach ( int elementB in setB )
                {
                    if ( elementA == elementB )
                        count++;
                }
            }
            if ( count > maxcount ) maxcount = count;
        }
    
        lock ( result )
            result.Add(setA, maxcount);
    });
    
    var result = new Dictionary<SetWithRange, int>();
    Parallel.ForEach(groupA, setA =>
    {
        var commonValues = groupB.Max(setB => setA.CommonValuesWith(setB));
        lock ( result )
            result.Add(setA, commonValues);
    });