Лекции / Лекция_7_алгоритмы_сортировки_продолжение_ipynb_Colab
.pdf
Итоговый результат: [-2, 0, 3, 5, 7]
Реализация алгоритма на Python
Функция для разбиения подпоследовательностей
def partition(sequence, lo_index, hi_index):
"""
РазбиениеЛомуто sequence-сортируемаяпоследовательность lo_index-леваяграницаподпоследовательности
hi_index-праваяграница подпоследовательности Возвращаетиндексопорногоэлемента
"""
#Опорныйэлемент-первыйэлемент подпоследовательности support_=elsequence[loment_index]
#j-индекудас,будутставитьсяэлементыменьше |
опорного |
||
#Начинаемсlo_index,таккакнаэтоминдексе |
покастоитопорныйэлемент |
||
j=lo_index |
|
|
|
#Проходимповсемэлементамначиная,со |
|
следующегопослеопорного |
|
for i |
in range(lo_index+ |
1,hi_index+ |
1): |
#Еслитекущийэлементменьшеопорного |
|
||
if sequence[i]<support_element: |
|
||
|
#Увеличиваемj-расширяемобластьэлементов |
меньшеопорного |
|
j+= |
1 |
|
|
|
#Меняемместамитекущийэлемент(sequence[i])с |
элеменатомпозицииj |
|
sequence[i],sequence[j]=sequence[j] |
,sequence[i] |
||
#Послепроходаставимопорныйэлементнаего |
|
правильноеместо(позицияj) |
|
#Меняемместамиопорныйэлемент |
(sequence[loсэлемеindex])натомпозицииj |
||
sequence[losequence[j]index],=sequence[j], |
sequence[lo_index] |
||
#Возвращаеминдексопорногоэлемента return j
Реализация алгоритма быстрой сортировки
def quick_sort(sequence, lo_index=None, hi_index=None):
""" |
|
|
|
Рекурсивнаябыстраясортировка( |
|
использованиемразбиенияЛомуто) |
|
sequence-сортируемаяпоследовательность |
|
||
lo_index-леваяграницаподпоследовательности |
|||
hi_index-праваяграница |
подпоследовательности |
||
""" |
|
|
|
#Инициализациялевойграницыприпервомвызове |
|
||
if lo_index |
is None: |
|
|
lo_index= |
0 |
|
|
#Инициализправойцияграницыприпервомвызове |
|
||
if hi_index |
is None: |
|
|
hi_index= |
len(sequence)- |
1 |
|
#Базовыйслучайесли:границысомкнулисьили |
|
пересеклись |
|
#(подпоследовательнизодногоэлементастьили |
|
пустая) |
|
if lo_index>=hi_index: |
|
|
|
return None |
|
|
|
#ВыполняемразбиениеЛомутополучаем, индекс |
|
опорногоэлемента |
|
pivot_index=partition(sequence,lo index,hi |
|
_index) |
|
#Рекурсивносортируемлевуючасть(элементы |
меньшеопорного) |
||
#Леваячастьот:lo_indexдоpivot_index-1 |
|
|
|
quick_sort(sequence,lo index,pivot_index- |
|
1) |
|
#Рекурсивносортируемправуючасть(элементы |
большеопорного) |
||
#Праваячастьот:pivot_index+1доhi_index |
|
||
quick_sort(sequence,pivot index+ |
|
1,hi_index) |
|
Быстрая сортировка. Трехчастное разбиение
На практике в большинстве случаев в сортируемой последовательности содержатся элементы с одинаковыми ключами сортировки. Но при разбиении последовательности на две части элементы равные опорному элементу (по сути в дальнейшей сортировке не нуждающиеся) все равно попадают в левую или правую часть и подвергаются сортировке. Этой проблемы
можно избежать если разбивать последовательность на три части (элементы меньше опорного, элементы больше опорного, элементы равны опорному) и для дальнейшей сортировки вызывать только подпоследовательности с элементами меньше опорного и элементами больше опорного.
Трехчастное разбиение Дейкстры
Один из простых для реализации алгоритм троичного разбиения был предложен Дейкстрой. Он предложил использовать три индекса lt , i , gt . После обработки последовательности:
a[lo .. lt-1] — элементы меньше опорного a[lt .. gt-1] — элементы равные опорному a[gt .. hi] — элементы больше опорного
Разбиение Дейкстры
1.В качестве опорного элемента выбирается первый элемент последовательности ( supportElement ). Объявляются переменные для хранения индексов:
lt = lo
i = lo + 1 gt = hi
2. Выполняем проход по последовательности, пока i <= gt :
Если sequence[i] меньше опорного:
Меняем местами sequence[lt] и sequence[i]
Увеличиваем lt на 1 Увеличиваем i на 1
Если sequence[i] больше опорного:
Меняем местами sequence[gt] и sequence[i]
Уменьшаем gt на 1
Если sequence[i] равен опорному:
Увеличиваем i на 1
3. Возвращаем lt и gt (границы области элементов, равных опорному)
Графическое пояснение алгоритма разбиения Дейкстры
Пошаговый алгоритм
Исходная последовательность: [0, 5, -2, 7, 3] |
|
Инициализация: Опорный элемент ( supportElement ) = 0 (первый элемент). lt = |
lo_index = 0 , i = lo_index + 1 = 1 , |
gt = hi_index = 4 . Начальное состояние: [0, 5, -2, 7, 3] с указателями lo, |
lt, i в начале и gt в конце. |
Шаг 1 (i=1, sequence[1]=5): 5 > 0 (больше). Меняем местами sequence[gt]=3 и sequence[i]=5 → [0, 3, -2, 7, 5] .
Уменьшаем gt=3 . i не увеличивается (остаётся 1), так как на позицию i пришёл новый элемент (3).
Шаг 2 (i=1, sequence[1]=3): 3 > 0 (больше). Меняем местами sequence[gt]=7 и sequence[i]=3 → [0, 7, -2, 3, 5] .
Уменьшаем gt=2 . i остаётся 1.
Шаг 3 (i=1, sequence[1]=7): 7 > 0 (больше). Меняем местами sequence[gt]=-2 и sequence[i]=7 → [0, -2, 7, 3, 5] .
Уменьшаем gt=1 .
Шаг 4: Теперь i=1 , gt=1 . Проверяем условие i <= gt (1 <= 1) → выполняем шаг. sequence[1] = -2 . -2 < 0 (меньше).
Меняем местами sequence[lt]=0 и sequence[i]=-2 → [-2, 0, 7, 3, 5] . Увеличиваем lt=1 , i=2 .
Шаг 5: i=2 , gt=1 . Условие i <= gt (2 <= 1) → ложь. Цикл завершён.
Результат разбиения: [-2, 0, 7, 3, 5] , lt=1 , gt=1 . Области: элементы меньше опорного (0) — [-2] (индекс 0); элементы равные опорному — [0] (индекс 1); элементы больше опорного — [7, 3, 5] (индексы 2..4).
Реализация алгоритма на Python
Функция для разбиения подпоследовательностей
def partition(sequence, lo_index, hi_index):
"""
ТрехчастноеразбиениеДейкстры sequence-сортируемаяпоследовательность lo_index-леваяграница hi_index-праваяграница
Возвращаетграницыобластиэлементовравных, опорному
"""
#Опорныйэлемент-первыйэлемент подпоследовательности support_el=sequence[loment_index]
#lt-границаобластиэлементовменьших,опорного |
|
|
lt=lo_index |
|
|
#i-текущийиндексдляпрохода |
|
|
i=lo_index+ |
1 |
|
#gt-границаобластиэлементовбольших,опорного |
|
|
gt=hi_index |
|
|
#Проходимпопоследовательностипокатекущий, |
индекснепревыситправуюграницу |
|
while i<=gt: |
|
|
#Еслитекущийэлементменьшеопорного |
|
|
if sequence[i]<support_element: |
|
|
|
#Меняемместамиэлеменатомграницеlt |
|
sequence[i],sequence[lt]=sequence[l |
t],sequence[i] |
|
|
#Сдвигаемlt iвправо |
|
i+= |
1 |
|
lt+= |
1 |
|
#Еслитекущийэлементбольшеопорного |
|
|
elif sequence[i]>support_element: |
|
|
|
#Меняемместамиэлеменатомграницеgt |
|
sequence[i],sequence[gt]=sequence[g |
t],sequence[i] |
|
|
#Сдвигаемgtвлево(iнеувеличиваемтаккак, |
новыйэлемещёнтнепроверен) |
gt-= |
1 |
|
#Еслитекущийэлементравенопорному |
|
|
else: |
|
|
|
#Простопереходимкследующемуэлементу |
|
i+= |
1 |
|
#Возвращаемграницыобластиэлементовравных, |
опорному |
|
return [lt,gt] |
|
|
Реализация алгоритма быстрой сортировки
def quick_sort(sequence, lo_index=None, hi_index=None):
"""
Рекурсивнаябыстраясортировкатрёхчастным разбиениемДейкстры sequence-сортируемаяпоследовательность lo_index-леваяграницаподпоследовательности
hi_index-праваяграница |
подпоследовательности |
||
""" |
|
|
|
#Инициализациялевойграницыприпервомвызове |
|
||
if lo_index |
is None: |
|
|
lo_index= |
0 |
|
|
#Инициализправойцияграницыприпервомвызове |
|
||
if hi_index |
is None: |
|
|
hi_index= |
len(sequence)- |
1 |
|
#Базовыйслучайесли:границысомкнулисьили |
пересеклись |
||
#(подпоследовательнизодногоэлементастьили |
пустая) |
||
if lo_index>=hi_index: |
|
|
|
return None |
|
|
|
#ВыполняемтрёхчастноеразбиениеДейкстры |
|
||
#Функцияpartitionвозвращаетсписок[lt,gt]- |
границыобластиэлементовравных,опорному |
||
h=partition(sequence,lo index,hi index) |
|
||
#Рекурсивносортируемлевуючасть(элементы |
меньшеопорного) |
||
#Леваячастьот:lo_indexдоlt-(1гдеlt=h[0]) |
|
||
quick_sort(sequence,lo index,h[ |
|
0]- 1) |
|
#Рекурсивносортируемправуючасть(элементы |
большеопорного) |
||
#Праваячастьотgt+1: доhi_index(гдеgt= |
|
h[1]) |
|
quick_sort(sequence,h[ |
1]+ |
1,hi_index) |
|
#Элементыравныеопорному(отh[0]доh[1])уже |
находятсясвоихместах |
||
#инетребуютдальнейшейсортировки |
|
|
|
Быстрая сортировка. Оптимизация |
|||
Модификация алгоритма быстрой сортировки
Для увеличения оптимальности алгоритма можно применить следующую модификацию. Рекурсивное разбиение последовательности до размера 1 заменить переходом в режим сортировки вставками в случае если размер подпоследовательности меньше или равен определенному значению. Оптимальными называются значения 16, 32, 64. При этом сама реализация остается довольно простой.
Реализация алгоритма на Python
Функция для сортировки вставкой
def insertion_sort(sequence, start_index, end_index):
""" |
|
|
|
|
Сортировкавставкамидляподмассива |
|
sequence[start_index:end_index+1] |
||
sequence-сортируемаяпоследовательность |
|
|
||
start_index-леваяграницасортируемого |
|
|
подмассива |
|
end_index-праваяграницасортируемого |
|
|
подмассива |
|
""" |
|
|
|
|
#Проходимповсемэлементамподмассиваот |
|
|
start_indexдоend_index |
|
for i |
in range(start_iendex,index+ |
|
1): |
|
#Запоминаемтекущийэлементкоторый, будем |
|
вставляотсортированнуюьчасть |
||
paste_element=sequence[i] |
|
|
|
|
#Сдвигаемэлементывправопока,онибольше |
|
вставляемого |
||
#Условиеневышли: залевуюграницуИпредыдущий |
|
элементбольшевставляемого |
||
while i>start_index |
and sequence[i- |
1]>paste_element: |
||
|
#Сдвигаемпредыдущийэлементнаоднупозицию |
|
вправо |
|
sequence[i]=sequence[i- |
|
1] |
|
|
|
#Перемещаемуказательвлево |
|
|
|
i=i- |
1 |
|
|
|
#Вставляемсохранённыйэлементнанайденноеместо sequence[i]=paste_element
Реализация алгоритма быстрой сортировки
def quick_sort(sequence, lo_index=None, hi_index=None):
""" |
|
Модифицированнаябыстраясортировка |
|
Прималомразмереподпоследовательности |
переключнасортировкуетсявставками |
sequence-сортируемаяпоследовательность |
|
|
||||
lo_index-леваяграницаподпоследовательности |
|
|||||
hi_index-праваяграница |
|
подпоследовательности |
||||
""" |
|
|
|
|
|
|
#Инициализациялевойграницыприпервомвызове |
|
|
||||
if lo_index |
is None: |
|
|
|
||
lo_index= |
|
|
0 |
|
|
|
#Инициализправойцияграницыприпервомвызове |
|
|
||||
if hi_index |
is None: |
|
|
|
||
hi_index= |
|
|
len(sequence) - |
1 |
|
|
#Еслиразмерподпоследовательности<=32 |
|
(оптимальноезначение) |
||||
#переключнасортировкуемсявставками |
|
|
||||
if hi_index-lo_index<= |
32: |
|
|
|||
insertion_sort |
(sequence,lo index,hi index |
) |
||||
return None |
|
|
|
|
||
#Выполняемразбие(напримериеразбиение, Ломуто |
|
илиХоара) |
||||
#Получаеминдексопорногоэлемента |
|
|
|
|||
pivot_index=partition |
|
(sequence,lo index,hi index |
) |
|||
#Рекурсивносортируемлевуючасть(элементы |
|
меньшеопорного) |
||||
quick_sort |
(sequence,lo index,pivot_-index |
1) |
||||
#Рекурсивносортируемправуючасть(элементы |
|
большеопорного) |
||||
quick_sort |
(sequence,pivot_+index |
1,hi_index ) |
||||
Напишите программный код или сгенерируйте его с помощью искусственного интеллекта.
