Cортировка слиянием с использованием O(1) дополнительной памяти — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 1: Строка 1:
 +
{| class="wikitable" align="center" style="color: red; background-color: black; font-size: 56px; width: 800px;"
 +
|+
 +
|-align="center"
 +
|'''НЕТ ВОЙНЕ'''
 +
|-style="font-size: 16px;"
 +
|
 +
24 февраля 2022 года российское руководство во главе с Владимиром Путиным развязало агрессивную войну против Украины. В глазах всего мира это военное преступление совершено от лица всей страны, всех россиян.
 +
 +
Будучи гражданами Российской Федерации, мы против своей воли оказались ответственными за нарушение международного права, военное вторжение и массовую гибель людей. Чудовищность совершенного преступления не оставляет возможности промолчать или ограничиться пассивным несогласием.
 +
 +
Мы убеждены в абсолютной ценности человеческой жизни, в незыблемости прав и свобод личности. Режим Путина — угроза этим ценностям. Наша задача — обьединить все силы для сопротивления ей.
 +
 +
Эту войну начали не россияне, а обезумевший диктатор. И наш гражданский долг — сделать всё, чтобы её остановить.
 +
 +
''Антивоенный комитет России''
 +
|-style="font-size: 16px;"
 +
|Распространяйте правду о текущих событиях, оберегайте от пропаганды своих друзей и близких. Изменение общественного восприятия войны - ключ к её завершению.
 +
|-style="font-size: 16px;"
 +
|[https://meduza.io/ meduza.io], [https://www.youtube.com/c/popularpolitics/videos Популярная политика], [https://novayagazeta.ru/ Новая газета], [https://zona.media/ zona.media], [https://www.youtube.com/c/MackNack/videos Майкл Наки].
 +
|}
 +
 
{{boring}}
 
{{boring}}
 
В конспекте содержится несколько ошибок, которые никто не исправлял уже лет 5, половины доказательств нету и вообще это стоит переписать. Если хотите заимплементить корректный алгоритм, придется дополнительно подумать головой. Алсо, про "сравнение в реальных условиях" - бред, по времени этот алгоритм в несколько раз медленнее std::sort. ~[[Участник:Yurik|Yurik]]~, 2017
 
В конспекте содержится несколько ошибок, которые никто не исправлял уже лет 5, половины доказательств нету и вообще это стоит переписать. Если хотите заимплементить корректный алгоритм, придется дополнительно подумать головой. Алсо, про "сравнение в реальных условиях" - бред, по времени этот алгоритм в несколько раз медленнее std::sort. ~[[Участник:Yurik|Yurik]]~, 2017

Версия 07:23, 1 сентября 2022

НЕТ ВОЙНЕ

24 февраля 2022 года российское руководство во главе с Владимиром Путиным развязало агрессивную войну против Украины. В глазах всего мира это военное преступление совершено от лица всей страны, всех россиян.

Будучи гражданами Российской Федерации, мы против своей воли оказались ответственными за нарушение международного права, военное вторжение и массовую гибель людей. Чудовищность совершенного преступления не оставляет возможности промолчать или ограничиться пассивным несогласием.

Мы убеждены в абсолютной ценности человеческой жизни, в незыблемости прав и свобод личности. Режим Путина — угроза этим ценностям. Наша задача — обьединить все силы для сопротивления ей.

Эту войну начали не россияне, а обезумевший диктатор. И наш гражданский долг — сделать всё, чтобы её остановить.

Антивоенный комитет России

Распространяйте правду о текущих событиях, оберегайте от пропаганды своих друзей и близких. Изменение общественного восприятия войны - ключ к её завершению.
meduza.io, Популярная политика, Новая газета, zona.media, Майкл Наки.
nothumb
Эта статья сделана из уныния и отчаяния.
Сделайте с ней что-нибудь.
Пожалуйста.

В конспекте содержится несколько ошибок, которые никто не исправлял уже лет 5, половины доказательств нету и вообще это стоит переписать. Если хотите заимплементить корректный алгоритм, придется дополнительно подумать головой. Алсо, про "сравнение в реальных условиях" - бред, по времени этот алгоритм в несколько раз медленнее std::sort. ~Yurik~, 2017


На вход алгоритм получает массив, который состоит из двух отсортированных частей. Нам необходимо за [math]O(1)[/math] дополнительной памяти и [math]O(n)[/math] времени получить отсортированный массив.
В реализации алгоритм весьма громоздкий. Но сравнение в реальных условиях реализации на C++ (на массивах длиной до [math]10^8[/math]) показало, что по числу сравнений алгоритм выигрывает у qsort примерно 10%, а по общему времени работы – от [math]1.2[/math] до [math]1.3[/math] раз.

Алгоритм

У нас есть массив, который состоит из двух отсортированных частей:

Merge O(1) 1.png

Разобьем наш массив на [math]cnt[/math] подряд идущих блоков длиной [math]len = \lfloor \sqrt{n} \rfloor [/math]. Остаток трогать не будем.

Merge O(1) 2.png

Найдем блок, содержащий конец первой отсортированной части. Поменяем его с последним блоком. В дальнейшем будем использовать его как буфер обмена.

Merge O(1) 3.png

Отсортируем блоки по возрастанию по первому элементу (если первые элементы равны, тогда по последнему).

Merge O(1) 4.png

Для этого подойдет любая квадратичная или более быстрая сортировка, которая требует [math] O (1) [/math] дополнительной памяти. Для сохранения линейной асимптотики надо использовать алгоритм, линейный по числу обменов, т.е. подходит сортировка выбором. Так как блоков [math] \sqrt{n} [/math], то количество операций на этом шаге [math] O(n) [/math].

Следует заметить, что, после сортировки этих блоков, элементы, которые стоят левее заданного и больше его, находились в противоположной части отсортированного массива, также они находятся в пределах одной группы, поэтому количество инверсий для каждого элемента не больше [math]\sqrt{n}[/math].

Пользуясь буфером обмена, последовательно сольем пары соседних блоков [math]([0, ~ len - 1][/math] и [math][len, ~ 2 ~ len - 1],[/math] потом [math][len, ~ 2 ~ len - 1][/math] и [math][2 ~ len, ~ 3 ~ len - 1],[/math] и т.д.[math])[/math].

Попытаемся слить первый и второй блок. Поменяем местами первый блок с буфером обмена. И, как в обычном слиянии, пользуясь двумя указателями, сливаем второй блок и только что измененный буфер. Результат начинаем записывать с начала первого блока. Чтобы не потерять данные, вместо записи используем обмен элементов. Так как блоки имеют одинаковую длину и между указателем на второй блок и указателем на запись расстояние равно длине блока, то слияние произойдет корректно (пример использования буфера обмена приведен ниже).

Merge O(1) 5.png

Так как после предыдущего шага количество инверсий для каждого элемента не больше [math]\sqrt{n}[/math], то ему надо сдвинуться влево не больше, чем на [math]\sqrt{n}[/math] элементов, поэтому в результате мы получим, что первые [math]len \cdot (cnt - 1)[/math] элементов исходного массива отсортированы. Количество блоков [math] \sqrt{n} [/math] и каждое слияние работает за [math] О O(\sqrt{n}) [/math] , поэтому количество операций на этом шаге [math] O(n) [/math].

[math]S[/math] — размер остатка вместе с буфером. Используя квадратичную или более быструю сортировку, которая требует [math] O(1) [/math] дополнительной памяти, отсортируем подмассив длиной [math] 2S [/math], который находится в конце.

Так как [math]S \lt 2 \sqrt{n}[/math], то сортировка пройдет за [math]O(n)[/math].

Merge O(1) 6.png

Теперь на последних [math] S [/math] местах будут находиться [math] S [/math] максимальных элементов. Оставшаяся часть представляет собой массив, содержащий две отсортированные части, причем размер второй равен [math] S [/math]. По аналогии с тем что делали раньше, только в обратную сторону, отсортируем оставшуюся часть, разделив ее на блоки длиной [math]S[/math], используя последние [math]S[/math] как буфер обмена. Не забудем после отсортировать буфер обмена.

Merge O(1) 7.png

В результате мы получили отсортированный исходный массив.

Пример использования буфера обмена

Merge O(1) buffer.png

Источники информации