Середній алгоритм прокатки в С


114

В даний час я працюю над алгоритмом, щоб реалізувати середній фільтр кочення (аналогічний фільтру середнього кочення) у C. З мого пошуку літератури, мабуть, є два досить ефективні способи зробити це. Перший - сортувати початкове вікно значень, потім виконати двійковий пошук, щоб вставити нове значення та видалити існуюче під час кожної ітерації.

Другий (від Hardle and Steiger, 1995, JRSS-C, Алгоритм 296) будує структуру купи подвійного кінця, з максипазом на одному кінці, на другому кінці, а посередньою в середині. Це дає алгоритм лінійного часу замість того, що є O (n log n).

Ось моя проблема: реалізація першої можлива, але мені потрібно це виконувати на мільйонах часових рядів, тому ефективність має велике значення. Останнє виявляється дуже важким у виконанні. Я знайшов код у файлі Trunmed.c коду для пакету статистики R, але він досить нерозбірливий.

Хтось знає про добре написану реалізацію C для лінійного алгоритму лінійної прокатки часу?

Редагувати: посилання на код Trunmed.c http://google.com/codesearch/p?hl=uk&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c


Тільки що реалізований рухомий засіб ... медіана переміщення дещо складніше. Спробуйте посередньо рухатися медіаною Google.
Метт

Спробували пошук у коді google та google. Він виявив код Trunmed.c та реалізацію іншою мовою для порту SGI коду Trunmed (з того, що я міг сказати). Крім того, алгоритм JRSS, який я цитував, мабуть, єдиний у серії журналу, для якого оригінальний код не був заархівований.
AWB

Скільки цифр у кожного часового ряду? Навіть при мільйоні з них, якщо у вас є лише кілька тисяч номерів, це може зайняти не більше хвилини або двох (якщо ваш код написаний ефективно).
Dana Sane

16
як лінійне рішення двох купи? це O (n log k), де k - розмір вікна, оскільки видалення купи - O (log k).
yairchu

3
Деякі реалізації та порівняння: github.com/suomela/median-filter
Jukka Suomela

Відповіді:


28

Я src/library/stats/src/Trunmed.cдекілька разів переглянув R, коли мені хотілося чогось подібного в самостійній підпрограмі C ++ / C / C. Зауважте, що це насправді дві реалізації в одній, див. src/library/stats/man/runmed.Rd(Джерело файлу довідки), який говорить

\details{
  Apart from the end values, the result \code{y = runmed(x, k)} simply has
  \code{y[j] = median(x[(j-k2):(j+k2)])} (k = 2*k2+1), computed very
  efficiently.

  The two algorithms are internally entirely different:
  \describe{
    \item{"Turlach"}{is the Härdle-Steiger
      algorithm (see Ref.) as implemented by Berwin Turlach.
      A tree algorithm is used, ensuring performance \eqn{O(n \log
        k)}{O(n * log(k))} where \code{n <- length(x)} which is
      asymptotically optimal.}
    \item{"Stuetzle"}{is the (older) Stuetzle-Friedman implementation
      which makes use of median \emph{updating} when one observation
      enters and one leaves the smoothing window.  While this performs as
      \eqn{O(n \times k)}{O(n * k)} which is slower asymptotically, it is
      considerably faster for small \eqn{k} or \eqn{n}.}
  }
}

Було б непогано побачити це повторне використання в більш самостійному режимі. Ви займаєтесь волонтерством? Я можу допомогти з деякими бітами R.

Редагувати 1 : Окрім посилання на старішу версію Trunmed.c, наведену вище, ось поточні копії SVN

  • Srunmed.c (для версії Stuetzle)
  • Trunmed.c (для версії Turlach)
  • runmed.R для функції R, що викликає ці

Редагування 2 : Райан Тібшірані має деякий код C і Fortran для швидкого серединного бінінгу, що може бути підходящою відправною точкою для віконного підходу.


Дякую Дірк. Після отримання чистого рішення я планую випустити його під GPL. Мені було б цікаво також налаштувати інтерфейси R та Python.
AWB

