Trouver la médiane courante à partir d'un flux d'entiers [dupliquer]

possibilité de dupliquer:

algorithme de la médiane mobile en C

étant donné que les entiers sont lus à partir d'un flux de données. Trouver la médiane des éléments lus jusqu'à présent de manière efficace.

Solution j'ai lu: Nous pouvons utiliser un tas max sur le côté gauche pour représenter les éléments qui sont moins que la médiane effective, et un tas min sur le côté droit de représenter les éléments qui sont supérieures à la médiane.

après traitement d'un élément entrant, le nombre d'éléments dans les tas diffère tout au plus de 1 élément. Lorsque les deux tas contiennent le même nombre d'éléments, nous trouvons la moyenne des données racine du tas comme médiane efficace. Lorsque le tas ne sont pas équilibrés, nous sélectionnons les efficace médiane à partir de la racine du tas contenant plus d'éléments.

mais comment construire un tas max et min heap c'est-à-dire comment connaîtrions-nous la médiane effective ici? Je pense que nous insérerions un élément dans max-heap, puis un autre dans min-heap, et ainsi de suite pour tous les éléments. Corrigez-moi Si je me trompe ici.

201
demandé sur Community 2012-05-18 21:56:11

8 réponses

il existe un certain nombre de solutions différentes pour trouver la médiane courante à partir de données en continu, je vais en parler brièvement à la toute fin de la réponse.

la question porte sur les détails de la solution spécifique (max heap / min heap solution), et comment fonctionne la solution basée sur le tas est expliqué ci-dessous:

pour les deux premiers éléments Ajouter un plus petit au maxHeap sur la gauche, et un plus grand au minHeap sur la droite. Ensuite, le processus de les flux de données un par un,

Step 1: Add next item to one of the heaps

   if next item is smaller than maxHeap root add it to maxHeap,
   else add it to minHeap

Step 2: Balance the heaps (after this step heaps will be either balanced or
   one of them will contain 1 more item)

   if number of elements in one of the heaps is greater than the other by
   more than 1, remove the root element from the one containing more elements and
   add to the other one

puis à tout moment vous pouvez calculer la médiane comme ceci:

   If the heaps contain equal amount of elements;
     median = (root of maxHeap + root of minHeap)/2
   Else
     median = root of the heap with more elements

maintenant je vais parler du problème en général comme promis au début de la réponse. Trouver la médiane courante à partir d'un flux de données est un problème difficile, et trouver une solution exacte avec des contraintes de mémoire efficaces est probablement impossible pour le cas général. D'autre part, si les données ont certaines caractéristiques que nous pouvons exploiter, nous pouvons développer efficace des solutions spécialisées. Par exemple, si nous savons que les données sont un type intégral , alors nous pouvons utiliser counting sort , qui peut vous donner un algorithme de mémoire constante temps. La solution basée sur le tas est une solution plus générale car elle peut être utilisée pour d'autres types de données (doubles). Et enfin, si la médiane exacte n'est pas nécessaire et une approximation est suffisante, vous pouvez simplement essayer d'estimer une probabilité la fonction de densité pour les données et estimer la médiane en utilisant cela.

349
répondu Hakan Serce 2017-01-28 11:01:44

si vous ne pouvez pas garder tous les objets en mémoire à la fois, ce problème devient beaucoup plus difficile. La solution heap vous demande de garder tous les éléments en mémoire en même temps. Ce n'est pas possible dans la plupart des applications du monde réel de ce problème.

au lieu de cela, comme vous voyez les nombres, gardez la trace du Compter du nombre de fois que vous voyez chaque entier. En supposant 4 nombres entiers de octets, c'est-à-dire 2^32 seaux, ou au plus 2^33 entiers (touche et compte pour chaque int), qui est de 2^35 octets ou 32 Go. Il sera probablement beaucoup moins que cela parce que vous n'avez pas besoin de stocker la clé ou de compter pour les entrées qui sont 0 (C.-à-d. comme un defaultdict en python). Cela prend un temps constant pour insérer chaque nouveau entier.

puis à n'importe quel point, pour trouver la médiane, il suffit d'utiliser les comptes pour déterminer quel entier est l'élément central. Cela prend du temps constant (bien qu'il s'agisse d'une constante importante, mais constante).

43
répondu Andrew C 2012-05-21 21:19:09

si la variance de l'entrée est statistiquement distribuée (par exemple normale , log-normale)... etc) puis l'échantillonnage du réservoir est un moyen raisonnable d'estimer les percentiles / médians à partir d'un cours d'eau arbitrairement long de nombres.

int n = 0;  // Running count of elements observed so far  
#define SIZE 10000
int reservoir[SIZE];  

while(streamHasData())
{
  int x = readNumberFromStream();

  if (n < SIZE)
  {
       reservoir[n++] = x;
  }         
  else 
  {
      int p = random(++n); // Choose a random number 0 >= p < n
      if (p < SIZE)
      {
           reservoir[p] = x;
      }
  }
}

