在一个算法中,我需要每次添加一个值时计算数据集的 75th 百分位数。目前我正在这样做:
- 获取值
x
- 将
x
插入到已经排序好的数组的末尾 - 将
x
向下交换,直到数组排序完成 - 读取位置为
array[array.size * 3/4]
的元素
第3步的时间复杂度为 O(n),其余时间复杂度为 O(1),但仍然很慢,特别是当数组变得更大时。有没有什么方法可以优化这个过程?
更新
谢谢 Nikita!由于我正在使用 C++,这是最容易实现的解决方案。以下是代码:
template<class T>
class IterativePercentile {
public:
/// Percentile has to be in range [0, 1(
IterativePercentile(double percentile)
: _percentile(percentile)
{ }
// Adds a number in O(log(n))
void add(const T& x) {
if (_lower.empty() || x <= _lower.front()) {
_lower.push_back(x);
std::push_heap(_lower.begin(), _lower.end(), std::less<T>());
} else {
_upper.push_back(x);
std::push_heap(_upper.begin(), _upper.end(), std::greater<T>());
}
unsigned size_lower = (unsigned)((_lower.size() + _upper.size()) * _percentile) + 1;
if (_lower.size() > size_lower) {
// lower to upper
std::pop_heap(_lower.begin(), _lower.end(), std::less<T>());
_upper.push_back(_lower.back());
std::push_heap(_upper.begin(), _upper.end(), std::greater<T>());
_lower.pop_back();
} else if (_lower.size() < size_lower) {
// upper to lower
std::pop_heap(_upper.begin(), _upper.end(), std::greater<T>());
_lower.push_back(_upper.back());
std::push_heap(_lower.begin(), _lower.end(), std::less<T>());
_upper.pop_back();
}
}
/// Access the percentile in O(1)
const T& get() const {
return _lower.front();
}
void clear() {
_lower.clear();
_upper.clear();
}
private:
double _percentile;
std::vector<T> _lower;
std::vector<T> _upper;
};
if (_lower.empty() || x <= _lower.front()) {
,因为其求值顺序是未定义的。 - davide_lower.empty()
返回true,则右侧不会被求值。 - martinus&&
和||
是一个例外,它们保证了求值的顺序。但是需要注意的是,如果它们被重载为方法,则可能会反转或不保证求值的顺序,但这在这里并不是问题。我将参考 SO 上的这个优秀答案 来解决这个问题。 - davide