9
@AWB Що закінчилося з цією ідеєю? Ви включили своє рішення в пакет?
Сюй Ван

20

Я не зміг знайти сучасну реалізацію структури даних c ++ зі статистикою замовлень, і в результаті реалізували обидві ідеї у верхньому посиланні кодерів, запропонованому MAK ( Редакція відповідності : прокрутка вниз до FloatingMedian).

Два мультисети

Перша ідея розділяє дані на дві структури даних (купи, мультисети тощо) з O (ln N) на вставку / видалення не дозволяє динамічно змінювати квантил без великих витрат. Тобто у нас може бути середня котиться або 75% кочення, але не обидва одночасно.

Сегментне дерево

У другій ідеї використовується сегментне дерево, яке є O (ln N) для вставки / видалення / запитів, але є більш гнучким. Найкраще "N" - це розмір вашого діапазону даних. Отже, якщо у вашій медіані прокатки є вікно з мільйоном елементів, але ваші дані коливаються від 1..65536, тоді потрібно лише 16 операцій на 1 рух мільйона вікна !!

Код c ++ схожий на те, що Деніс розмістив вище ("Ось простий алгоритм квантованих даних")

GNU Замовлення статистичних дерев

Перед тим, як відмовитись, я виявив, що stdlibc ++ містить статистичні дерева порядку !!!

У них дві критичні операції:

iter = tree.find_by_order(value)
order = tree.order_of_key(value)

Див . Посібник з libstdc ++ посібником policy_based_data_structures_test (пошук "розділити та приєднатися").

Я загорнув дерево для використання в заголовку зручності для компіляторів, що підтримують часткові typedefs стилю c ++ 0x / c ++ 11:

#if !defined(GNU_ORDER_STATISTIC_SET_H)
#define GNU_ORDER_STATISTIC_SET_H
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

// A red-black tree table storing ints and their order
// statistics. Note that since the tree uses
// tree_order_statistics_node_update as its update policy, then it
// includes its methods by_order and order_of_key.
template <typename T>
using t_order_statistic_set = __gnu_pbds::tree<
                                  T,
                                  __gnu_pbds::null_type,
                                  std::less<T>,
                                  __gnu_pbds::rb_tree_tag,
                                  // This policy updates nodes'  metadata for order statistics.
                                  __gnu_pbds::tree_order_statistics_node_update>;

#endif //GNU_ORDER_STATISTIC_SET_H

Насправді контейнери з розширенням libstdc ++ не допускають кількох значень! Як запропоновано моїм іменем вище (t_order_statistic_set), кілька значень об'єднуються. Отже, їм потрібно трохи більше працювати для наших цілей :-(
Лев Гудштадт

Нам потрібно 1) скласти карту значень для підрахунку (замість наборів) 2) розміри гілок повинні відображати кількість ключів (libstdc ++ - v3 / include / ext / pb_ds / деталь / tree_policy / order_statistics_imp.hpp) успадкувати від дерево, і 3) вставити перевантаження (), щоб збільшити кількість / виклик update_to_top (), якщо значення вже присутнє 4) перевантаження стерти (), щоб зменшити кількість / виклик update_to_top (), якщо значення не унікальне (Див. libstdc ++ - v3 / включати / ext / pb_ds / detail / rb_tree_map_ / rb_tree_.hpp) Будь-які добровольці ??
Лев Гудштадт

15

Я зробив реалізацію C тут . Ще кілька деталей є у цьому питанні: Медіана кочення в реалізації С - Терлач .

Використання зразка:

int main(int argc, char* argv[])
{
   int i,v;
   Mediator* m = MediatorNew(15);

   for (i=0;i<30;i++)
   {
      v = rand()&127;
      printf("Inserting %3d \n",v);
      MediatorInsert(m,v);
      v=MediatorMedian(m);
      printf("Median = %3d.\n\n",v);
      ShowTree(m);
   }
}

6
Чудова, швидка та чітка реалізація, заснована на наборі міні-медіан-макс. Дуже гарна робота.
Йоханнес Рудольф

