Предположим, что в определенный момент времени у вас есть коллекция номеров N
и узнать медианный элемент: M
. Теперь вам дается новое значение, X
, и поэтому вам может потребоваться обновить M
. (Или, вернее, вам нужно будет, считая, что номера, с которыми вы имеете дело, уникальны. Кроме того, все образцы получены серийно, поэтому проблем с параллелизмом нет.)Алгоритм быстрого среднего обновления
Расчет нового среднего прост: возьмите старое среднее, добавьте X
, умножьте на N
и разделите на N + 1
. (Это ясно из проверки того, как определено среднее значение N элементов. На данный момент я не слишком беспокоюсь о цифрах.)
Мой вопрос: может кто-нибудь предложить творческий/роман (или, возможно, оптимальный) подход к проблеме обновления медианы? Я приведу пример (простая идея моего собственного дизайна) ниже, с небольшим анализом:
В этом примере я буду использовать std::forward_list
, так как C++ 11, где я столкнулся с этим совсем недавно. Без ограничения общности, я собираюсь предположить, что вы делаете это правильно: поддерживая упорядоченный список элементов (тип T), встречающихся до сих пор, std::forward_list<T> sorted;
Когда появляется T x;
, просто сложите его на место, используя:
sorted.merge(std::forward_list<T> {{ x }});
Кстати, мне любопытно, есть ли у кого лучший (более эффективный/элегантный) метод для этого. Приветствия приветствуются.
Итак, X
теперь является частью sorted
, и вот моя идея, в двух словах:
auto it = sorted.begin(), itend = sorted.end();
typename std::forward_list<T>::size_type count = std::distance(it, itend);
for (const auto &e : sorted) {
if (it == itend || ++it == itend) {
M = (count % 2) ? e : (e + M)/2;
break;
} else { ++it; }
}
Приятной (если не несколько трудно понять), что происходит здесь в том, что: так как вы переместите итератора дважды (и, если можно, смешно, я могу добавить, хотя и по цене двух сравнений) для каждого элемента, когда будет достигнуто end()
, мы будем иметь правильное (медианное) значение. Если есть нечетное число элементов, M
- это просто этот образец, если нет, это просто среднее значение этого элемента и старая (выталкиваемая) медиана. Поскольку нечетные и четные числа чередуются, либо старый, либо новый M
действительно будут в коллекции. Это рассуждение звучит, да?
Вам не нужно комментировать мой метод O (3n), если вы считаете, что это мусор/твой намного лучше; Я просто предлагаю это как отправную точку.
Вы, конечно, можете сделать это в O (журнал (п)) время. Добавление элемента в сбалансированное двоичное дерево и выбор k-го наибольшего элемента - это операции O (log (n)) времени. Для чего-то более интересного, возможно, попробуйте 2 кучи, один для n/2 самых больших элементов, один для самого маленького. –
@MarcGlisse: см. Мой ответ, вам не нужно искать элемент kth larges, потому что вы уже знаете его или (k + 1) -й или (k-1) -ый самый большой элемент :-) –
@SauceMaster кажется, вы отслеживаете элемент с большинством событий в списке. Это не медиана. –