增量计算与提前计算¶
增量计算与提前计算
增量计算是指仅对变化的数据部分进行处理,而非重新计算整个数据集。
提前计算(也称为预计算)则是在运行时需求出现之前预先计算并存储结果,以避免在关键路径上进行实时计算。预计算在C++这种编译型语言也叫编译期计算,通过const派系的特性、TMP技术可以实现,在基础部分有提到多种方法实现。
这两种计算模式常常结合使用:提前计算静态或半静态数据,增量计算动态变化数据。
增量计算的最常见的场景就是移动平均数、中位数、最大值、最小值等指标的计算。
实现增量计算的关键在于高效的数据结构选择和变化跟踪机制。
#include <deque>
#include <iostream>
#include <set>
#include <vector>
class DataStatistics {
private:
std::deque<int> window;
int maxSize;
long long sum;
std::deque<int> maxDeque; // 用于滑动窗口的最大值
std::deque<int> minDeque; // 用于滑动窗口的最小值
// 用于中位数计算 - 使用multiset维护有序状态
std::multiset<int> lowerHalf; // 最大堆(使用反向迭代器)
std::multiset<int> upperHalf; // 最小堆
public:
DataStatistics(int size) : maxSize(size), sum(0) {}
void addData(int value) {
if (maxSize == 0) {
return;
}
if (window.size() >= maxSize) {
removeOldest();
}
window.push_back(value);
sum += value;
while (!maxDeque.empty() && maxDeque.back() < value) {
maxDeque.pop_back();
}
maxDeque.push_back(value);
while (!minDeque.empty() && minDeque.back() > value) {
minDeque.pop_back();
}
minDeque.push_back(value);
addToMedianStructure(value);
}
double getMovingAverage() {
if (window.empty()) return 0.0;
return static_cast<double>(sum) / window.size();
}
int getMax() {
if (window.empty()) return 0;
return maxDeque.front();
}
int getMin() {
if (window.empty()) return 0;
return minDeque.front();
}
double getMedian() {
if (window.empty()) return 0.0;
if (lowerHalf.size() == upperHalf.size()) {
if (lowerHalf.empty()) return 0.0;
return (*lowerHalf.rbegin() + *upperHalf.begin()) / 2.0;
} else {
return *lowerHalf.rbegin();
}
}
int getCurrentSize() { return window.size(); }
std::vector<int> getData() {
return std::vector<int>(window.begin(), window.end());
}
private:
void removeOldest() {
if (window.empty()) return;
int oldestValue = window.front();
sum -= oldestValue;
window.pop_front();
if (!maxDeque.empty() && maxDeque.front() == oldestValue) {
maxDeque.pop_front();
}
if (!minDeque.empty() && minDeque.front() == oldestValue) {
minDeque.pop_front();
}
removeFromMedianStructure(oldestValue);
}
void addToMedianStructure(int value) {
if (lowerHalf.empty() || value <= *lowerHalf.rbegin()) {
lowerHalf.insert(value);
} else {
upperHalf.insert(value);
}
balanceHeaps();
}
void removeFromMedianStructure(int value) {
auto it_lower = lowerHalf.find(value);
if (it_lower != lowerHalf.end()) {
lowerHalf.erase(it_lower);
} else {
auto it_upper = upperHalf.find(value);
if (it_upper != upperHalf.end()) {
upperHalf.erase(it_upper);
}
}
balanceHeaps();
}
void balanceHeaps() {
// 确保lowerHalf的大小要么与upperHalf相同,要么比它多一个
if (lowerHalf.size() > upperHalf.size() + 1) {
int moved = *lowerHalf.rbegin();
lowerHalf.erase(std::prev(lowerHalf.end()));
upperHalf.insert(moved);
} else if (upperHalf.size() > lowerHalf.size()) {
int moved = *upperHalf.begin();
upperHalf.erase(upperHalf.begin());
lowerHalf.insert(moved);
}
}
};