在算法中,我必须在添加一个值时计算数据集的第75个百分位数。现在我正在这样做:
获取值x
x
swap
x
在位置上阅读元素
array[array.size * 3/4]
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;
};
您可以使用两个
aps来做到这一点。不确定是否有一个“人为人为”的解决方案,但是该解决方案提供了大多数编程语言的标准库中提供的时间复杂性和堆。 第一堆(堆A)包含最小的75%元素,另一个堆(堆B) - 其余(最大25%)。第一个具有最大的元素,第二个元素 - 最小。
请参阅新元素是否为O(logn)
。如果是这样,请将其添加到堆
x
,否则 - 堆max(A)
A
B
(o(logn))中删除最大元素,然后将其添加到堆B(也o(logn))
如果堆B变得太大,则类似。
找到“ 0.75中位数”
<=即将从A(或B中最小)获取最大元素。需要O(logn)或O(1)时间,具体取决于堆的实现。 eDit 正如dolphin
x
和A
,那么,对于每个size(A) = floor(n * 0.75)
。
最大的标准实现(例如Java的Treemap)是订单统计树。
如果您可以使用近似答案来完成,则可以使用直方图,而不是将整个值保持在内存中。
对于每个新值,将其添加到适当的垃圾箱中。
通过遍历垃圾箱并总结数量,直到达到人口规模的75%来计算百分位数。百分位数是在bin的(您停在)之间的低点之间的bin。
这将提供O(b)的复杂性,其中b是垃圾箱的计数,即size(B)
。 (使用
n > 0
适合您的用户案例)。我已经在JVM库中实现了此逻辑:
https://github.com/ibm/hbpe您可以用作参考。 您可以使用二进制搜索在O(log n)中找到正确的位置。 但是,将阵列移动仍然是O(n)。
创建了大量整数(甚至会起作用),其元素数量等于您的数据的最大值。
例如,如果t的最大值为100,000,则创建一个数组
array[array.size * 3/4] = min(B)
现在迭代整个值,as
range_size/bin_size
现在计算百分位数AS
here是一种JavaScript解决方案。在浏览器控制台中复制将其搭配起来。
int[] index = new int[100000]; // 400kb
包含分数列表,
for each (int t : set_of_values) {
index[t]++;
}
// You can do a try catch on ArrayOutOfBounds just in case :)
为列表的cives。因此,第75个百分位数为76.8,而99%为87.9。
int sum = 0, i = 0;
while (sum < 0.9*set_of_values.length) {
sum += index[i++];
}
return i;