Як я можу знайти версію Java для цього рішення?
Hengameh

10

Я використовую цей додатковий медіанський оцінювач:

median += eta * sgn(sample - median)

який має таку ж форму, як і більш поширений середній оцінювач:

mean += eta * (sample - mean)

Тут eta - невеликий параметр швидкості навчання (наприклад 0.001), і sgn()це функція signum, яка повертає один із {-1, 0, 1}. (Використовуйте etaподібну константу, якщо дані нестаціонарні, і ви хочете відслідковувати зміни в часі; інакше для стаціонарних джерел використовуйте щось на зразок eta = 1 / nконвергенції, де nкількість вибірок, що спостерігаються до цього часу.)

Також я змінив серединний оцінювач, щоб він працював для довільних квантових значень. Загалом, квантильна функція повідомляє вам значення, яке ділить дані на два дроби: pі 1 - p. Наступне оцінює це значення поступово:

quantile += eta * (sgn(sample - quantile) + 2.0 * p - 1.0)

Значення pмає бути в межах [0, 1]. Це, по суті, зміщує sgn()симетричний вихід функції {-1, 0, 1}на нахил в бік, розділяючи зразки даних на два відрізки нерівного розміру (дроби pта 1 - pдані менше / більше, ніж кількісна оцінка відповідно). Зауважте, що для p = 0.5цього це зводиться до медіанного оцінювача.


2
Класно, Ось модифікація, яка коригує 'eta' на основі середнього бігу ... (середнє значення використовується як приблизна оцінка медіани, тому вона конвергується на великі значення з тією ж швидкістю, що і на крихітні значення). тобто настройка налаштовується автоматично. stackoverflow.com/questions/11482529/…
Джефф МакКлінток

3
Для подібної методики дивіться цей документ про скромне потокове передавання: arxiv.org/pdf/1407.1121v1.pdf Він може оцінити будь-який квартал і адаптується до змін в середньому. Це вимагає, щоб ви зберігали лише два значення: останню оцінку та напрямок останнього коригування (+1 або -1). Алгоритм у виконанні простий. Я вважаю, що похибка знаходиться в межах 5% приблизно 97% часу.
Пол Черноч

9

Ось простий алгоритм квантованих даних (місяці пізніше):

""" median1.py: moving median 1d for quantized, e.g. 8-bit data

Method: cache the median, so that wider windows are faster.
    The code is simple -- no heaps, no trees.

Keywords: median filter, moving median, running median, numpy, scipy

See Perreault + Hebert, Median Filtering in Constant Time, 2007,
    http://nomis80.org/ctmf.html: nice 6-page paper and C code,
    mainly for 2d images

Example:
    y = medians( x, window=window, nlevel=nlevel )
    uses:
    med = Median1( nlevel, window, counts=np.bincount( x[0:window] ))
    med.addsub( +, - )  -- see the picture in Perreault
    m = med.median()  -- using cached m, summ

How it works:
    picture nlevel=8, window=3 -- 3 1s in an array of 8 counters:
        counts: . 1 . . 1 . 1 .
        sums:   0 1 1 1 2 2 3 3
                        ^ sums[3] < 2 <= sums[4] <=> median 4
        addsub( 0, 1 )  m, summ stay the same
        addsub( 5, 1 )  slide right
        addsub( 5, 6 )  slide left

Updating `counts` in an `addsub` is trivial, updating `sums` is not.
But we can cache the previous median `m` and the sum to m `summ`.
The less often the median changes, the faster;
so fewer levels or *wider* windows are faster.
(Like any cache, run time varies a lot, depending on the input.)

See also:
    scipy.signal.medfilt -- runtime roughly ~ window size
    http://stackoverflow.com/questions/1309263/rolling-median-algorithm-in-c

"""

from __future__ import division
import numpy as np  # bincount, pad0

__date__ = "2009-10-27 oct"
__author_email__ = "denis-bz-py at t-online dot de"


