在算法中,每当添加一个值时,我都必须计算数据集的第75个百分位数。现在我正在这样做:
x
array[array.size * 3/4]
点3是O(n),其余点是O(1),但这仍然很慢,尤其是当数组变大时。有什么办法可以优化这个?
更新
谢谢妮基塔!由于我使用的是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; };
您可以使用两个堆来完成。不确定是否有一个“人为”的解决方案,但是这种解决方案具有O(logn)时间复杂性,并且大多数编程语言的标准库中也包含堆。
O(logn)
第一个堆(堆A)包含最小的75%元素,另一个堆(堆B)-其余(最大25%)。第一个元素在顶部的位置最大,第二个元素-最小的位置。
查看new元素x是否为<= max(A)。如果是,请将其添加到heap A,否则添加到heap B。 现在,如果我们添加x到堆A并且它变得太大(容纳超过75%的元素),我们需要从A(O(logn))中删除最大的元素,然后将其添加到堆B(也是O(logn))中。 如果堆B太大,则类似。
max(A)
A
B
只需从A中选取最大的元素(或从B中选取最小的元素)。需要O(logn)或O(1)时间,具体取决于堆实现。
编辑 正如 Dolphin 所说,我们需要精确地指定每n个堆的大小(如果我们想要精确的答案)。例如,如果size(A) = floor(n * 0.75)和size(B)是余数,则对于n > 0,array[array.size * 3/4] = min(B)。
size(A) = floor(n * 0.75)
size(B)
n > 0
array[array.size * 3/4] = min(B)