アルゴリズムでは、値を追加するたびに、データセットの 75パーセンタイル を計算する必要があります。今私はこれをやっています:
x
x
をすでにソートされた配列の後ろに挿入します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;
};
あなたは2つでそれを行うことができます ヒープ 。 「工夫された」解決策が少ないかどうかはわかりませんが、これはO(logn)
時間計算量を提供し、ヒープはほとんどのプログラミング言語の標準ライブラリにも含まれています。
最初のヒープ(ヒープA)には最小の75%の要素が含まれ、別のヒープ(ヒープB)には残り(最大の25%)が含まれます。最初の要素は上部に最大の要素があり、2番目の要素は最小です。
新しい要素x
が<= max(A)
であるかどうかを確認します。そうである場合は、ヒープA
に追加し、そうでない場合は、ヒープB
に追加します。
ここで、ヒープAにx
を追加し、それが大きくなりすぎた場合(要素の75%以上を保持)、A
から最大の要素を削除する必要があります(O( logn))そしてそれをヒープB(またO(logn))に追加します。
ヒープ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)
。
単純な 注文統計ツリー で十分です。
このツリーのバランスの取れたバージョンは、O(logn)時間の挿入/削除とランクによるアクセスをサポートします。したがって、75%のパーセンタイルだけでなく、66%または50%なども取得できます。コードを変更せずに必要です。
75%パーセンタイルに頻繁にアクセスするが、挿入頻度は低い場合は、挿入/削除操作中にいつでも75%パーセンタイル要素をキャッシュできます。
ほとんどの標準的な実装(JavaのTreeMapなど)は、順序統計ツリーです。