"réservoir" est alors une course, uniforme (juste), un échantillon de toutes les entrées - indépendamment de la taille. Trouver la médiane (ou n'importe quel centile) est alors une question simple de trier le réservoir et de sonder le point intéressant.

puisque le réservoir est de taille fixe, le sort peut être considéré comme effectivement O(1) - et cette méthode fonctionne avec le temps constant et la consommation de mémoire.

39
répondu Colm MacCárthaigh 2012-05-21 23:13:42

la manière la plus efficace de calculer un centile d'un flux que j'ai trouvé est l'algorithme P2: Raj Jain, Imrich Chlamtac: L'algorithme P2 pour le calcul dynamique des Quantiiles et des histogrammes sans stocker les Observations. Commun. ACM 28(10): 1076-1085 (1985)

L'algorithme est simple à mettre en œuvre et fonctionne très bien. C'est une estimation, cependant, donc gardez cela à l'esprit. Extrait du résumé:

un algorithme heuristique est proposé pour le calcul dynamique qf la médiane et d'autres quantiles. Les estimations sont produites dynamiquement que les observations sont générés. Les observations ne sont pas stockées; par conséquent, l'algorithme a une très petite et fixe exigence de stockage indépendamment du nombre d'observations. Cela le rend idéal pour la mise en œuvre dans une puce quantile qui peut être utilisé dans les contrôleurs industriels et enregistreurs. L'algorithme est ensuite étendu à histogramme de traçage. La précision de l'algorithme est analysé.

24
répondu Hellblazer 2012-05-21 23:14:09

ce problème a une solution exacte qui n'a besoin que des éléments n les plus récents pour être gardés en mémoire. Il est rapide et échelles.

An indexable skiplist supports o(LN n) insertion, suppression, et recherche indexée d'éléments arbitraires tout en maintenant l'ordre trié. Couplée à une file D'attente FIFO qui suit la n-ème entrée la plus ancienne, la solution est simple:

class RunningMedian:
    'Fast running median with O(lg n) updates where n is the window size'

    def __init__(self, n, iterable):
        self.it = iter(iterable)
        self.queue = deque(islice(self.it, n))
        self.skiplist = IndexableSkiplist(n)
        for elem in self.queue:
            self.skiplist.insert(elem)

    def __iter__(self):
        queue = self.queue
        skiplist = self.skiplist
        midpoint = len(queue) // 2
        yield skiplist[midpoint]
        for newelem in self.it:
            oldelem = queue.popleft()
            skiplist.remove(oldelem)
            queue.append(newelem)
            skiplist.insert(newelem)
            yield skiplist[midpoint]

Voici des liens vers le code de travail complet (une version de classe facile à comprendre et une version de générateur optimisée avec le code indexable skiplist inlined):

20
répondu Raymond Hettinger 2012-05-22 06:14:18

une façon intuitive de penser à cela est que si vous aviez un arbre binaire complètement équilibré, alors la racine serait l'élément médian, car il y aurait le même nombre d'éléments plus petits et plus grands. Maintenant, si l'arbre n'est pas plein ce ne sera pas tout à fait le cas puisqu'il y aura des éléments manquants du dernier niveau.

donc ce que nous pouvons faire à la place est d'avoir la médiane, et deux arbres binaires équilibrés, un pour les éléments inférieurs à la médiane, et un pour les éléments supérieure à la médiane. Les deux arbres doivent être maintenus à la même taille.

lorsque nous obtenons un nouvel entier du flux de données, nous le comparons à la médiane. Si elle est supérieure à la médiane, on l'ajoute à l'arbre de droite. Si les deux tailles d'arbre diffèrent plus de 1, nous enlevons l'élément min de l'arbre droit, en faisons la nouvelle médiane, et mettons l'ancienne médiane dans l'arbre gauche. De même, pour les plus petits.

14
répondu Irene Papakonstantinou 2012-05-22 18:59:01

Efficace " est un mot qui dépend du contexte. La solution à ce problème dépend de la quantité de requêtes effectuées par rapport à la quantité d'insertions. Supposons que vous insérez N nombres et K fois vers la fin vous étiez intéressé par la médiane. La complexité de l'algorithme basé sur les tas serait O(N log N + K).

envisager la variante suivante. Plunk les nombres dans un tableau, et pour chaque requête, exécuter l'algorithme de sélection linéaire (en utilisant le quicksort pivot, par exemple). Maintenant vous avez un algorithme avec le temps d'exécution O(kn).

maintenant si K est suffisamment petit (requêtes peu fréquentes), ce dernier algorithme est en fait plus efficace et vice versa.

6
répondu Peteris 2012-05-22 10:22:28

vous ne pouvez pas faire ça avec un seul tas? mise à Jour: pas de. Voir le commentaire.

Invariant: après avoir lu 2*n entrées, Le min-tas tient le n le plus grand d'entre eux.

Boucle

: Lire 2 entrées. Ajoutez les deux au tas, et retirez le tas min. Cela rétablit l'invariant.

ainsi quand 2n entrées ont été lues, Le min de tas est le nième plus grand. Il faut être une petite complication supplémentaire pour faire la moyenne des deux éléments autour de la position médiane et pour traiter les requêtes après un nombre impair d'entrées.

-1
répondu Darius Bacon 2012-05-21 21:51:50