# теория · шаг 3 из 7
Поразрядная сортировка: подсчёт по цифрам
Коротко
Поразрядная сортировка сортирует большие целые (и строки) за линейное время, применяя
устойчивую сортировку подсчётом по одной ЦИФРЕ за раз - от МЛАДШЕЙ к старшей. После
прохода по всем d цифрам массив полностью отсортирован. Секрет - в устойчивости: сортируя
по очередной цифре, мы не рушим порядок, наведённый предыдущими (младшими) цифрами.
Стоимость Θ(d·(n + b)), где d - число цифр, b - основание (сколько значений у одной
цифры, например 10). Так подсчёт распространяют на широкий диапазон значений, где прямой
подсчёт раздул бы счётчики.
Если ты понимаешь, почему поразрядная сортировка идёт от младшей цифры к старшей, зачем ей устойчивость подсчёта и откуда берётся Θ(d·(n+b)), листай до «Теперь сам».
Как думаешь, как сортировать подсчётом большие числа
У подсчёта есть беда: при широком диапазоне значений массив счётчиков раздувается.
Сортировать подсчётом числа до миллиарда нельзя - счётчиков был бы миллиард. Но у числа
есть ЦИФРЫ, и каждая цифра - это маленький диапазон (для десятичных - всего 0..9).
Напрашивается: сортировать не по всему числу сразу, а по цифрам по очереди, каждый раз
дешёвым подсчётом с k = 10. Вопрос лишь в том, в каком ПОРЯДКЕ брать цифры и как не
испортить уже наведённый порядок. Ответ - от младшей к старшей, опираясь на устойчивость.
На что это похоже
Старая картотека, которую надо разложить по номерам вида «сотни-десятки-единицы». Ты раскладываешь карточки на 10 стопок по ЕДИНИЦАМ и собираешь их по порядку стопок. Потом те же карточки раскладываешь по ДЕСЯТКАМ и снова собираешь. Наконец - по СОТНЯМ. Важно, что на каждом шаге, собирая стопки, ты не перемешиваешь карточки ВНУТРИ стопки - берёшь их в том порядке, в каком они легли. Тогда к концу карточки с одинаковой сотней уже упорядочены по десяткам и единицам из прошлых проходов. Три дешёвых раскладки по 10 стопок - и вся картотека в порядке.
Механизм: устойчивый подсчёт по цифрам от младшей к старшей
Пусть числа записаны в основании b (например, b = 10) и имеют d цифр. Повторяем d
раз, начиная с МЛАДШЕЙ цифры (единицы), к старшей:
- взять текущую цифру каждого числа (для десятичных -
(x // разряд) % 10); - устойчиво отсортировать массив ПО ЭТОЙ ЦИФРЕ сортировкой подсчётом (
k = b).
Устойчивость - обязательна: сортируя по очередной (более старшей) цифре, элементы с
ОДИНАКОВОЙ текущей цифрой должны сохранить порядок, наведённый предыдущими (младшими)
проходами. После d-го прохода (по старшей цифре) массив отсортирован полностью.
Правило
Поразрядная сортировка применяет УСТОЙЧИВУЮ сортировку подсчётом по одной цифре за раз,
от МЛАДШЕЙ к старшей; после d проходов массив отсортирован. Устойчивость обязательна: она
хранит порядок, наведённый младшими цифрами. Стоимость Θ(d·(n + b)), где d - число
цифр, b - основание. Так подсчёт распространяют на большой диапазон значений: вместо
одного огромного k - d дешёвых проходов с маленьким b.
Разбор: почему от младшей цифры к старшей, а не наоборот
Идти надо от МЛАДШЕЙ. Смысл: старшая цифра важнее младшей, поэтому она должна сортироваться
ПОСЛЕДНЕЙ - тогда её порядок «главнее». А внутри одинаковой старшей цифры порядок задают
младшие, отсортированные РАНЬШЕ и сохранённые устойчивостью. Проверим на 32, 15, 12, 25.
По единицам: 32, 12, 15, 25 (цифры единиц 2,2,5,5; 32 и 12 сохранили взаимный
порядок). По десяткам: 12, 15, 25, 32 (цифры десятков 1,1,2,3; 12 и 15 имели
одинаковый десяток 1 и остались в порядке, наведённом единицами, - 12 перед 15).
Готово. Если бы шли от СТАРШЕЙ, то следующая (младшая) сортировка перемешала бы уже
наведённый старшей порядок - и всё сломалось бы. Направление «от младшей» плюс устойчивость
- вот два условия корректности.
Разбор: когда поразрядная выигрывает у n·log n
Стоимость Θ(d·(n + b)). Если b фиксировано (скажем, 10 или 256), а число цифр d
невелико и не растёт с n, то это Θ(d·n) = Θ(n) - линейно, быстрее n·log n. Например,
сортировать n 32-битных целых можно за d = 4 прохода по байтам (b = 256): Θ(4·(n +
256)) = Θ(n). Выигрыш реален, когда элементов МНОГО, а ключи фиксированной, небольшой
длины. Оговорка: d на самом деле связано с диапазоном значений (d ≈ log_b(макс)), так
что для по-настоящему огромных или переменной длины ключей d растёт, и преимущество перед
n·log n тает. Поразрядная - не всегда «бесплатная линия», а линия при небольшом
фиксированном числе разрядов.
Что ломается без этого
Первая ошибка - использовать НЕустойчивую сортировку для прохода по цифре: тогда каждый
следующий проход перемешивает порядок, наведённый предыдущими, и результат неверен.
Устойчивость подсчёта здесь не украшение, а условие корректности. Вторая - идти от СТАРШЕЙ
цифры к младшей в простом варианте (LSD, least significant digit - «от младшей цифры»):
младшие проходы разрушат старший порядок. (Есть и MSD-вариант, most significant digit -
«от старшей цифры», но он устроен иначе, рекурсивно по группам, и это отдельная история.)
Третья - забыть, что d зависит от диапазона: на ключах переменной или огромной
длины поразрядная теряет линейность.
Зачем это в работе
Поразрядная сортировка - способ отсортировать большие целочисленные ключи и строки
фиксированной длины за линейное время: числовые идентификаторы, IP-адреса, даты как числа,
строки одинаковой длины. Её используют в базах данных и системах обработки больших массивов
чисел, где n огромно, а ключи - фиксированной битовой ширины: там Θ(n) поразрядной
обгоняет Θ(n·log n) быстрой сортировки. Вместе с подсчётом она показывает главную мысль
раздела: n·log n - предел только для СРАВНЕНИЙ, а зная структуру ключей (целые, цифры,
байты), можно сортировать за линию. Выбор сортировки - это в первую очередь вопрос «что я
знаю про свои ключи».
Теперь сам
Поразрядная сортировка обрабатывает 12 и 15 при проходе по ДЕСЯТКАМ. У обоих цифра
десятков - 1. Почему после этого прохода 12 окажется раньше 15, хотя по десяткам они
равны?
Ответ. Потому что проход по десяткам УСТОЙЧИВ, а 12 и 15 уже были упорядочены
предыдущим проходом - по ЕДИНИЦАМ (2 < 5, значит 12 встало раньше 15). При устойчивой
сортировке по десяткам элементы с одинаковой цифрой десятков (1 у обоих) сохраняют свой
входной порядок - тот, что навёл проход по единицам. Поэтому 12 остаётся перед 15. Это
и есть работа устойчивости: более старшая цифра задаёт грубый порядок, а внутри равных
старших цифр порядок берётся от младших, отсортированных раньше и не перемешанных.
Главное
Поразрядная сортировка применяет УСТОЙЧИВУЮ сортировку подсчётом по одной цифре за раз, от
МЛАДШЕЙ к старшей; после d проходов массив отсортирован. Устойчивость обязательна - она
хранит порядок, наведённый младшими цифрами (без неё каждый проход рушил бы предыдущий).
Стоимость Θ(d·(n + b)): при фиксированных небольших d и b это Θ(n), быстрее
n·log n. Так подсчёт распространяют на большой диапазон - d дешёвых проходов с маленьким
основанием b вместо одного огромного k. Выигрыш - когда элементов много, а ключи
фиксированной небольшой длины.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий