我有一大堆数据要循环,以便确定从时间点'D1'到未来'D2'中某个时间点的数据集的各种统计数据。基本上,我想在每次值之间的差异大于10时添加到数据库。例如:
Datum[] data = x;
for( Datum d1 : data ){
Datum[] tail = y; //From d1 up to 10 elements ahead
for( Datum d2 : tail ){
//Calculate difference
if( (d2.val - d1.val) > 10 ){
//Insert into database
}
}
}
我的问题是,有没有更好的算法/方法来做到这一点?由于尾部的9个元素在外循环的下一次迭代中被重用,我可以从中获益吗?我的目标是将其降低到远远小于(Big-O Notation)O(n 2 ),但我无法绕过它。通常,找到满足标准的D1,D2对意味着下一个D1元素也将具有更大的匹配机会。我可以利用这个优势吗?
我试图让它尽可能高效,因为数据集非常大。
答案 0 :(得分:1)
基于索引的for循环可能比迭代器执行得更好,因为您可以直接索引原始数组并避免复制到新数组。你有更好的记忆位置,错误分享的机会等等。
答案 1 :(得分:1)
你所拥有的是经典的扫描线算法,它是O(k * n),其中k是“重叠”或内部循环遍历的部分。在你的情况下,无论n是什么
,它最多为10Datum[] data = x;
for(int i=0;i<data.length;i++ ){
Datum d1=data[i];
Datum[] tail = y; //From d1 up to 10 elements ahead
for(int j=i+1;j<Math.min(i+10,data.length);i++){
d2 = data[j];
//Calculate difference
if( (d2.val - d1.val) > 10 ){
//Insert into database
break;//inner loop
}
}
}
答案 2 :(得分:0)
在你的鞋子里,我要做的第一件事是描绘一个典型的数据集,并找出时间的去向。这应该提供一些关于优化工作重点的提示。
假设计算与减法/比较一样简单,并且可以快速访问数组,那么您建议优化保存到数据库的建议应该是下一个优先级。例如,与单个插入语句相比,写出文本文件和使用批量插入可以提供非常快的性能。如果您坚持使用单独的插入,并且正在使用JDBC,那么批量更新将是一个很大的帮助,因为它们可以避免与数据库通信的延迟。
如果仍然不够快,可以考虑将数组分区为N个分区,并让每个分区由单独的线程处理。如果处理受CPU限制,这将特别有效。
最后,寻找代码级优化,例如通过使用索引来避免迭代器。如果写入数据库的项目数与迭代的元素数相比较小,则迭代器创建可能是瓶颈。
如果元素的数量大于10,并且严格地说,超过cpu缓存中的数量,则将扫描分解为更小的块将更有效。例如,不是从data2扫描1000个元素,而是将其分解为(例如)10次扫描100次,其中10次扫描中的每次扫描使用不同的d1值。这类似于以块方式实现矩阵多表示并更好地利用cpu高速缓存。
虽然你使用两个循环,通常是一个O(N ^ 2)算法,但第二个循环有一个固定的大小--10个元素,所以这减少到一个简单的常数因子 - 你大约是10倍更多的工作。
答案 3 :(得分:0)
有一种渐近更快的方法来解决这个问题,但我对于它是否会在实践中运行速度更加严重,因为你的窗口大小(10)太小了。如果你想增加这个大小 - 我称之为k - 更大,那么你可能会考虑选择类似下面的方法。
当您使用此算法时,您需要维护一个支持两个操作的k个元素的窗口:
实现此目的的一种方法是将所有元素存储在数据结构中,并结合平衡二叉搜索树和队列。队列包含按照它们在原始序列中出现的顺序存储的所有k个元素,并且被使用以便我们可以记住在需要添加新元素时要逐出哪个元素。平衡的BST存储按排序顺序存储的每个元素的副本。这意味着您可以实现上述操作:
总的来说,如果你有n个元素和你需要插入数据库的z对,这个算法将花费O(n log k + z)时间。要看到这一点,请注意我们总共执行n个操作副本(1),每个副本需要O(log k)时间。我们还执行n个操作副本(2),其中O(n log k)时间用于查找后继,然后O(z)所有迭代的总时间列出所有匹配对。
与您最初发布的O(nk)算法相比,此算法的渐近运行时间更好。假设匹配的数量不是“非常大”(例如,按照nk的顺序),当你增加n和k时,这将会快得多。
如果您存储的值是小范围内的整数(例如,0 - 10,000),则可以通过将平衡BST替换为针对整数优化的数据结构来进一步加快速度,例如{{3} },将其减少为O(n log log k + z)。同样,这只是更快渐近,如果你将k保持在10,这几乎肯定是不值得的。
希望这有帮助!