#...............................................................................
class Median1:
    """ moving median 1d for quantized, e.g. 8-bit data """

    def __init__( s, nlevel, window, counts ):
        s.nlevel = nlevel  # >= len(counts)
        s.window = window  # == sum(counts)
        s.half = (window // 2) + 1  # odd or even
        s.setcounts( counts )

    def median( s ):
        """ step up or down until sum cnt to m-1 < half <= sum to m """
        if s.summ - s.cnt[s.m] < s.half <= s.summ:
            return s.m
        j, sumj = s.m, s.summ
        if sumj <= s.half:
            while j < s.nlevel - 1:
                j += 1
                sumj += s.cnt[j]
                # print "j sumj:", j, sumj
                if sumj - s.cnt[j] < s.half <= sumj:  break
        else:
            while j > 0:
                sumj -= s.cnt[j]
                j -= 1
                # print "j sumj:", j, sumj
                if sumj - s.cnt[j] < s.half <= sumj:  break
        s.m, s.summ = j, sumj
        return s.m

    def addsub( s, add, sub ):
        s.cnt[add] += 1
        s.cnt[sub] -= 1
        assert s.cnt[sub] >= 0, (add, sub)
        if add <= s.m:
            s.summ += 1
        if sub <= s.m:
            s.summ -= 1

    def setcounts( s, counts ):
        assert len(counts) <= s.nlevel, (len(counts), s.nlevel)
        if len(counts) < s.nlevel:
            counts = pad0__( counts, s.nlevel )  # numpy array / list
        sumcounts = sum(counts)
        assert sumcounts == s.window, (sumcounts, s.window)
        s.cnt = counts
        s.slowmedian()

    def slowmedian( s ):
        j, sumj = -1, 0
        while sumj < s.half:
            j += 1
            sumj += s.cnt[j]
        s.m, s.summ = j, sumj

    def __str__( s ):
        return ("median %d: " % s.m) + \
            "".join([ (" ." if c == 0 else "%2d" % c) for c in s.cnt ])

#...............................................................................
def medianfilter( x, window, nlevel=256 ):
    """ moving medians, y[j] = median( x[j:j+window] )
        -> a shorter list, len(y) = len(x) - window + 1
    """
    assert len(x) >= window, (len(x), window)
    # np.clip( x, 0, nlevel-1, out=x )
        # cf http://scipy.org/Cookbook/Rebinning
    cnt = np.bincount( x[0:window] )
    med = Median1( nlevel=nlevel, window=window, counts=cnt )
    y = (len(x) - window + 1) * [0]
    y[0] = med.median()
    for j in xrange( len(x) - window ):
        med.addsub( x[j+window], x[j] )
        y[j+1] = med.median()
    return y  # list
    # return np.array( y )

def pad0__( x, tolen ):
    """ pad x with 0 s, numpy array or list """
    n = tolen - len(x)
    if n > 0:
        try:
            x = np.r_[ x, np.zeros( n, dtype=x[0].dtype )]
        except NameError:
            x += n * [0]
    return x

#...............................................................................
if __name__ == "__main__":
    Len = 10000
    window = 3
    nlevel = 256
    period = 100

    np.set_printoptions( 2, threshold=100, edgeitems=10 )
    # print medians( np.arange(3), 3 )

    sinwave = (np.sin( 2 * np.pi * np.arange(Len) / period )
        + 1) * (nlevel-1) / 2
    x = np.asarray( sinwave, int )
    print "x:", x
    for window in ( 3, 31, 63, 127, 255 ):
        if window > Len:  continue
        print "medianfilter: Len=%d window=%d nlevel=%d:" % (Len, window, nlevel)
            y = medianfilter( x, window=window, nlevel=nlevel )
        print np.array( y )

# end median1.py

4

Медіану кочення можна знайти, підтримуючи два розділи чисел.

Для підтримки розділів використовуйте Min Heap та Max Heap.

Max Heap міститиме числа менші, ніж рівні медіани.

Min Heap міститиме числа, більші від медіани.

Балансування обмеження: якщо загальна кількість елементів є рівною, то обидві купи повинні мати рівні елементи.

якщо загальна кількість елементів непарна, то Max Heap матиме ще один елемент, ніж Min Heap.

Середній елемент: Якщо обидва розділи мають рівну кількість елементів, то медіана буде складати половину суми max елемента з першого розділу та min елемента з другого розділу.

В іншому випадку медіана буде максимальним елементом з першого розділу.

Алгоритм-
1- Візьміть дві купи (1 хв. Хв. І 1 макс. Купи)
   Max Heap міститиме кількість перших половин елементів
   Min Heap буде містити кількість елементів другої половини

2- Порівняйте нове число з потоку з вершиною Max Heap, 
   якщо вона менша або дорівнює, додайте це число в макс. 
   В іншому випадку додайте число в Min Heap.

3- якщо у min Heap більше елементів, ніж у Max Heap 
   потім видаліть верхній елемент Min Heap і додайте в Max Heap.
   якщо max Heap має більше одного елемента, ніж у Min Heap 
   потім видаліть верхній елемент Max Heap і додайте в Min Heap.

4- Якщо обидві купи мають рівну кількість елементів, то
   медіана буде складати половину суми max елемента з Max Heap та min element від Min Heap.
   В іншому випадку медіана буде максимальним елементом з першого розділу.
public class Solution {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        RunningMedianHeaps s = new RunningMedianHeaps();
        int n = in.nextInt();
        for(int a_i=0; a_i < n; a_i++){
            printMedian(s,in.nextInt());
        }
        in.close();       
    }

    public static void printMedian(RunningMedianHeaps s, int nextNum){
            s.addNumberInHeap(nextNum);
            System.out.printf("%.1f\n",s.getMedian());
    }
}

