Блог пользователя SkyHawk

Автор SkyHawk, 15 лет назад, По-русски

В попытке достичь красного желтого хотя бы фиолетового цвета, и как следствие, при чтении умной книжки родилась идея провести сравнение различных методов сортировки. Мне кажется, результат может быть интересен сообществу, поэтому привожу сравнительную таблицу. Все сортировки реализованы для массива целых 32-битных чисел.

 Название О(...) Данные Время(c) Код Описание
 Выбором n2  104 0.04linklink
 Пузырьковая n2   104  0.22linklink. Устойчивая. Плюсы: легка для написания.
 Пузырьковая (опт.)  n2   104  0.23link Добавлен сторожевой элемент (вообще можно добавить много оптимизаций, только толку от них не много =) )
 Вставками  n2   104  0.04 link link Эффективна на маленьких наборах. Устойчивая.
 Быстраяn*logn 106  0.16linklink.Неустойчивая сортировка.В худшем случае работает за О(n2). Тратит О(log n) памяти (в худшем случае О(n))
 С++ sort n*logn    106   0.11link Встроенная в С++ STL сортировка.
С++ stable_sortn*(logn)2   106   0.14 link Её устойчивая версия. При достаточном объеме дополнительной памяти работает за О(n*logn)
 С qsortn*logn   106   0.36linkВстроенная в Си сортировка 
 Слиянием n*logn  106  0.37link link Устойчивая сортировка. Расходует О(n) памяти.
Слиянием(опт.) n*logn   106   0.16link Оптимизация выделения памяти и использование сортировки вставками для маленьких подмассивов.
 Пирамидальная (HeapSort)n*logn   106  0.3 linklink Требует О(1) дополнительной памяти, строит сортирующее дерево, которое можно использовать для других целей. Неустойчива.
 Поразряднаяn*sizeof(int)   106  0.03 linklink. Числа сортируются побайтно. Требует  О(n) памяти.
 Блочная (BucketSort)   106  0.2linklink. Корзины реализованны как списки на основе массива целых чисел. Требует О(n) памяти.
 Рандомная =)n*n!  100.02 - 3link link.

  Здесь лежат все файлы единым архивом, make-файлом и тестирующей программой для проверки сортировок. 

Компилировал с опциями оптимизации под Linux: g++ -march=native -static -O3 -c filename.cpp

В планах: 

  • Реализовать сортировки для списков и строк.
  • Научиться писать нормальные makefile'ы
  • Написать Natural Merge Sort

Хотелось бы увидеть:

  • Советы по реализации и оптимизации алгоритмов
  • Более качественные версии ваших любимых сортировок.
  • Нормальный BucketSort (радиус кривизны моих рук не позволяет написать это)

Заранее спасибо за комментарии и удачи на предстоящем Beta Round!

P.S.Напоследок: SleepSort =)

UPD. Добавлено более полное описание. Спасибо всем за замечания!

  • Проголосовать: нравится
  • +40
  • Проголосовать: не нравится

15 лет назад, скрыть # |
Rev. 2  
Проголосовать: нравится +2 Проголосовать: не нравится

Интересный материал:) На мой взгляд, было бы неплохо добавить в таблицу для каждой сортировки ее преимущества и недостатки по сравнению с другими.

Не знаю как на С++, но на Паскале строки сортируются так же как и массивы чисел.

  • 15 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +6 Проголосовать: не нравится
    Угу, например мин/макс/среднюю асимптотику, возможно примеры входных данных для крайних случаев, является ли сортировка stable. Может ещё что-то.
  • 15 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится
    Строки - понятно что так же, но сравнение строк - операция довольно дорогая. С другой стороны, при попытке использовать поразрядную сортировку асимптотика составит О(n*k), где k - длина строк, что при маленьком количестве длинных строк будет накладно. Поэтому результат может отличаться.
15 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
Интересная задумка.
Не знаю, видели вы или нет, но на Вики есть очень подробный материал об очень многих сортировках., включая описание их преимуществ и недостатков. Можно использовать, как начальную базу для собственных проверок.
  • 15 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится
    Интересно будет проверить их эффективность в реализации для списков. Про это мало где пишут, а задача порой важна.
