实现有限大小的TimeWindowBuffer

时间:2013-01-12 02:36:22

标签: java algorithm data-structures timeout buffer

我昨天接受采访。我无法找到一个编程问题的解决方案,我想在这里得到一些想法。问题是:

我需要在Java中实现 TimeWindowBuffer ,它存储用户随着时间的推移不断接收的数字。缓冲区有一个 maxBufferSize 。用户想要知道过去几秒的平均值,即用户传入的 timeWindow (因此这是一个滑动窗口)。我们可以从系统中获取当前时间(例如Java中的System.currentTimeMills())。 TimeWindowBuffer 类是这样的:

public class TimeWindowBuffer {
  private int maxBufferSize;
  private int timeWindow;

  public TimwWindowBuffer(int maxBufferSize, int timeWindow) {
     this.maxBufferSize = maxBufferSize;
     this.timeWindow = timeWindow;
  }

  public void addValue(long value) {
     ...
  }

  public double getAvg() {
     ...
     return average;
  }

  // other auxiliary methods
}

示例:

假设,用户每秒都会收到一个号码(用户可能无法以特定的速率收到号码),并想知道过去5秒的平均值
输入
maxBufferSize = 5,timeWindow = 5(s)
数字= { - 5 4 -8 -8 -8 1 6 1 8 5}
输出(我在此列出公式以供说明,但用户只需要结果) :
-5 / 1(t = 1)
(-5 + 4)/ 2(t = 2)
(-5 + 4-8)/ 3(t = 3)
(-5 + 4 - 8 - 8)/ 4(t = 4)
(-5 + 4 - 8 - 8 - 8)/ 5(t = 5)
(4 - 8 - 8 - 8 + 1)/ 5(t = 6)
(-8 - 8 - 8 + 1 + 6)/ 5(t = 7)
(-8 - 8 + 1 + 6 + 1)/ 5(t = 8)
(-8 + 1 + 6 + 1 + 8)/ 5(t = 9)
(1 + 6 + 1 + 8 + 5)/ 5(t = 10)

由于没有指定 TimeWindowBuffer 的数据结构,我一直在考虑保留一对值及其增加的时间。所以我的底层缓冲区声明是这样的:

 private ArrayList<Pair> buffer = new ArrayList<Pair>(maxBufferSize);

其中

class Pair {
  private long value;
  private long time;
  ...
}

由于按时间顺序添加了对,我可以对列表进行二进制搜索,并计算落入 timeWindow 的数字的平均值。问题是缓冲区有一个 maxBufferSize (尽管ArrayList没有),我必须在缓冲区已满时删除最旧的值。而且这个值仍然可以满足 timeWindow ,但现在它已经记录下来,我永远不会知道它何时到期。

我被困在这里当前。

我不需要直接回答,但在这里有一些讨论或想法。如果对问题和我的描述有任何疑惑,请现在就告诉我。

2 个答案:

答案 0 :(得分:1)

我喜欢这样的小谜题。我没有编译此代码,也没有考虑到生产使用所需的所有内容。就像我没有设计一种方法来将错过的值设置为0 - 即,如果每个滴答都没有出现值。

但这会让你想到另一种方式......

public class TickTimer
{
  private int tick = 0;
  private java.util.Timer timer = new java.util.Timer();

  public TickTimer(double timeWindow)
  {
    timer.scheduleAtFixedRate(new TickerTask(),
          0, // initial delay
          Math.round(1000/timeWindow)); // interval
  }

  private class TickerTask extends TimerTask
  {
    public void run ()
    {
      tick++;
    }
  }

  public int getTicks()
  {
    return tick;
  }
}

public class TimeWindowBuffer
{
  int buffer[];
  TickTimer timer;

  final Object bufferSync = new Object();

  public TimeWindowBuffer(int maxBufferSize, double timeWindow)
  {
    buffer = new int[maxBufferSize]; 
    timer = TickTimer(timeWindow);
  }

  public boolean add(int value)
  {
    synchronize(bufferSync)
    {
      buffer[timer.getTicks() % maxBufferSize] = value;
    }
  }

  public int averageValue()
  {
    int average = 0;

    synchronize(bufferSync)
    {
      for (int i: buffer)
      {
        average += i;
      }
    }

    return average/maxBufferSize;
  }
}

答案 1 :(得分:0)

您的问题可以概括为使用常量内存来计算流上的某些统计信息。

对我而言,这是一个堆(优先级队列),其中time为关键,value为值,顶部为time

收到新的(time,value)后,将其添加到堆中。如果堆大小大于缓冲区大小,只需删除堆中的根节点,直到堆足够小。

同样通过使用堆,您可以在O(1)时间内在缓冲区(即堆)中获得最小time,因此只需删除根(具有最小time的节点)直到所有过时的对都被清除。

对于统计信息,请保留整数sum。向堆中添加新对时,sum = sum + value of pair。从堆中删除根时,sum = sum - value of root