class RunningMedianHeaps{
    PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
    PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(Comparator.reverseOrder());

    public double getMedian() {

        int size = minHeap.size() + maxHeap.size();     
        if(size % 2 == 0)
            return (maxHeap.peek()+minHeap.peek())/2.0;
        return maxHeap.peek()*1.0;
    }

    private void balanceHeaps() {
        if(maxHeap.size() < minHeap.size())
        {
            maxHeap.add(minHeap.poll());
        }   
        else if(maxHeap.size() > 1+minHeap.size())
        {
            minHeap.add(maxHeap.poll());
        }
    }

    public void addNumberInHeap(int num) {
        if(maxHeap.size()==0 || num <= maxHeap.peek())
        {
            maxHeap.add(num);
        }
        else
        {
            minHeap.add(num);
        }
        balanceHeaps();
    }
}

Мені не зрозуміло, яку користь надає третя відповідь на Яву на питання C. Вам слід задати нове запитання, а потім надіслати свою відповідь Java на це питання.
jww

логіка загинула після прочитання цього "потім видаліть верхній елемент Min Heap та додайте в Min Heap". .Та якнайменше люб’язно прочитайте альго перед публікацією
Cyclotron3x3

4
Цей алгоритм не для медіани, що котиться, а для медіани зростаючої кількості елементів. Для котячої медіани також потрібно видалити елемент із купи, який потрібно знайти спочатку.
Вальтер

2

Можливо, варто зазначити, що існує особливий випадок, який має просте точне рішення: коли всі значення в потоці є цілими числами у (відносно) малому визначеному діапазоні. Наприклад, припустимо, що всі вони повинні лежати між 0 і 1023. У цьому випадку просто визначте масив з 1024 елементів та кількість, і очистіть усі ці значення. Для кожного значення в збільшенні потоку відповідний бін і кількість. Після закінчення потоку знайдіть бін, який містить найвище значення count / 2 - це легко досягти шляхом додавання послідовних бункерів, починаючи з 0. Використовуючи той самий метод, можна знайти значення довільного рангового порядку. (Існує незначне ускладнення, якщо під час запуску знадобиться виявлення насичення бака та "підвищення" розміру контейнерів для зберігання до більшого типу.)

