Как аппроксимировать x-й процентиль для большого неизвестного количества чисел

Недавно наткнулся на этот вопрос о том, как найти x-й процентиль для данного потока чисел. У меня есть базовое понимание того, как этого можно было бы достичь, если бы поток был относительно небольшим (можно сохранить в памяти, отсортировать и найти значение x), но мне было интересно, как можно аппроксимировать процентиль, если поток чисел достаточно большой и количество чисел неизвестно.


person Bruce    schedule 30.08.2017    source источник
comment
Я не думаю, что вы можете сделать это без сохранения чисел (хотя и не обязательно в памяти).   -  person Henry    schedule 30.08.2017
comment
Вы знаете примерное распределение значений? Или жесткие ограничения?   -  person M Oehm    schedule 30.08.2017
comment
Нет, нет четкого указания на распределение значений, кроме диапазона, в котором будут появляться числа. Эти значения, по сути, представляют собой время отклика сервера, и, следовательно, было заявлено, что некоторые из значений времени отклика могут выглядеть немного не по порядку (но ответы, которые слишком не по порядку, могут быть отброшены).   -  person Bruce    schedule 30.08.2017


Ответы (1)


Я думаю, вы могли бы использовать выборку из резервуара, чтобы равномерно выбрать k элементов из потока S, а затем аппроксимировать xth процентиль S с x-м процентилем этих k чисел. k зависит от того, сколько у вас памяти и насколько точным должно быть приближение.


ИЗМЕНИТЬ

Вот пример кода для проверки решения:

// create random stream of numbers
Random random = new Random(0);
List<Integer> stream = new ArrayList<Integer>();
for (int i = 0; i < 100000; ++i) {
    stream.add((int) (random.nextGaussian() * 100 + 30));
}
// get approximate percentile
int k = 1000; // sample size
int x = 50; // percentile
// init priority queue for sampling
TreeMap<Double, Integer> queue = new TreeMap<Double, Integer>();
// sample k elements from stream
for (int val : stream) {
    queue.put(random.nextDouble(), val);
    if (queue.size() > k) {
        queue.pollFirstEntry();
    }
}
// get xth percentile from k samples
List<Integer> sample = new ArrayList<Integer>(queue.values());
Collections.sort(sample);
int approxPercent = sample.get(sample.size() * x / 100);
System.out.println("Approximate percentile: " + approxPercent);
// get real value of the xth percentile
Collections.sort(stream);
int percent = stream.get(stream.size() * x / 100);
System.out.println("Real percentile: " + percent);

Результат:

Приблизительный процентиль: 29

Реальный процентиль: 29

Я получил довольно хорошее приближение для каждого x, которое я использовал, и в настоящее время я не понимаю, почему оно не подходит для вашего случая.

person Aleksei Shestakov    schedule 30.08.2017
comment
Итак, в настоящее время я пытаюсь провести отбор проб из резервуара, при этом выбранные элементы сохраняются в массиве. Но кажется, что приближение все еще далеко от желаемого x-го процентиля. Итак, мне было интересно, может ли изменение структуры данных оптимизировать это в любом случае? Кроме того, элементы потока — это время отклика и так далее, хотя некоторые значения времени отклика могут отображаться не по порядку; как правило, они находятся в некотором порядке, и слишком неправильные ответы могут быть отброшены. Зная это, есть ли другой алгоритм выборки, который был бы лучше с учетом этого? - person Bruce; 03.09.2017
comment
@Bruce, я добавил в ответ пример кода. В настоящее время я не понимаю, почему это приближение не работает для вас. Может быть, вы можете привести пример потока? - person Aleksei Shestakov; 03.09.2017