对于24小时一分钟的布尔记录,什么是良好的数据结构

时间:2014-03-24 14:48:12

标签: c++ data-structures

我的任务是创建一个数据结构,在过去24小时的每一分钟都保存一个布尔值。 (事件X发生了吗?)我需要一直保持最后24小时。 (即,将不断添加数据,弹出旧数据。)数据将持久保存到闪存驱动器。我们在嵌入式平台上,但内存不是那么有限(我有128MB可用),但碎片可能会成为一个问题。这是一个实时系统,但由于记录是每分钟,因此几乎没有运行时限制。

界面看起来像这样:

class x_record {
  public:
    // record whether or not x occurred this minute
    void record_entry(bool x_occured);

    // how many minutes has x occured in the last 24hrs? 
    unsigned int x_occurance_minutes() const;

  private:
    // HERE BE DRAGONS 
};

用于存储实际数据的良好数据结构是什么?我目前最喜欢的是std::deque<bool>和一个24 long long的数组,其中64个中的60个每个用于60分钟的一小时。后者是目前持久性的最爱。

我认为我对这两种想法的利弊都有很好的了解,但希望你们中的一些人可以提供更多内容,甚至更多的想法。

P.S。:这是严格的C ++ 03 + TR1 + Boost 1.52,没有C ++ 11/14可用。

7 个答案:

答案 0 :(得分:9)

要详细说明vector<bool>版本,我认为这是一个非常好的主意,因为您总是存储相同数量的数据(至少是这样的)我的理解):

class x_record {
   vector<bool> data;
   unsigned int currentMinute;

public:
   x_record(): data(24*60, false), currentMinute(0){}

   // record whether or not x occurred this minute
   void record_entry(bool x_occured){
      data[currentMinute] = x_occured;
      currentMinute = (currentMinute+1)%data.size();
   }

};

这样,矢量大小是恒定的,因此不应该被分段(因为它是同时分配的)。您可以使用currentMinute变量跟踪当前分钟。当您填写所有字段时,只需将0设置为%(24*60)覆盖旧数据,因为您不需要它。

您也可以使用普通的数组而不是vector<bool>,但这需要更多空间(因为通常C ++存储bool值的方式与{ {1}}),或者当我们得到char专业化时,在我看来 - 重新发明轮子的一些操作。

答案 1 :(得分:6)

循环缓冲区:

int countBits(std::uint32_t v) {
  // source: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
  typedef std::uint32_t T;
  v = v - ((v >> 1) & (T)~(T)0/3);                           // temp
  v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);      // temp
  v = (v + (v >> 4)) & (T)~(T)0/255*15;                      // temp
  return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * CHAR_BIT; // count
}

class x_record {
  public:
    x_record() { std::memset(&history, 0, sizeof(history)); }

    // record whether or not x occurred this minute
    void record_entry(bool x_occured) {
      uint64_t mask = 1 << (bit % 32);
      uint64_t set = x_occured ? mask : 0;
      history[bit / 32] = (history[bit / 32] & ~mask) | set;

      bit = (bit + 1) % (24*60);
    }

    // how many minutes has x occured in the last 24hrs? 
    int x_occurance_minutes() const {
      int count = 0;
      for (int i=0; i<45; ++i) {
        count += countBits(history[i]);
      }
      return count;
    }

  private:
    std::uint32_t history[45];
    short bit = 0;
};

答案 2 :(得分:3)

我每小时都会有一个std::vector<bool>,而且每小时只有一次。所以你可以拥有std::deque<std::vector<bool> >。同样,它可能是std::deque<long long>,但与向量相比,我看不到任何好处。

它使事情变得高效,易于理解且不易出错。

答案 3 :(得分:3)

正如评论中所建议的,std::bitset可能是一个不错的选择。它是一个固定大小的位序列,可以独立操作。它比std::vector<bool>占用更少的内存(甚至认为你说这对你来说不是问题)。但是,如果您需要将序列设置为循环,则可能需要将其包装在另一个容器中,以便始终保持最后24 * 60分钟,而不是一天中的24 * 60分钟。

答案 4 :(得分:1)

如果您只担心事件在过去24小时内发生的频率,并且可以完全忽略事件发生的时间,您只需记录事件发生的时间。

考虑以下(未经测试):

class x_record {
public:
  // record whether or not x occurred this minute
  void record_entry(bool x_occured) {
    if (x_occured) {
      m_records.insert(getTime());
    }
  }

  // how many minutes has x occured in the last 24hrs? 
  unsigned int x_occurance_minutes() {
    clearRecords();
    return m_records.size();
  }

private:
    time_t getTime() const {
      return time(NULL) / 60; // Get Minute time stamp
    }

    void clearRecords() {
      // Erase all records that happend before the last 24 hours
      time_t frameStart = getTime() - 60*60*24;
      for (std::set<time_t>::iterator it = m_recods.begin(); it != m_records.end(); ++it) {
        if (*it < frameStart) {
          m_records.erase(it);
        } else {
          break;
        }
      }
    }

private:
    std::set<time_t> m_records;
};

如果事件稀疏发生,这是最合适的。

它使用约束来设置以严格的弱顺序存储它们的元素,以便首先列出具有较低时间戳的元素。还

您应该考虑使用不同的密钥类型插入集合,因为time_t不能保证代表秒。

答案 5 :(得分:1)

我建议使用一个包含boost.dynamic_bitset的类,一个用于处理新值存储的计数器和一个用于通过小时/分钟访问的转换函数。

dynamic_set处理大部分要求:

  • boost,no C ++ 11
  • 紧凑
  • 处理24 * 60位
  • 有一个count()函数用于计算设置位
  • 返回用于存储为位的块
  • 没有&#34;容器&#34; std :: vector
  • 的问题

答案 6 :(得分:1)

正如Marius Bancila首先提出in a comment然后in an answer所建议的那样(请尽快给出答案,他给出了原来是解决方案的提示),std::bitset<>是理想的为了这。但是,由于他的回答相当模糊,我决定对我最终使用的内容进行更具体的描述:

class x_record {
  public:
    void record_entry(bool x_occured) {
      record_ <<= 1;
      record_[0] = x_occurred;
    }

    unsigned int x_occurance_minutes() const {
      return record_.count();
    }

    void write_to(std::ostream& os) const {
      os << m_overload_minutes.to_string();
    }

    void read_from(std::istream& is) const {
      std::string bits;
      is >> std::setw(minutes_to_keep) >> bits;
      if( !is || bits.size()!=minutes_to_keep )
        throw std::runtime_error("invalid file format");

      record_ = std::bitset<60*24>(bits);
    }

  private:
    std::bitset<60*24>  record_;
};

正如您所看到的,std::bitset<>正是我所需要的。此外,持久化数据非常容易。

当然,实际上,这是一个更复杂的 1 ,但原则上这确实是整个事情。

感谢大家帮助我!

1 事实证明,每隔几个msecs就更容易调用x_occurance_minutes()record_.count()似乎有一些开销,所以我在{{1}中调用它(仅每分钟调用一次)代替并缓存结果。