Цей особливий випадок може здатися штучним, але на практиці це дуже часто. Він також може бути застосований як наближення до дійсних чисел, якщо вони лежать в межах діапазону і відомий "досить хороший" рівень точності. Це буде майже для будь-якого набору вимірювань на групі об'єктів "реального світу". Наприклад, висоти або ваги групи людей. Не достатньо великий набір? Він би працював так само добре для довжини або ваги всіх (окремих) бактерій на планеті - якщо припустити, що хтось може надати дані!

Схоже, я неправильно прочитав оригінал - який, здається, хоче отримати медіану ковзаючого вікна замість справедливої ​​медіани дуже довгого потоку. Цей підхід досі працює для цього. Завантажте перші значення N потоку для початкового вікна, а потім для збільшення значення N + 1-го потоку, збільшивши відповідний бін, зменшуючи бін, що відповідає значенню 0-го потоку. У цьому випадку необхідно зберегти останні N значень, щоб дозволити декремент, що можна зробити ефективно, циклічно адресуючи масив розміром N. Оскільки положення медіани може змінюватися лише на -2, -1,0,1 , 2 на кожному кроці розсувного вікна, не потрібно підсумовувати всі бункери до медіани на кожному кроці, просто відрегулюйте "медіанний вказівник" залежно від того, які бічні бінки були змінені. Наприклад, якщо і нове значення, і вилучене падають нижче поточної медіани, воно не змінюється (зсув = 0). Метод руйнується, коли N стає занадто великим, щоб зручно зберігати його в пам'яті.


1

Якщо у вас є можливість посилатись на значення як функцію точок у часі, ви можете вибирати значення із заміною, застосовуючи завантажувальну завантаження для генерації завантаженого середнього значення в довірчих інтервалах. Це може дозволити вам обчислити приблизну медіану з більшою ефективністю, ніж постійно сортувати вхідні значення в структуру даних.


1

Для тих, кому потрібна медіана бігу на Яві ... PriorityQueue - твій друг. O (log N) вставити, O (1) поточну медіану та O (N) видалити. Якщо ви знаєте розподіл своїх даних, ви можете зробити набагато краще, ніж це.

public class RunningMedian {
  // Two priority queues, one of reversed order.
  PriorityQueue<Integer> lower = new PriorityQueue<Integer>(10,
          new Comparator<Integer>() {
              public int compare(Integer arg0, Integer arg1) {
                  return (arg0 < arg1) ? 1 : arg0 == arg1 ? 0 : -1;
              }
          }), higher = new PriorityQueue<Integer>();

  public void insert(Integer n) {
      if (lower.isEmpty() && higher.isEmpty())
          lower.add(n);
      else {
          if (n <= lower.peek())
              lower.add(n);
          else
              higher.add(n);
          rebalance();
      }
  }

  void rebalance() {
      if (lower.size() < higher.size() - 1)
          lower.add(higher.remove());
      else if (higher.size() < lower.size() - 1)
          higher.add(lower.remove());
  }

  public Integer getMedian() {
      if (lower.isEmpty() && higher.isEmpty())
          return null;
      else if (lower.size() == higher.size())
          return (lower.peek() + higher.peek()) / 2;
      else
          return (lower.size() < higher.size()) ? higher.peek() : lower
                  .peek();
  }

  public void remove(Integer n) {
      if (lower.remove(n) || higher.remove(n))
          rebalance();
  }
}

c ++ має замовлення статистичних дерев від gnu в розширенні до стандартної бібліотеки. Дивіться мою публікацію нижче.
Лев Гудштадт

