Поиск k-ой порядковой статистики — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м
Строка 15: Строка 15:
 
=== Код алгоритма ===
 
=== Код алгоритма ===
  
Ниже представлен код представленного алгоритма. При реализации, однако, вместо рекурсивных вызовов изменяются границы поиска статистики во внешнем цикле. В коде счититаем, что процедура '''partition''' принимает массив и границы отрезка, который будет рассечён (причём правая граница отрезка не включается) и возвращает индекс опорного элемента. Также, считается, что массив индексируется с нуля.
+
Ниже представлен код представленного алгоритма. При реализации, однако, вместо рекурсивных вызовов изменяются границы поиска статистики во внешнем цикле. В коде считаем, что процедура '''partition''' принимает массив и границы отрезка, который будет рассечён (причём правая граница отрезка не включается) и возвращает индекс опорного элемента. Также, считается, что массив индексируется с нуля.
  
 
  '''int''' findOrderStatistic('''int[]''' array, '''int''' k) {
 
  '''int''' findOrderStatistic('''int[]''' array, '''int''' k) {
Строка 37: Строка 37:
 
=== Анализ времени работы ===
 
=== Анализ времени работы ===
  
Аналогично QuickSort, может возникнуть такой же худщий случай (процедура '''partition''' возвращает каждый раз левую или правую границу рассматриваемой части), при котором время работы составит <tex>\Omega(n^2)</tex>. Однако, если считать, что '''partition''' возвращает все элементы рассматриваемого отрезка с равной вероятностью, то можно оценить матожидание времени работы как <tex>O(n)</tex>.
+
Аналогично QuickSort, может возникнуть такой же худший случай (процедура '''partition''' возвращает каждый раз левую или правую границу рассматриваемой части), при котором время работы составит <tex>\Omega(n^2)</tex>. Однако, если считать, что '''partition''' возвращает все элементы рассматриваемого отрезка с равной вероятностью, то можно оценить матожидание времени работы как <tex>O(n)</tex>.
  
 
Будем оценивать количество сравнений. При поиске статистики в массиве размера <tex>n</tex> функция '''partition''' (точнее, одна из распространённых вариаций) совершает не более <tex>n - 1</tex> сравнений. Далее, в зависимости от <tex>k</tex> выбирается левая или правая половины (или вообще алгоритм завершает работу). Оценку проводим сверху, то есть, будем считать, что каждый раз выбирается большая половина.
 
Будем оценивать количество сравнений. При поиске статистики в массиве размера <tex>n</tex> функция '''partition''' (точнее, одна из распространённых вариаций) совершает не более <tex>n - 1</tex> сравнений. Далее, в зависимости от <tex>k</tex> выбирается левая или правая половины (или вообще алгоритм завершает работу). Оценку проводим сверху, то есть, будем считать, что каждый раз выбирается большая половина.

Версия 22:48, 12 мая 2014

Определение:
[math]k[/math]-ой порядковой статистикой набора элементов линейно упорядоченного множества называется такой его элемент, который является [math]k[/math]-ым элементом набора в порядке сортировки


Модификация QuickSort

Описание алгоритма

Будем использовать процедуру рассечения массива элементов из алгоритма сортировки QuickSort. Пусть нам надо найти [math]k[/math]-ую порядковую статистику, а после рассечения опорный элемент встал на позицию [math]m[/math]. Возможно три случая:

  • k = m. Порядковая статистика найдена.
  • k < m. Рекурсивно ищем [math]k[/math]-ую статистику в первой половине массива.
  • k > m. Рекурсивно ищем [math](k - m - 1)[/math]-ую статистику во второй половине массива.

Код алгоритма

Ниже представлен код представленного алгоритма. При реализации, однако, вместо рекурсивных вызовов изменяются границы поиска статистики во внешнем цикле. В коде считаем, что процедура partition принимает массив и границы отрезка, который будет рассечён (причём правая граница отрезка не включается) и возвращает индекс опорного элемента. Также, считается, что массив индексируется с нуля.

int findOrderStatistic(int[] array, int k) {
  int left = 0, right = array.length;
  while (true) {
    int mid = partition(array, left, right);

    if (mid == k) {
      return array[mid];
    }
    else if (k < mid) {
      right = mid;
    }
    else {
      k -= mid + 1;
      left = mid + 1;
    }
  }
}

Анализ времени работы

Аналогично QuickSort, может возникнуть такой же худший случай (процедура partition возвращает каждый раз левую или правую границу рассматриваемой части), при котором время работы составит [math]\Omega(n^2)[/math]. Однако, если считать, что partition возвращает все элементы рассматриваемого отрезка с равной вероятностью, то можно оценить матожидание времени работы как [math]O(n)[/math].

Будем оценивать количество сравнений. При поиске статистики в массиве размера [math]n[/math] функция partition (точнее, одна из распространённых вариаций) совершает не более [math]n - 1[/math] сравнений. Далее, в зависимости от [math]k[/math] выбирается левая или правая половины (или вообще алгоритм завершает работу). Оценку проводим сверху, то есть, будем считать, что каждый раз выбирается большая половина.

[math]T(n) \le \frac 1n \sum\limits_{k = 1}^n \left ( T \left ( \max \left \{k - 1; n - k \right \} \right ) + n - 1 \right ) =[/math]
[math]= n - 1 + \frac 1n \sum\limits_{k = 1}^n T(\max \{k - 1; n - k\}) = n - 1 + \frac 2n \sum\limits_{k = \lfloor n/2 \rfloor}^{n - 1} T(k)[/math]

Предположим, что [math]T(k) \le ck[/math] для некоторой константы [math]c[/math] и всех [math]k \lt n[/math] (будем доказывать оценку по индукции). Тогда верно неравенство:

[math]T(n) = n - 1 + \frac 2n \sum\limits_{k = \lfloor n/2 \rfloor}^{n - 1} ck[/math]

Преобразуем сумму из правой части равенства по формуле суммы арифметической прогрессии и оценим преобразованное выражение:

[math]\sum\limits_{k = \lfloor n/2 \rfloor}^{n - 1} ck = \frac 12 \left (\left \lceil \frac n2 \right \rceil - 1 \right) \left( c \left \lfloor \frac n2 \right \rfloor + c(n - 1) \right ) \le \frac c2 \left (\frac{n + 1}2 - 1\right) \frac{3n - 2}2 = c \frac{n - 1}4 \frac{3n - 2}2[/math]

Воспользуемся полученной оценкой для оценки исходного выражения. Также, предположим, что [math]c \ge 4[/math]:

[math]T(n) \le n - 1 + \frac{2c}n \frac{n - 1}4 \frac{3n - 2}2 = n - 1 + c\frac{n - 1}{2n} \frac{3n - 2}2 \le \frac c4 (n - 1) + \frac c4\left (\frac{n - 1}n (3n - 2)\right) \le[/math]
[math]\le \frac c4 (n - 1 + 3n - 2) = \frac c4 (4n - 3) \le cn[/math]

Для довершения доказательства необходима проверка базы индукции, но она тривиальна: для выборки порядковой статистики из одного элемента сравнений не требуется: [math]T(1) = 0 \lt 4[/math]. Итого, мы доказали, что [math]T(n) \le 4n[/math], следовательно, [math]T(n) = O(n)[/math]

Ссылки

  • Selection algorithm — Wikipedia
  • Donald Knuth. The Art of Computer Programming, Volume 3: Sorting and Searching, Third Edition. Addison-Wesley, 1997. ISBN 0-201-89685-0. Section 5.3.3: Minimum-Comparison Selection, pp.207–219.