В общем, это сложно сделать правильно, особенно для обработки вырожденных рядов, которые уже отсортированы или имеют набор значений в «начале» списка, но в конце списка есть значения в другом диапазоне.
Основная идея построения гистограммы является наиболее многообещающей. Это позволяет накапливать информацию о распределении и отвечать на запросы (например, медианы) из нее. Медиана будет приблизительной, поскольку вы, очевидно, не храните все значения. Место для хранения фиксировано, поэтому оно будет работать с любой последовательностью длины, которая у вас есть.
Но вы не можете просто построить гистограмму, скажем, из первых 100 значений и постоянно использовать эту гистограмму ... изменение данных может сделать эту гистограмму недействительной. Итак, вам нужна динамическая гистограмма, которая может изменять свой диапазон и интервалы на лету.
Создайте структуру с N ячейками. Вы сохраните значение X для каждого перехода слота (всего N + 1 значений), а также заполнение корзины.
Поток в ваших данных. Запишите первые N + 1 значений. Если поток заканчивается до этого, отлично, у вас загружены все значения, и вы можете найти точную медиану и вернуть ее. В противном случае используйте значения для определения вашей первой гистограммы. Просто отсортируйте значения и используйте их как определения бункеров, каждый бункер имеет популяцию 1. Это нормально, когда есть дубли (бины с 0 шириной).
Теперь добавьте новые значения. Для каждого из них выполняется двоичный поиск, чтобы найти корзину, к которой он принадлежит. В общем случае вы просто увеличиваете заполнение этого бункера и продолжаете. Если ваш образец выходит за границы гистограммы (самый высокий или самый низкий), просто расширьте диапазон конечного бина, чтобы включить его. Когда ваш поток готов, вы найдете среднее значение выборки, найдя ячейку с равным населением по обе стороны от нее и линейно интерполируя оставшуюся ширину ячейки.
Но этого недостаточно ... вам все равно нужно АДАПТИРОВАТЬ гистограмму к данным, которые передаются в потоке. Когда корзина переполняется, вы теряете информацию о подраспределении этой корзины. Вы можете исправить это, адаптировавшись на основе некоторой эвристики ... Самый простой и надежный - это если контейнер достигает определенного порогового значения (что-то вроде 10 * v / N, где v = # значений, наблюдаемых до сих пор в потоке, и N - количество ящиков), вы РАЗДЕЛИТЕ эту переполненную корзину. Добавьте новое значение в середину бункера, дайте каждой стороне половину исходной популяции бункера. Но теперь у вас слишком много корзин, поэтому вам нужно УДАЛИТЬ корзину. Хорошей эвристикой для этого является поиск корзины с наименьшим произведением численности населения и ширины. Удалите его и объедините с его левым или правым соседом (в зависимости от того, какой из соседей имеет наименьшее произведение ширины и населения). Выполнено! Обратите внимание, что при объединении или разделении ячеек информация теряется, но это неизбежно ... у вас есть только фиксированное хранилище.
Этот алгоритм хорош тем, что обрабатывает все типы входных потоков и дает хорошие результаты. Если у вас есть возможность выбрать порядок выборки, лучше всего использовать случайную выборку, поскольку это сводит к минимуму разбиение и слияние.
Алгоритм также позволяет запрашивать любой процентиль, а не только медиану, поскольку у вас есть полная оценка распределения.
Я использую этот метод в своем собственном коде во многих местах, в основном для журналов отладки ... где некоторые статистические данные, которые вы записываете, имеют неизвестное распределение. С этим алгоритмом вам не нужно гадать заранее.
Обратной стороной является неравная ширина бинов, что означает, что вам нужно выполнять двоичный поиск для каждого образца, поэтому ваш сетевой алгоритм - O (NlogN).
person
SPWorley
schedule
12.03.2009