Я думаю, що ваш код тут не вказано правильно. Є деякі неповні частини, такі як: }), higher = new PriorityQueue<Integer>();або new PriorityQueue<Integer>(10,. Я не міг запустити код.
Hengameh

@Hengameh Java закінчує операції крапками з комою - розриви рядків взагалі не мають значення. Ви, мабуть, скопіювали його неправильно.
Матвій

Вам слід задати нове запитання, а потім надіслати свою відповідь Java на це питання.
jww

0

Ось такий, який може бути використаний, коли точний вихід не важливий (для цілей відображення тощо). Вам потрібні сумарні рахунки та lastmedian, плюс нове значення.

{
totalcount++;
newmedian=lastmedian+(newvalue>lastmedian?1:-1)*(lastmedian==0?newvalue: lastmedian/totalcount*2);
}

Дає досить точні результати для таких речей, як page_display_time.

Правила: потік введення повинен бути рівним за порядком часу відображення сторінки, великим рахунком (> 30 тощо) і мати нульову медіану.

Приклад: час завантаження сторінки, 800 предметів, 10 мс ... 3000 мс, середній 90 мс, реальна медіана: 11 мс

Після 30 входів помилка медіанів, як правило, <= 20% (9 мс .. 12 мс), і стає все менше і менше. Після 800 входів похибка становить + -2%.

Ще один мислитель із подібним рішенням знаходиться тут: Median Filter Супер ефективна реалізація


-1

Ось реалізація java

package MedianOfIntegerStream;

import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;


public class MedianOfIntegerStream {

    public Set<Integer> rightMinSet;
    public Set<Integer> leftMaxSet;
    public int numOfElements;

    public MedianOfIntegerStream() {
        rightMinSet = new TreeSet<Integer>();
        leftMaxSet = new TreeSet<Integer>(new DescendingComparator());
        numOfElements = 0;
    }

    public void addNumberToStream(Integer num) {
        leftMaxSet.add(num);

        Iterator<Integer> iterMax = leftMaxSet.iterator();
        Iterator<Integer> iterMin = rightMinSet.iterator();
        int maxEl = iterMax.next();
        int minEl = 0;
        if (iterMin.hasNext()) {
            minEl = iterMin.next();
        }

        if (numOfElements % 2 == 0) {
            if (numOfElements == 0) {
                numOfElements++;
                return;
            } else if (maxEl > minEl) {
                iterMax.remove();

                if (minEl != 0) {
                    iterMin.remove();
                }
                leftMaxSet.add(minEl);
                rightMinSet.add(maxEl);
            }
        } else {

            if (maxEl != 0) {
                iterMax.remove();
            }

            rightMinSet.add(maxEl);
        }
        numOfElements++;
    }

    public Double getMedian() {
        if (numOfElements % 2 != 0)
            return new Double(leftMaxSet.iterator().next());
        else
            return (leftMaxSet.iterator().next() + rightMinSet.iterator().next()) / 2.0;
    }

    private class DescendingComparator implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }

    public static void main(String[] args) {
        MedianOfIntegerStream streamMedian = new MedianOfIntegerStream();

        streamMedian.addNumberToStream(1);
        System.out.println(streamMedian.getMedian()); // should be 1

        streamMedian.addNumberToStream(5);
        streamMedian.addNumberToStream(10);
        streamMedian.addNumberToStream(12);
        streamMedian.addNumberToStream(2);
        System.out.println(streamMedian.getMedian()); // should be 5

        streamMedian.addNumberToStream(3);
        streamMedian.addNumberToStream(8);
        streamMedian.addNumberToStream(9);
        System.out.println(streamMedian.getMedian()); // should be 6.5
    }
}

Вам слід задати нове запитання, а потім надіслати свою відповідь Java на це питання.
jww

-4

Якщо ви просто вимагаєте згладженого середнього, швидкий / простий спосіб - це помножити останнє значення на x, а середнє значення на (1-x), а потім додати їх. Потім це стає новим середнім.

редагувати: Не те, що запитував користувач, і не настільки статистично достовірне, але досить добре для багатьох застосувань
Я залишу його тут (незважаючи на голоси) для пошуку!


2
Це обчислює середнє значення. Він хоче медіану. Крім того, він обчислює медіану розсувного вікна значень, а не всього набору.
А. Леві

1
Це обчислює середнє значення вікна значень з постійною занепадом залежно від X - це дуже корисно, коли продуктивність має значення, і вам не можна заважати робити фільтр kalman. Я поставив його, щоб пошук міг його знайти.
Мартін Бекетт

Це те, що я також відразу подумав, запровадивши такий фільтр як дуже базовий і дешевий фільтр низьких частот для аудіо-програми.
Джеймс Морріс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.