15 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
Возможно, для тех, кто все эти сортировки хорошо знает материал не интересен(я например всех не знаю, по большому счету это и не обязательно, но полезно), но ведь эту таблицу могут увидеть как раз те, кто находится на первом году обучения и почерпнуть полезную информацию для себя. И как бы там ни было автор таблицы, сделав ее, уж точно знания свои не ухудшил (а возможно открыл для себя что-то новое. и не важно будь то какая либо мелочь или что-то поглобальнее - от этого только польза).

  • 15 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +2 Проголосовать: не нравится
    Да, эта таблица скорее для начинающих. К коим я и себя отношу =) Но на мой взгляд даже здесь можно почерпнуть чего-нибудь интересного, например превосходство разрядной сортировки, а также то, что стандартная сортировка настолько эффективна. 
15 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
Не очень понятно, почему "Блочная (BucketSort)" имеет асимптотику O(n). Кто-нибудь может объяснить подробно?
  • 15 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится
    Она вроде имеет асимптотику O(n) только в том случае, когда входные данные случайны. В Кормене есть доказательство ожидания времени работы на массиве случайных дробных чисел от 0 до 1.
  • 15 лет назад, скрыть # ^ |
    Rev. 2  
    Проголосовать: нравится 0 Проголосовать: не нравится

    Нет, не асимптотику. Это ожидаемое время работы на случайно равномерно распределенных входных данных. Асимптотика, вроде бы, O(n2)
15 лет назад, скрыть # |
 
