The Median Maintenance Problem: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(18 intermediate revisions by the same user not shown)
Line 2: Line 2:
* [[Heap#Canonical_Uses_of_a_Heap|Heaps]]
* [[Heap#Canonical_Uses_of_a_Heap|Heaps]]
=Problem=
=Problem=
Give a sequence of numbers x<sub>1</sub>, x<sub>2</sub>, .... x<sub>n</sub>, report at each step i the median number among the numbers seen so far.
Give a sequence of numbers x<sub>1</sub>, x<sub>2</sub>, .... x<sub>n</sub>, report at each step i the median number among the numbers seen so far. For a given k, the and assuming that x<sub>1</sub>, x<sub>2</sub>, .... x<sub>k</sub> are sorted in an ascending order, the median will be on (k + 1)/2 position if k is odd and on k/2 position is k is even.


=Discussion=
=Discussion=
Line 8: Line 8:


=Solution=
=Solution=
Use two heaps: a [[Heap#Min_Heap_and_Max_Heap|max heap]] H<sub>low</sub> that supports the <tt>[[Heap#REMOVE-MAX|REMOVE-MAX]]</tt> operation and a [[Heap#Min_Heap_and_Max_Heap|min heap]] H<sub>high</sub> that supports the <tt>[[Heap#REMOVE-MIN|REMOVE-MIN]]</tt> operation.  
Use two heaps: a [[Heap#Min_Heap_and_Max_Heap|max heap]] H<sub>low</sub> that supports the <tt>[[Heap#REMOVE-MAX|REMOVE-MAX]]</tt> operation with O(1) running time and a [[Heap#Min_Heap_and_Max_Heap|min heap]] H<sub>high</sub> that supports the <tt>[[Heap#REMOVE-MIN|REMOVE-MIN]]</tt> operation, also with O(1) running time.  


Maintain the first smallest half elements in H<sub>low</sub> and the largest half of the elements in H<sub>high</sub>. When a new element comes, if its smaller than the current median insert it in H<sub>low</sub>, if it is higher than the current median, insert it in  H<sub>high</sub>. Keep track of heap sizes and rebalance when necessary, so the total number of elements is evenly split among the heaps.
The idea is to maintain the first smallest half elements in H<sub>low</sub> and the largest half of the elements in H<sub>high</sub>.


This is implemented by appropriately inserting each new element in the corresponding heap, and then rebalancing the heaps so H<sub>low</sub> and H<sub>high</sub> have the same number of elements, or H<sub>low</sub> has with at most 1 element more than H<sub>high</sub>. Rebalancing means extracting the top of the heap from the oversized heap and inserting the element in the other heap.
Maintaining this load for both heaps makes possible to read the current median, at any moment, as the top of H<sub>low</sub>.
<font size=-1>
while each x:
  if H<sub>low</sub> is empty || x ≤ H<sub>low</sub>.MAXIMUM()
    H<sub>low</sub>.INSERT(x)
  else
    H<sub>high</sub>.INSERT(x)
  REBALANCE(H<sub>low</sub>, H<sub>high</sub>)
  record running median as H<sub>low</sub>.MAXIMUM()
</font>
<font size=-1>
REBALANCE(H<sub>low</sub>, H<sub>high</sub>) {
  <font color=teal># Because we're rebalancing at each step and we are maintaining the halves either equal,
  # or H<sub>low</sub> larger than H<sub>high</sub> with one element the difference in size can only be 2, 1, 0 or -1</font>
  difference = H<sub>low</sub>.SIZE() - H<sub>high</sub>.SIZE()
  if difference == 2
    <font color=teal># Shift H<sub>low</sub> maximum element to H<sub>high</sub></font>
    H<sub>high</sub>.INSERT(H<sub>low</sub>.REMOVE-MAX())
  else if difference == 1 || difference == 0
    <font color=teal># Balanced, nothing to do</font>
  else if difference == -1
    <font color=teal># Shift H<sub>high</sub> minimum element to H<sub>low</sub></font>
    H<sub>low</sub>.INSERT(H<sub>high</sub>.REMOVE-MIN())
  else
    <font color=teal># We're not supposed to be in this situation</font>
    ERROR()
}
</font>
More details: {{External|https://www.coursera.org/learn/algorithms-graphs-data-structures/lecture/iIzo8/heaps-operations-and-applications}}
More details: {{External|https://www.coursera.org/learn/algorithms-graphs-data-structures/lecture/iIzo8/heaps-operations-and-applications}}
=Playground Implementation=
=Playground Implementation=
{{External|https://github.com/ovidiuf/playground/blob/master/learning/stanford-algorithms-specialization/06-median-maintenance/src/main/java/playground/stanford/mm/MedianMaintenance.java}}

Latest revision as of 01:30, 14 October 2021

Internal

Problem

Give a sequence of numbers x1, x2, .... xn, report at each step i the median number among the numbers seen so far. For a given k, the and assuming that x1, x2, .... xk are sorted in an ascending order, the median will be on (k + 1)/2 position if k is odd and on k/2 position is k is even.

Discussion

The problem can be resolved by repeatedly solving the selection problem on the set of numbers seen so far, but the selection problem has a running time of O(n), so repeating the selection algorithm for each number will have a running time of O(n2). The median maintenance problem is a canonical use case for a heap.

Solution

Use two heaps: a max heap Hlow that supports the REMOVE-MAX operation with O(1) running time and a min heap Hhigh that supports the REMOVE-MIN operation, also with O(1) running time.

The idea is to maintain the first smallest half elements in Hlow and the largest half of the elements in Hhigh.

This is implemented by appropriately inserting each new element in the corresponding heap, and then rebalancing the heaps so Hlow and Hhigh have the same number of elements, or Hlow has with at most 1 element more than Hhigh. Rebalancing means extracting the top of the heap from the oversized heap and inserting the element in the other heap.

Maintaining this load for both heaps makes possible to read the current median, at any moment, as the top of Hlow.

while each x:
  if Hlow is empty || x ≤ Hlow.MAXIMUM()
    Hlow.INSERT(x)
  else
    Hhigh.INSERT(x)
  REBALANCE(Hlow, Hhigh)
  record running median as Hlow.MAXIMUM()

REBALANCE(Hlow, Hhigh) {
  # Because we're rebalancing at each step and we are maintaining the halves either equal, 
  # or Hlow larger than Hhigh with one element the difference in size can only be 2, 1, 0 or -1
  difference = Hlow.SIZE() - Hhigh.SIZE()
  if difference == 2 
    # Shift Hlow maximum element to Hhigh
    Hhigh.INSERT(Hlow.REMOVE-MAX())
  else if difference == 1 || difference == 0
    # Balanced, nothing to do
  else if difference == -1
    # Shift Hhigh minimum element to Hlow
    Hlow.INSERT(Hhigh.REMOVE-MIN())
  else
    # We're not supposed to be in this situation
    ERROR()
}

More details:

https://www.coursera.org/learn/algorithms-graphs-data-structures/lecture/iIzo8/heaps-operations-and-applications

Playground Implementation

https://github.com/ovidiuf/playground/blob/master/learning/stanford-algorithms-specialization/06-median-maintenance/src/main/java/playground/stanford/mm/MedianMaintenance.java