快速算法查找列表时间范围内的元素

时间:2013-11-26 14:56:41

标签: java arrays list

问题: 我有一个包含时间和值的数据列表(时间=长毫秒和双值)。我现在需要计算不同时间范围内的几个平均值 我每秒最多获得50个值,但有时只有几个值,需要保持最后10秒,因此需要500个值。

我想要的是:计算时间> =开始和时间< =结束的值的平均值。

我可以确保没有时间是双倍的,所以它可以用作关键。

目前我使用数组来存储值,并且有一个位置标记,一旦达到500就会重置为0,因此旧的条目被重新计算。我可以很容易地改变它。

我不确定最快的方法是什么,例如手动搜索数组或使用列表,hashMap,Collection(带比较器?)或者其他。我找不到(java)类似列表的函数,我在内置搜索“key> = x”或“value> = x”。

性能比良好或简单的编码更重要。

指向正确的方向会很高兴。

每次计算新值时,我会计算最后10个值的平均值,即每秒30-50次计算,这是最重要的数据。我需要区分测量中的小错误和实际更改。 我额外计算每1/10秒的平均值(这可能会被丢弃),最后计算一秒的平均值和最后10秒的平均值。这是每秒额外12次平均计算。减少计算次数并不是一种选择。

由于这有点抽象,下面是数据的示例(其中avg是根据最后10个值计算的,但这不是程序逻辑)。

value           Avg timeReading timeReadingISO
1024,6668701172 -       1385408750828   2013-11-25 19:45:50
1024,6668701172 -       1385408751350   2013-11-25 19:45:51
1024,6668701172 -       1385408751859   2013-11-25 19:45:51
1024,6683349609 -       1385408752373   2013-11-25 19:45:52
1024,6683349609 -       1385408752878   2013-11-25 19:45:52
1024,6689453125 -       1385408753385   2013-11-25 19:45:53
1024,6689453125 -       1385408753895   2013-11-25 19:45:53
1024,6721191406 -       1385408754406   2013-11-25 19:45:54
1024,6721191406 -       1385408754912   2013-11-25 19:45:54
1024,6774902344 -       1385408755432   2013-11-25 19:45:55
1024,6774902344 1024,67 1385408755994   2013-11-25 19:45:55
1024,6774902344 1024,67 1385408756502   2013-11-25 19:45:56
1024,6837158203 1024,67 1385408757012   2013-11-25 19:45:57
1024,6837158203 1024,67 1385408757520   2013-11-25 19:45:57
1024,689453125  1024,68 1385408758028   2013-11-25 19:45:58
1024,689453125  1024,68 1385408758536   2013-11-25 19:45:58
1024,6938476563 1024,68 1385408759055   2013-11-25 19:45:59
1024,6938476563 1024,68 1385408759560   2013-11-25 19:45:59
1024,6990966797 1024,68 1385408760075   2013-11-25 19:46:00
1024,6990966797 1024,69 1385408760579   2013-11-25 19:46:00
1024,7038574219 1024,69 1385408761086   2013-11-25 19:46:01
1024,7038574219 1024,69 1385408761596   2013-11-25 19:46:01
1024,7111816406 1024,69 1385408762103   2013-11-25 19:46:02
1024,7111816406 1024,70 1385408762606   2013-11-25 19:46:02
1024,7111816406 1024,70 1385408763112   2013-11-25 19:46:03
1024,7111816406 1024,70 1385408763622   2013-11-25 19:46:03
1024,7172851563 1024,70 1385408764128   2013-11-25 19:46:04
1024,7172851563 1024,71 1385408764637   2013-11-25 19:46:04
1024,7208251953 1024,71 1385408765149   2013-11-25 19:46:05

1026,5457763672 -       1385474621756   2013-11-26 14:03:41
1026,6057128906 -       1385474621790   2013-11-26 14:03:41
1026,6257324219 -       1385474621823   2013-11-26 14:03:41
1026,6057128906 -       1385474621858   2013-11-26 14:03:41
1026,6257324219 -       1385474621890   2013-11-26 14:03:41
1026,6257324219 -       1385474621921   2013-11-26 14:03:41
1026,6057128906 -       1385474621956   2013-11-26 14:03:41
1026,5457763672 -       1385474621988   2013-11-26 14:03:41
1026,6557617188 -       1385474622022   2013-11-26 14:03:42
1026,6657714844 -       1385474622057   2013-11-26 14:03:42
1026,6257324219 1026,61 1385474622090   2013-11-26 14:03:42
1026,6057128906 1026,62 1385474622123   2013-11-26 14:03:42
1026,6657714844 1026,62 1385474622159   2013-11-26 14:03:42
1026,6557617188 1026,62 1385474622193   2013-11-26 14:03:42
1026,6557617188 1026,63 1385474622227   2013-11-26 14:03:42
1026,6257324219 1026,63 1385474622260   2013-11-26 14:03:42
1026,6257324219 1026,63 1385474622298   2013-11-26 14:03:42
1026,6557617188 1026,63 1385474622330   2013-11-26 14:03:42
1026,6257324219 1026,64 1385474622365   2013-11-26 14:03:42
1026,6257324219 1026,64 1385474622401   2013-11-26 14:03:42
1026,6257324219 1026,64 1385474622431   2013-11-26 14:03:42
1026,5758056641 1026,64 1385474622466   2013-11-26 14:03:42
1026,6057128906 1026,63 1385474622501   2013-11-26 14:03:42
1026,5457763672 1026,63 1385474622533   2013-11-26 14:03:42
1026,5457763672 1026,62 1385474622565   2013-11-26 14:03:42
1026,6057128906 1026,61 1385474622599   2013-11-26 14:03:42
1026,6057128906 1026,60 1385474622631   2013-11-26 14:03:42
1026,5758056641 1026,60 1385474622665   2013-11-26 14:03:42
1026,5457763672 1026,59 1385474622702   2013-11-26 14:03:42
1026,6057128906 1026,59 1385474622734   2013-11-26 14:03:42
1026,6557617188 1026,58 1385474622766   2013-11-26 14:03:42
1026,5758056641 1026,59 1385474622800   2013-11-26 14:03:42
1026,6057128906 1026,59 1385474622836   2013-11-26 14:03:42
1026,6057128906 1026,59 1385474622868   2013-11-26 14:03:42
1026,5158691406 1026,59 1385474622901   2013-11-26 14:03:42
1026,5457763672 1026,59 1385474622935   2013-11-26 14:03:42
1026,6856689453 1026,58 1385474622966   2013-11-26 14:03:42

