Algorithm 百分位数重复计算的快速算法?
在一个算法中,每当我添加一个值时,我必须计算数据集的长度。现在,我正在这样做:Algorithm 百分位数重复计算的快速算法?,algorithm,optimization,data-structures,percentile,Algorithm,Optimization,Data Structures,Percentile,在一个算法中,每当我添加一个值时,我必须计算数据集的长度。现在,我正在这样做: 获取值x 在后面已排序的数组中插入x 向下交换x直到数组排序 读取位置array[array.size*3/4]处的元素 点3是O(n),其余的是O(1),但这仍然非常慢,特别是当数组变大时。有没有办法优化这个 更新 谢谢你,尼基塔!由于我使用C++,这是最容易实现的解决方案。代码如下: template<class T> class IterativePercentile { public: ///
x
x
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;
};
模板
类迭代百分位数{
公众:
///百分位数必须在[0,1]范围内(
迭代百分位数(双百分位数)
:_百分位(百分位)
{ }
//在O(log(n))中添加一个数字
无效添加(常量T&x){
如果(_lower.empty()| | x size_lower){
//从低到高
std::pop_heap(_lower.begin(),_lower.end(),std::less());
_上。推回(_下。回());
std::push_heap(_upper.begin(),_upper.end(),std::greater());
_放低。弹回();
}否则如果(\u lower.size()
您可以使用二进制搜索在O(logn)中找到正确的位置。但是,向上移动数组仍然是O(n)。您可以用两个数组来完成。不确定是否有“人为”解决方案,但此解决方案提供了O(logn)
时间复杂性和堆也包含在大多数编程语言的标准库中
第一个堆(堆A)包含最小的75%元素,另一个堆(堆B)-其余的(最大的25%)。第一个堆的顶部有最大的元素,第二个堆的顶部有最小的元素
x
是否为0,array[array.size*3/4]=min(B)
简单即可
此树的平衡版本支持O(logn)时间插入/删除和按等级访问。因此,您不仅可以获得75%的百分位数,还可以获得66%或50%的百分位数或任何您需要的值,而无需更改代码
如果频繁访问75%的百分位数,但插入频率较低,则在插入/删除操作期间始终可以缓存75%的百分位数元素
大多数标准实现(如Java的TreeMap)都是顺序统计树。如果您有一组已知的值,则以下内容将非常快速: 创建一个大整数数组(即使是字节也可以),其元素数等于数据的最大值。 例如,如果t的最大值为100000,则创建一个数组
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 :)
现在将百分位数计算为
int sum = 0, i = 0;
while (sum < 0.9*set_of_values.length) {
sum += index[i++];
}
return i;
int和=0,i=0;
while(总和<0.9*设置值长度){
总和+=指数[i++];
}
返回i;
也可以考虑使用TeaMeP而不是数组,如果这些值不符合这些限制。
< P>这是一个JavaScript解决方案。复制粘贴在浏览器控制台中,它工作。<代码> $得分<代码>包含列表,并且<代码>百分位数< /代码>给出列表的<代码>第n个百分位< /代码>。因此第七十五百分位是76.8和99%为87.9function get_percentile($percentile, $array) {
$array = $array.sort();
$index = ($percentile/100) * $array.length;
if (Math.floor($index) === $index) {
$result = ($array[$index-1] + $array[$index])/2;
}
else {
$result = $array[Math.floor($index)];
}
return $result;
}
$scores = [22.3, 32.4, 12.1, 54.6, 76.8, 87.3, 54.6, 45.5, 87.9];
get_percentile(75, $scores);
get_percentile(90, $scores);
如果你可以用一个近似的答案,你可以使用直方图而不是把整个值保存在内存中 对于每个新值,将其添加到相应的bin中。 通过遍历存储箱并对计数求和计算第75个百分位,直到达到人口规模的75%。百分位值介于存储箱(您停止在)下限和上限之间 这将提供O(B)复杂度,其中B是存储箱的计数,即
范围大小/bin大小
(根据您的用户情况使用bin大小
)
我已经在JVM库中实现了这个逻辑:您可以将其用作参考。但是如何确定堆a是否变得太大?@Raze2dust堆a应该包含大约75%的元素。如果它的大小超过这个值,它就会变得太大。@Raze2dust如果您的意思是“如何获得堆大小”,它是一个O(1)操作:)我认为这个想法会奏效,但我认为有必要做一些修改。首先,其中一个堆上应该始终有您要查找的项目。这样,您就可以计算出给定数量的元素的每个堆的大小
堆a=floor(n*.75)和堆B=ceil(n*.25)
(在本例中)。接下来,当您添加一个项时,确定哪个堆需要增长。如果堆A需要增长,并且该项小于B的顶部,请将其添加到A。否则,请删除B的顶部,将其添加到A,然后将新项添加到B。(删除然后添加作为修改更有效)@Nikita-不,只是一些调整。定义应该增长的堆使添加操作稍微简单一些(您的添加可以执行3个O(logn)操作(添加、删除、添加)。我的建议是两个(修改、添加)在最坏的情况下。你选择哪个堆并不重要,但是选择一个小堆来存放物品将使堆的大小更接近,从而获得(可能微不足道的)性能提升。很好,我最近在一次采访中遇到了一个类似的问题。Nikita已经给出了我的答案。@Alexandru:类似!=相同:-)我认为这里不需要堆解决方案。它可能适用于:,但我认为它是一个mis应用程序。我认为:中存在未定义的行为:如果(_lower.empty()| | x@davide)计算顺序定义良好,如果_lower.empty()
返回true,则右侧为否