Проголосовать: нравится +12 Проголосовать: не нравится
Вам будет интересно узнать, что стабильная сортировка работает за время . Пруф: 1,2,3
  • 15 лет назад, скрыть # ^ |
     
    Проголосовать: нравится -8 Проголосовать: не нравится
    наверное стандартная сортировка быстрее за счет того что идет работа с указателями...
    • 15 лет назад, скрыть # ^ |
       
      Проголосовать: нравится +8 Проголосовать: не нравится
      А почему коммент к моему сообщению?
      • 15 лет назад, скрыть # ^ |
         
        Проголосовать: нравится 0 Проголосовать: не нравится
        ну как бы в принципе встроенный сорт работает дольше чем NlogN и по идее время работы должно занимать больше, а получается наоборот... вот как бы поэтому и к вашему
        • 15 лет назад, скрыть # ^ |
           
          Проголосовать: нравится 0 Проголосовать: не нравится
          Он очень прямо написан. И я думаю что все таки Nlogn хотя бы в среднем. Там даже всякая магия типа если мы очень маленькие то разберем ручками (например для 5 можно 7 сравнений), если просто маленькие то вставками, иначе Qsort. А если мы как-то глубоко ушли в рекурсию, то толи merge сорт толи heap сорт запускается вроде. Хотя может все это легенды, но 5 миллионов чисел сортирует спокойно за секунду. (во всяком случае, я помню что где-то такое сдавал)
        • 15 лет назад, скрыть # ^ |
           
          Проголосовать: нравится +5 Проголосовать: не нравится
          stl::sort работает за в худшем случае в большинстве реализаций (ms, sgi, stlport), ибо там не qsort, а introsort.
  • 15 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится
    Спасибо! Не вдавался в этот вопрос, сейчас исправлю.
    • 15 лет назад, скрыть # ^ |
       
      Проголосовать: нравится -18 Проголосовать: не нравится
      А ваша быстрая за n2 в худшем случае работает. Надо писать быструю с выбором случайного элемента, а не среднего.
      • 15 лет назад, скрыть # ^ |
         
        Проголосовать: нравится 0 Проголосовать: не нравится
        Это-то понятно, но вроде как классический вариант быстрой... Зато теперь для неё не трудно построить пример худшего случая =) И если честно, я её просто не люблю. 
      • 15 лет назад, скрыть # ^ |
         
        Проголосовать: нравится +9 Проголосовать: не нравится
        Взятие случайного элемента сложность не изменит.
        Тест будет уже не подобрать просто так. Однако, зная устройство рандома и сид, это можно сделать. И в любом случае то, что тест не подобрать, не значит, что его нет. А он есть.
        • 15 лет назад, скрыть # ^ |
           
          Проголосовать: нравится +1 Проголосовать: не нравится
          Сложность худшего останется, да, но вот вероятность наступления такого случая становится ничтожно мала. Введение случайной величины приводит к тому, что худший случай перестаёт зависеть от строения входных данных и теперь зависит лишь от генератора. В данном случае, вы уже не тест подбираете, а изменяете условия окружения.
          • 15 лет назад, скрыть # ^ |
             
            Проголосовать: нравится +9 Проголосовать: не нравится
            Вы не правы. Если при равномерном распределении входных данных взять вероятность плохого исхода или просто распределение времени выполнения сортировки, то переход к случайному выбору медианы не изменит ничего. Поэтому, если все большие тесты жюри рандомные и программа прогоняется один раз, то шансы пройти у неё тоже не меняются. Вероятно, предположение про жюри можно заменить условием, что они не пытаются специально завалить конкретную реализацию сортировки (что обычно так). В таком случае на олимпиаде вообще нет разницы, какой метод использовать.
            • 15 лет назад, скрыть # ^ |
               
              Проголосовать: нравится +5 Проголосовать: не нравится
              =============================================
              Кхм, посмотрите, пожалуйста, ещё раз, внимательнее, на то, что я писал. Я пока не могу видеть момента в моём сообщении, в котором я явно не прав.
              Дело, вроде же, было так:
              1) Вы говорите про подбор теста для деградации
              2) Я говорю, что при введении случайной велчины худший случай перестаёт явно зависеть от конкретного строения входных данных
              3) Вы говорите про равномерное распределение входных данных
              В последнем случае - да, рандомизация не улучшит ситуацию, но, надо заметить, что до этого вашего сообщения в предыдущем обсуждении не было речи о равномерном распределении, а лишь о конкретном "валящем" тесте.
              Вообще, рандомизация вводится как раз из тех соображений, что в жизни некоторые, нехорошие для быстрой сортировки, входные данные могут попадаться чаще других. Рандомизация, как я это себе преставляю, как бы уравнивает все входные данные, превращая их в, словно бы, равномерно распределённые. Могу, конечно, ошибаться, но вроде же всё так.
              • 15 лет назад, скрыть # ^ |
                 
                Проголосовать: нравится -16 Проголосовать: не нравится
                Не хочу засорять прямой эфир. Кто может написать на java такую задачу: даны два числа (a,b<=2^(10000)). надо найти a xor b.
              • 15 лет назад, скрыть # ^ |
                 
                Проголосовать: нравится +8 Проголосовать: не нравится
                Надо начать чуть раньше.
                freopen написал такую фразу, из которой можно было бы сделать вывод, что введение случайности меняет трудоёмкость.
                Основной смысл моего комментария был в том, что это не так. Трудоёмкость остаётся прежней. В подтверждение своей точки зрения я сказал, что можно подобрать тест. Точнее, подбирать его никто не собирается, просто он существует.
                Следующий мой комментарий был возмущением по поводу вашей фразы "вероятность наступления такого случая становится ничтожно мала". Формально она неверна, что я и попытался показать.
                По поводу вашего последнего замечания. Может, это и так, но, поскольку в среднем быстрая сортировка всё-таки работает за O(n log n), доля количества плохих тестов не более O(log n/n), то есть немного. Плюс ещё два субъективных аргумента. Мне кажется, у плохих тестов не такая простая структура, чтобы они хоть сколь-нибудь часто попадались на соревнованиях. За несколько лет студенческих олимпиад, что мне приходилось писать сортировку вручную, ни разу подлян не было.
                Резюме. На олимпиадах можно использовать, если хочется перестраховаться. Формально трудоёмкость всё равно O(n2).
                • 15 лет назад, скрыть # ^ |
                   
                  Проголосовать: нравится 0 Проголосовать: не нравится
                  А я налетал на antiqsort 3 или 4 раза.
                  • 15 лет назад, скрыть # ^ |
                     
                    Проголосовать: нравится 0 Проголосовать: не нравится
                    Ясно. Но это называется именно подляна. Не ясно, чем может руководствоваться жюри, давая такие тесты. Разве что самолюбие потешить.
                    • 15 лет назад, скрыть # ^ |
                       
                      Проголосовать: нравится 0 Проголосовать: не нравится
                      Да, но это кажется в порядке вещей. Если жюри предполагает, что есть случай на который у кучи участников будет завал даже в правильном алгоритме (крайний случай, переполнение и т.п.), неужели жюри не даст тест на такой случай?
                      • 15 лет назад, скрыть # ^ |
                         
                        Проголосовать: нравится 0 Проголосовать: не нравится
                        Только в том случае, если очень гордится, что его разобрало само. Это стандартный алгоритм, все знают его свойства. Задача жюри обеспечить качественное black-box тестирование, а не давать странное преимущество одному правильному решению перед другим. Я привык к такому пониманию роли жюри.
                        • 15 лет назад, скрыть # ^ |
                           
                          Проголосовать: нравится 0 Проголосовать: не нравится
                          Скажите, а как вы относитесь к взломам и челленжам?
                          • 15 лет назад, скрыть # ^ |
                             
                            Проголосовать: нравится +6 Проголосовать: не нравится
                            Как к white-box тестированию. То есть да, если код будут смотреть, то не стоит писать без случайного выбора. Если вас почелленджили антикусортом, я в этом ничего страшного не вижу в отличие от заготовленных заранее тестов.
                            • 15 лет назад, скрыть # ^ |
                               
                              Проголосовать: нравится 0 Проголосовать: не нравится
                              Ясно, спасибо за развернутый ответ.
                            • 15 лет назад, скрыть # ^ |
                               
                              Проголосовать: нравится 0 Проголосовать: не нравится
                              ===================================
                              А не считаете ли вы плохим, что одинаковые решения в разных комнатах могут быть зачтенным у одного и незачтенным у другого?
                            • 15 лет назад, скрыть # ^ |
                               
                              Проголосовать: нравится 0 Проголосовать: не нравится
                              А что если я делаю qsort опираясь на встроенный в паскаль rand, а мой оппонент подобрал тест против рандома в паскале?
                              • 15 лет назад, скрыть # ^ |
                                Rev. 2  
                                Проголосовать: нравится +1 Проголосовать: не нравится

                                ___шире___пожалуйста____________________________________________
                                вроде, паскалисты в начале randomize прописывают. Не знаю, является ли это эквивалентом srand(time(null)) в C++?
                                • 15 лет назад, скрыть # ^ |
                                   
                                  Проголосовать: нравится 0 Проголосовать: не нравится
                                  ==================================
                                  Но это же бред. Т.е. любая ошибка в qsort, включая вот такую тупость дает возможность меня челленжить.
                                  • 15 лет назад, скрыть # ^ |
                                     
                                    Проголосовать: нравится +8 Проголосовать: не нравится
                                    ==============================
                                    >ошибка дает возможность меня челленжить
                                    >бред
                                    • 15 лет назад, скрыть # ^ |
                                       
                                      Проголосовать: нравится -8 Проголосовать: не нравится
                                      =====================
                                      Ваши сообщения через чур лаконичны. Что имелось в виду? Я имею в виду, что есть малый процент участников, которые подготовили тест против qsort с рандомизацией  но без случайной инициализации рандома (что оправдано в других олимпиадах, ведь жюри может перетестировать решение и зачесть худший из результатов), эти участники должны получить преимущество перед остальными? А среди них те, кто попал в комнату к большому количеству паскалистов? Как то оно не слишком здорово.
                                      • 15 лет назад, скрыть # ^ |
                                         
                                        Проголосовать: нравится 0 Проголосовать: не нравится
                                        ===========================
                                        Я так понимаю, скоро все взломы будут добавляться в систесты.
                                        Так что паскалистам придется писать heapsort или mergesort (почему они не делают этого всегда?)
                                      • 15 лет назад, скрыть # ^ |
                                         
                                        Проголосовать: нравится 0 Проголосовать: не нравится
                                        ==================================
                                        У системы с комнатами и челленджами есть много недостатков, связанных со случайным/недостаточно честным разбиением. Это не имеет отношения к проблемам qsort.
                • 15 лет назад, скрыть # ^ |
                  Rev. 2  
                  Проголосовать: нравится 0 Проголосовать: не нравится

                  ==========================
                  Про вероятность плохого случая и контекст, в котором это говорилось, я уже писал раньше.
                  А по поводу написания  быстрой сортировки на студенческих олимпиадах: 
                  Сейчас это уже, имхо, лишнее (stl-вский sort вполне хорош (он, если не ошибаюсь, в основных реализациях представляет собой introsort или что-то похожее), у Java, если не ошибаюсь, со встроенной сортировкой тоже хорошо). Я ни в коем случае не утверждаю, что её не нужно знать и понимать, и уметь написать. Но писать каждый раз с нуля - ненужная роскошь.

                  А, ещё, в легенды СП уже, наверное, вошла история про то, как на одном из соревнованийбыл подобран тест под qsort из Си и по этой причине упала задача у Petr
                  • 15 лет назад, скрыть # ^ |
                     
                    Проголосовать: нравится 0 Проголосовать: не нравится
                    Ну я и не говорю, что его надо писать. Сейчас такая ситуация только у школьников, наверное.
                    Про Пету я слышал другую версию. Это были шарпы, а тест был типа сколько-то возрастающих чисел, потом сколько-то убывающих. Так что это беда конкретной реализации в языке, которой, и правда, непозволительны подобные промахи.
                  • 15 лет назад, скрыть # ^ |
                     
                    Проголосовать: нравится 0 Проголосовать: не нравится
                    Там тест был не специально такой. Просто оказалось, что на тесте "1 2 ... n n ... 2 1" стандартный qsort работает за O(n^2). Желающие могут доказать.
  • 15 лет назад, скрыть # ^ |
    Rev. 3  
    Проголосовать: нравится 0 Проголосовать: не нравится

    Это только если не получится выделить O(n) дополнительной памяти для merge sort (который работает за ), тогда оно переключается на in-place merge sort (), что, кстати, и написано в приведенных Вами ссылках (и в Стандарте: 25.3.1.2).
    Думаю, для стандартного применения можно считать, что памяти хватит :)