2 个答案:

答案 0 :(得分:1)

首先,在计算平均值时,您应该创建结构的副本(或使用一个线程安全的并在添加或删除期间遍历它不会导致痛苦),除非您在一个线程中执行所有操作。

我猜你的集合中的元素是有序排序的,因为你按顺序接收更新(如果没有查找等效的排序列表)。

我的方法是选择平均测量的最小间隔。让我们说10个值。然后你可以创建50个集合(大小为10),其中每个集合都是类,它为你提供了计算平均值的方法。然后,您可以选择要计算的平均值。只计算收集平均值的平均值。 更重要的是 - 一旦给定集合的计算平均值不会改变,您可以将其缓存

请注意,由于已经处理了最小间隔,因此您无需将值从一个集合传输到另一个集合。如果新的10个元素进入缓冲区,您只需重新分配参考。

/* initializing */
MySlicedCollection buffer = new MySlicedCollection();
MySlicedCollection[] mscArray = new MySlicedCollection[50];

/* when every 10 values came in */
for(int i = mscArray.length-1; i > 0 ; --i) {
    mscArray[i] = mscArray[i-1];
}
mscArray[0] = buffer;
buffer = new MySlicedCollection();

/* avg of all collection */
for(MySlicedCollection msc : mscArray) {
    sum += msc.getAverage();
}
sum /= 50;

您还应该考虑使用以前的结果计算平均值。如果你必须计算1秒和2秒的平均值,那么你可以将剩余的平均值加到已计算的平均值一秒钟并将其除以2.

/* avg for one second */
for(int i = 0; i < 5; ++i) {
    sumOneSec += mscArray[i].getAverage();
}
sumOneSec /= 5;

/* avg for two seconds */
for(int i = 5; i < 10; ++i) {
    sumTwoSec += mscArray[i].getAverage();
}
sumTwoSec = ((sumTwoSec/5) + sumOneSec) / 2;

但请记住有人说:“先行动然后行动” - 也许你的表现足够了?

<小时/> 更新的 通过使用Circular buffer并执行简单的技巧,您可以保存至少一次迭代。如果缓冲区已满(并且其容量为50),则存在已知的平均值和另一个值 - 您只需通过计算重新计算

avg = (avg * 50 - oldestValue + newValue)/50;

遗憾的是,由于浮点变量有限表示,这会给你的计算带来一点误差,但是由于你使用的是双值,我认为你不需要这样的精度。类似的解决方案可以提供给另一个平均值,但这需要更多的思考:)

答案 1 :(得分:0)

在Maciej的答案中缓存平均值组是一种有效的方法。当前列表的一个简单方法是Java SortedSet,这是一个接口,因此您最终会使用TreeSet

创建一个Comparable对象来存储您的时间和值,或者为SortedSet创建一个Comparator。确保根据时间(而不是值)对其进行排序。

public class Holder implements Comparable
{
  private double time, value;
  public Holder (double t, double v)
  {
    this.time = t;
    this.value = v;
  }

  public double getValue()
  {  return this.value; }

  public double getTime()
  {  return this.time; }

  //You may want a better comparator.
  public int compareTo( Holder h )
  {
    return this.getTime < h.getTime() ? -1 : 1;
  }
}

只需按照常规方式为集合添加值,它们将根据时间自动排序。如果您想要过去10的平均值,请找到当前时间并致电sortedSet.tailSet( new CustomObject( currentTime - 10000 ) )。现在迭代返回的集合来计算平均值。

SortedSet<Holder> slice = allHolders.tailset( new Holder( currentTime - 10000 ) );
double sum = 0.0;
for( Holder h : slice )
{
  sum += h.getValue();
} 
double result = sum / slice.size();

如果您觉得平均呼叫有延迟,您可以找到.subSet()的时间组。