Ответ 1
Чтобы ответить на этот вопрос, давайте сначала определим, как мы можем оценить время выполнения сортировки вставки. Если мы сможем найти хорошее математическое выражение для среды выполнения, мы можем затем манипулировать этим выражением, чтобы определить среднюю продолжительность выполнения.
Ключевое наблюдение, которое нам нужно иметь, заключается в том, что время выполнения сортировки вставки тесно связано с числом инверсий во входном массиве. Инверсия в массиве представляет собой пару элементов A [i] и A [j], которые находятся в неправильном относительном порядке, то есть я < j, но A [j] А [I]. Например, в этом массиве:
0 1 3 2 4 5
Существует одна инверсия: переключатели 3 и 2 должны переключаться. В этом массиве:
4 1 0 3 2
Есть 6 инверсий:
- 4 и 1
- 4 и 0
- 4 и 3
- 4 и 2
- 1 и 0
- 3 и 2
Одним из важных свойств инверсий является то, что отсортированный массив не имеет никаких инверсий в нем, поскольку каждый элемент должен быть меньше, чем все, что происходит после него, и больше, чем все, что происходит перед ним.
Причиной этого является то, что существует прямая связь между объемом работы, выполняемой в сортировке вставки, и количеством инверсий в исходном массиве. Чтобы увидеть это, рассмотрите несколько быстрых псевдокодов для сортировки вставки:
- Для я = 2.. n: (Предполагая 1-индексацию)
- Установите j = я - 1.
- Пока A [j] > A [j + 1]:
- Переведите A [j] и A [j + 1].
- Установите j = j - 1.
Обычно, определяя общий объем работы, выполняемой такой функцией, мы можем определить максимальный объем работы, выполняемой внутренним циклом, а затем умножить его на количество итераций внешнего цикла. Это даст верхнюю границу, но не обязательно плотную. Лучший способ объяснить всю проделанную работу - признать, что существуют два разных источника работы:
- Внешний цикл, который насчитывает 2, 3,..., n и
- Внутренний цикл, который выполняет свопы.
Этот внешний цикл всегда работает & Theta; (n). Тем не менее, внутренний цикл выполняет объем работы, который пропорционален общему количеству свопов, выполненных в течение всего времени выполнения алгоритма. Чтобы узнать, сколько работы будет выполняться в этом цикле, нам нужно будет определить, сколько общих свопов производится во всех итерациях алгоритма.
Здесь присутствуют инверсии. Обратите внимание, что при запуске сортировки вставки он всегда меняет смежные элементы в массиве и только заменяет два элемента, если они образуют инверсию. Итак, что происходит с общим числом инверсий в массиве после выполнения обмена? Ну, наглядно, у нас есть следующее:
[---- X ----] A[j] A[j+1] [---- Y ----]
Здесь X - это часть массива, идущая перед swapped парами, а Y - часть массива, идущего после swapped пары.
Предположим, что мы заменим A [j] и A [j + 1]. Что происходит с количеством инверсий? Итак, рассмотрим произвольную инверсию между двумя элементами. Существует 6 возможностей:
- Оба элемента находятся в X или оба элемента находятся в Y, или один элемент находится в X, а один элемент находится в Y. Тогда инверсия все еще существует, поскольку мы не перемещали ни один из этих элементов.
- Один элемент находится в X или Y, а другой - либо A [j], либо A [j + 1]. Тогда инверсия все еще существует, поскольку относительные упорядочения элементов не изменились, хотя их абсолютные позиции могут иметь.
- Один элемент - A [j], а другой A [j + 1]. Затем инверсия удаляется после обмена.
Это означает, что после выполнения свопа мы уменьшаем количество инверсий точно одним, потому что исчезла только инверсия соседней пары. Это чрезвычайно важно по следующей причине: если мы начнем с я инверсий, каждый своп уменьшит число на один. Как только никаких инверсий не осталось, больше не выполняется свопов. Следовательно, количество свопов равно числу инверсий!
Учитывая это, мы можем точно выразить время выполнения сортировки вставки как & Theta; (n + I), где я - количество инверсий исходного массива. Это соответствует нашим первоначальным границам времени выполнения - в отсортированном массиве есть 0 инверсий, а время выполнения - & Theta; (n + 0) = & Theta; (n), а в массиве с обратной сортировкой - n (n - 1 )/2 инверсии, а время выполнения - & Theta; (n + n (n-1)/2) = & Theta; (n 2). Острота!
Итак, теперь у нас есть сверхточный способ анализа времени выполнения сортировки вставки, заданного конкретным массивом. Посмотрим, как мы можем проанализировать его среднюю продолжительность выполнения. Для этого нам нужно сделать предположение о распределении входных данных. Поскольку сортировка вставки - это алгоритм сортировки на основе сравнения, фактические значения входного массива фактически не имеют значения; на самом деле имеет значение только их относительный порядок. В дальнейшем я буду предполагать, что все элементы массива различны, хотя, если это не так, анализ не изменит все так сильно. Я укажу, куда уходят вещи - script, когда мы доберемся туда.
Чтобы решить эту проблему, мы собираемся ввести кучу индикаторных переменных формы X ij, где X ij - случайная величина, которая равна 1, если A [i] и A [j] образуют инверсию и 0 в противном случае. Там будет n (n - 1)/2 этих переменных, по одному для каждой отдельной пары элементов. Обратите внимание, что эти переменные учитывают каждую возможную инверсию в массиве.
Учитывая эти X, мы можем определить новую случайную переменную I, равную общему числу инверсий в массиве. Это будет дано суммой X:
I = & Sigma; X <суб > IJсуб >
Нам интересно E [I], ожидаемое количество инверсий в массиве. Используя линейность ожидания, это
E [I] = E [& Sigma; X ij] = & Sigma; E [X <суб > IJсуб > ]
Итак, теперь, если мы сможем получить значение E [X ij], мы можем определить ожидаемое количество инверсий и, следовательно, ожидаемое время выполнения!
К счастью, поскольку все X ij являются двоичными индикаторными переменными, мы имеем, что
E [X ij] = Pr [X ij= 1] = Pr [A [i] и A [j] являются инверсией]
Итак, какая вероятность, учитывая случайный входной массив без дубликатов, что A [i] и A [j] являются инверсией? Половина времени A [i] будет меньше A [j], а другая половина времени A [i] будет больше A [j]. (Если дубликаты разрешены, есть скрытый дополнительный термин для обработки дубликатов, но на этот раз мы будем игнорировать). Следовательно, вероятность того, что инверсия между A [i] и A [j] равна 1/2. Следовательно:
E [I] = & Sigma; E [X ij] = & Sigma; (1/2)
Так как в сумме найдется n (n - 1)/2 членов, то это работает на
E [I] = n (n - 1)/4 = & Theta; (n 2)
Итак, в ожидании будут инверсии & Theta; (n 2), поэтому по ожиданию время выполнения будет & Theta; (n 2 + n) = & Theta; (п 2). Это объясняет, почему поведение сортировки в среднем случае есть & Theta; (n 2).
Надеюсь, это поможет!