C# 用c语言进行海量数字比较#
数字集的比较太慢。有什么更有效的方法来解决这个问题 我有两组,每组大约有500万套,每组有6个数字,每个数字在1到100之间。集合和组未排序和重复 下面是一个例子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}
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();
timefor5000000=timeFor10000/10000/10000*5000000*5000000
=时间10000*250000
结果
- 四个
块: 平均时间=48628ms;500万台的预计时间=3377小时foreach
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); });