15 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
кст, рандомная сортировка реализована немного криво, например на массиве 1, 1, 1 она тлится
15 лет назад, скрыть # |
 
Проголосовать: нравится +17 Проголосовать: не нравится
А где GoroSort, почему не упомянут? :)
15 лет назад, скрыть # |
 
Проголосовать: нравится +5 Проголосовать: не нравится
Хотелось бы увидеть Гномью Сортировку!А чем отличается с++ qsort и быстрая сортировка, не считая времени Работы! Разве не один и тот же алгоритм?
  • 15 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +7 Проголосовать: не нравится
    Если вы говорите про std::sort (а не std::qsort), то он ведёт себя примерно так:
    1. Если n ≤ 3, то разбираем руками за три сравнения.
    2. Далее пускаем хорошо написанный qsort
    3. Если он углубился слишком сильно - heapsort.
15 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
кстати, раз уж речь зашла о сортировках, на каком тесте, например для 10, быстрая сортировка будет работать N2?
15 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
Вообще имеет смысл сравнивать для сортировок не только время работы, но и количество сравнений. так например для тяжёлой функции сравнения Merge sort будет круче чем qsort, std::stable_sort круче чем std::sort.
15 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится
Давно было интересно -- а существует сортировка, которая обладает следующими характеристиками:
1. Время худшее: O(N log N) -- пусть даже с очень тупо завышенной константой
2. Память худшая: O(1)
3. Stable
или нет?