2012-05-19 6 views
33

Per trovare la mediana di un array non ordinato, possiamo fare un min-heap in O (nlogn) tempo di n elementi, e quindi siamo in grado di estrarre uno per uno n/2 elementi per ottenere la mediana Ma questo approccio richiederebbe tempo (nlogn).Trovare la mediana di un array non ordinato

Possiamo fare lo stesso con qualche metodo nel tempo O (n)? Se possiamo, per favore dì o suggerisci qualche metodo.

+0

possibile duplicato di [Come trovare il kesimo elemento più grande in una matrice non ordinata di lunghezza n in O (n)?] (Http: // stackoverflow .com/questions/251781/how-to-find-the-kth-largest-element-in-an-unsorted-array-of-length-n-in-on) –

+7

Ricorda che se ci vuole O (nlogn) quindi potresti anche solo ordinare l'array e dividere l'indice per 2. – Zombies

+2

building heap richiede O (n) tempo non O (nlogn) – JerryGoyal

risposta

31

È possibile utilizzare il Median of Medians algoritmo per trovare mediana di un array non ordinato in tempo lineare.

+0

È approssimativo ma dovrebbe funzionare abbastanza bene. –

+7

@KevinKostlan In realtà non è approssimativo, è la vera mediana e lo trova in tempo lineare.Si noti che dopo aver trovato la mediana delle mediane (che è garantita per essere maggiore di almeno il 30% degli elementi e minore di almeno il 30% degli elementi) si partiziona l'array usando quel pivot. Quindi si ricorre (se necessario) in uno di quegli array che è al massimo% 70 la dimensione dell'array originale per trovare la mediana reale (o nel caso generale la statistica k). – dcmm88

10

Quickselect funziona in O (n), questo viene anche utilizzato nella fase di partizione di Quicksort.

+4

Non penso che quickselect necessariamente darebbe la mediana in UN SOLO run. Dipende dalla tua scelta pivot. – Yashasvi

+0

Sfortunatamente, quickselect per trovare la mediana prenderà O (n^2) nel peggiore dei casi. Ciò si verifica quando riduciamo l'array di appena 1 elemento in ogni iterazione di QuickSelect. Considera una matrice già ordinata e scegliamo sempre la maggior parte degli elementi come pivot. So che è un po 'sciocco farlo, ma è così che sono i casi peggiori. –

0

Esso può essere fatto utilizzando QuickSelect algoritmo in O (n), fanno riferimento a KTH statistiche d'ordine (algoritmi randomizzati).

9

L'algoritmo di selezione rapida può trovare il k-esimo elemento più piccolo di un array in tempo di esecuzione lineare (O(n)). Ecco un'implementazione in python:

import random 

def partition(L, v): 
    smaller = [] 
    bigger = [] 
    for val in L: 
     if val < v: smaller += [val] 
     if val > v: bigger += [val] 
    return (smaller, [v], bigger) 

def top_k(L, k): 
    v = L[random.randrange(len(L))] 
    (left, middle, right) = partition(L, v) 
    # middle used below (in place of [v]) for clarity 
    if len(left) == k: return left 
    if len(left)+1 == k: return left + middle 
    if len(left) > k: return top_k(left, k) 
    return left + middle + top_k(right, k - len(left) - len(middle)) 

def median(L): 
    n = len(L) 
    l = top_k(L, n/2 + 1) 
    return max(l) 
0

come dice Wikipedia, mediana-di-mediane è teoricamente O (n), ma non è utilizzato nella pratica, perché il sovraccarico di trovare perni "buoni" lo rende troppo lento .
http://en.wikipedia.org/wiki/Selection_algorithm

Ecco sorgente di Java per un algoritmo QuickSelect per trovare l'elemento k'th in un array:

/** 
* Returns position of k'th largest element of sub-list. 
* 
* @param list list to search, whose sub-list may be shuffled before 
*   returning 
* @param lo first element of sub-list in list 
* @param hi just after last element of sub-list in list 
* @param k 
* @return position of k'th largest element of (possibly shuffled) sub-list. 
*/ 
static int select(double[] list, int lo, int hi, int k) { 
    int n = hi - lo; 
    if (n < 2) 
     return lo; 

    double pivot = list[lo + (k * 7919) % n]; // Pick a random pivot 

    // Triage list to [<pivot][=pivot][>pivot] 
    int nLess = 0, nSame = 0, nMore = 0; 
    int lo3 = lo; 
    int hi3 = hi; 
    while (lo3 < hi3) { 
     double e = list[lo3]; 
     int cmp = compare(e, pivot); 
     if (cmp < 0) { 
      nLess++; 
      lo3++; 
     } else if (cmp > 0) { 
      swap(list, lo3, --hi3); 
      if (nSame > 0) 
       swap(list, hi3, hi3 + nSame); 
      nMore++; 
     } else { 
      nSame++; 
      swap(list, lo3, --hi3); 
     } 
    } 
    assert (nSame > 0); 
    assert (nLess + nSame + nMore == n); 
    assert (list[lo + nLess] == pivot); 
    assert (list[hi - nMore - 1] == pivot); 
    if (k >= n - nMore) 
     return select(list, hi - nMore, hi, k - nLess - nSame); 
    else if (k < nLess) 
     return select(list, lo, lo + nLess, k); 
    return lo + k; 
} 

Non ho incluso la fonte dei metodi confrontare e scambiare, quindi è facile cambia il codice per lavorare con Object [] invece di double [].

In pratica, è possibile che il codice precedente sia o (N).

+1

swap ??????????????? – Bohdan

13

Ho già upvoted la risposta @dasblinkenlight poiché l'algoritmo bfprt infatti risolve questo problema in O (n). Voglio solo aggiungere che questo problema potrebbe essere risolto in tempo O (n) usando anche gli heap. La compilazione di un heap può essere eseguita in tempo O (n) utilizzando il valore bottom-up. Dai uno sguardo al seguente articolo per una spiegazione dettagliata

Supponendo che l'array abbia N elementi, devi creare due heap: Un MaxHeap che contiene i primi N/2 elementi (o (N/2) +1 se N è dispari) e un MinHeap che contiene gli elementi rimanenti. Se N è dispari, la tua mediana è l'elemento massimo di MaxHeap (O (1) ottenendo il massimo). Se N è pari, allora la tua mediana è (MaxHeap.max() + MinHeap.min())/2 anche questo richiede O (1). Pertanto, il costo reale dell'intera operazione è l'operazione di creazione dell'heap che è O (n).

BTW questo algoritmo MaxHeap/MinHeap funziona anche quando non si conosce il numero degli elementi dell'array in precedenza (se si deve risolvere lo stesso problema per un flusso di numeri interi per e.g). È possibile visualizzare ulteriori dettagli su come risolvere questo problema nel seguente articolo Median Of integer streams

+3

Perché funziona? Supponiamo che il tuo array sia [3, 2, 1]. Quindi metteremo i primi 2 in un heap massimo: [3, 2], quindi 3 sarebbe la radice, quindi 2, il suo figlio deve essere più piccolo di quello. E avremmo [1] nel mucchio minimo. In base a questo algoritmo, sceglieremo il massimo (root), di maxHeap come mediana. Questo non ci darebbe 3? – Arkidillo

+0

È O (n^2) tempo peggiore, non O (n). Quando ci si riferisce alla complessità del Big O di un algoritmo, senza specificare il caso, si presume che si stia riferendo al momento peggiore. – Rick