提问人:sbi 提问时间:3/24/2014 最后编辑:sbi 更新时间:10/27/2014 访问量:863
对于 24 小时逐分钟的布尔记录,什么是好的数据结构
What's a good data structure for a 24hr minute-by-minute boolean record
问:
我的任务是创建一个数据结构,该结构在过去 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
};
存储实际数据的好数据结构是什么?我目前最喜欢的是 和 24 的数组,其中 60 位中的 64 位每个 60 位用于 60 分钟的小时。后者是目前最爱的坚持。std::deque<bool>
long long
我想我对这两种想法的利弊都有一个很好的了解,但希望你们中的一些人能提供额外的内部信息,甚至更多的想法。
P.S.:这是严格的 C++03 + TR1 + Boost 1.52,没有可用的 C++11/14。
答:
我每小时都会有一个,而且每小时只放弃一次。所以你可以有一个.同样,它可能是一个,但与向量相比,我没有看到任何好处。std::vector<bool>
std::deque<std::vector<bool> >
std::deque<long long>
它使事情变得高效、易于理解且不易出错。
评论
-1
vector<bool>
bool
std::deque<long long>
为了详细说明版本,我认为这是一个不错的主意,因为您始终存储相同数量的数据(至少我是这样理解的):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)
您也可以使用普通数组而不是 ,但这需要更多空间(因为通常 C++ 存储值的方式与 ),或者一些位操作 - 在我看来 - 当我们获得专业化时重新发明轮子。vector<bool>
bool
char
vector<bool>
评论
+1
24*60
record_entry
data.size()
unsigned int x_occurance_minutes() const;
return std::count( data.begin(), data.end(), true );
data
data(24*60, false)
当您只关心事件发生在过去 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
评论
+1
循环缓冲区:
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;
};
评论
long long
+1
uint64_t
countBits()
x_record
我建议使用一个包含boost.dynamic_bitset的类、一个用于处理新值存储的计数器和一个用于通过小时/分钟访问的转换函数。
dynamic_set 可处理大多数要求:
- 提升,没有 C++11
- 紧凑的
- 处理 24*60 位
- 具有用于对设置位进行计数的 count() 函数
- 返回用于存储为位的块
- 没有 std::vector 的“容器”问题
评论
+1
正如评论中建议的那样,可能是一个不错的选择。它是一个固定大小的位序列,可以独立操作。它比 a 占用更少的内存(即使你认为你说这对你来说不是问题)。但是,如果您需要使序列呈圆形,则可能需要将其包装在另一个容器中,以便始终保留最后 24*60 分钟,而不是一天中的 24*60 分钟。std::bitset
std::vector<bool>
评论
正如马里乌斯·班西拉(Marius Bancila)首先在评论中建议的那样,然后在答案中建议(请去投票支持该答案,他给出的提示最终是解决方案),是理想的选择。然而,由于他的回答相当模糊,我决定对我最终使用的内容进行更具体的描述: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 事实证明,每隔几毫秒调用一次更容易,这似乎有些开销,所以我调用了它(每分钟调用一次)并缓存了结果。x_occurance_minutes()
record_.count()
record_entry()
评论
:)
vector<bool>(24*60, false)
std::bitset
protected
friend