Pull to refresh

Comments 39

Кратко.


Я написал новый пупер алгоритм сортировки, он работает быстрее, чем быстрая сортировка, но я не уверен, что она работает правильно.

Эволюция программиста:
— свой, улучшенный, алгоритм сортировки
— свой, улучшенный, алгоритм компрессии данных
— своя библиотека контейнеров, улучшенная по сравнению с std::*
— долгий и мучительный рефакторинг по замене всего вышенаписанного на «стандартные»

Судя по коду, алгоритм действительно сортирует, но не учтен худший случай для алгоритма, а именно случай, в котором localCatchUp минимальных и localCatchUp максимальных элементов на каждой итерации рекурсии оказываются в начале сортируемого массива.
Такая ситуация соответствует выбору почти минимального элемента в алгоритме быстрой сортировки.

upd. to Methos:

там вроде в коде есть проверка идентичности с результатом квиксорта (да, это не строгое доказательство)

А худший случай так и остался по сложности О(n^2)? Тогда чем эта сортировка лучше сортировки слиянием?

На счёт стабильности, не уверен, не проверял. Можете сами проверить. Но по-идее она должна достигаться очень легко. Просто в некоторых местах вместо знака > поставить ≥ или что-то того.

Вы серьезно?

Возможно на счёт
вместо знака > поставить ≥
я переборщил, но у меня не было написать стабильный алгоритм. Тот же Quick Sort не является стабильным алгоритмом сортировки

(а) какая сложность по памяти?
(б) что по сравнению с timsort?

(а) O (3n)
(б) с тимсортом не сравнивал
O (3n)

А у квиксорта — O(log n), если мне память не изменяет.


с тимсортом не сравнивал

А зря, потому что про него известно, что он быстрее квиксорта.

(а) O (3n)

У вас на момент рекурсивного вызова выделено и не освобождено О(n) памяти (массивы restFirst и restSecond). Глубина рекурсии О(log n). Соответственно, сложность по памяти О(n * log n).


UPD: пардон, там arr освобождается. Все таки O(n)

с тимсортом не сравнивал

Сравнил за вас.
Тимсорт лучше чуть менее, чем везде.


Время выполнения на моей машине:


image


                   qsort  std::sort    timsort    newSort
       random      79.56    51.2199    85.0301      122.1
       sorted    36.9301    13.1499   0.339997    4.02001
  part-sorted    78.5999       46.2    28.0399         31
 chunk-sorted    93.1501      43.41    2.73001       11.7
      rsorted      37.88      10.57   0.789863       4.54
 part-rsorted    71.7799      35.73       36.5      32.44
chunk-rsorted      94.85      47.71    3.04001      13.71

Исходник (слегка модифицированный авторский): https://pastebin.com/kQVC6Cam

Это всё конечно очень круто, но в приведённом вами коде сортируются разные массивы. Если мы рандомно сгенерировали массив для сортировки с помощью qsort, а потом генерируем совсем другой массив с другими элементами для алгоритма timsort, то как-то необъективно получается.

В 4 случаях из 7 всем алгоритмам подаются абсолютно одинаковые массивы. В остальных 3 случаях данные отличаются, но их характер — нет. Необъективным было бы сравнивать одного случайной значение с другим. И на основе одного сравнения делать выводы. Здесь же этих случайных значений сто миллионов штук. И они имеют одинаковое распределение. Так что сравнение вполне себе объективно.


Впрочем, если несогласны — добавьте строчку srand(1); между 982 и 983 строками. Получите одинаковые массивы и сравнение, которое на 95% объективнее. Вот только результат сравнения будет таким-же.

Очень странно, я запустил ваш исходник у себя (что я изменил — так это то, что уменьшил в 10 раз количество элементов, так как при 106 ну ооочень долго выполняется), и результаты у нас кардинально отличаются. Можете посмотреть скрин ниже. Запускал из-под 19 студии.
image
На самом деле очень странно наблюдать такое поведение.

Потому что измерять производительность в отладочной сборке — все равно что измерять скорость гоночных болидов в болоте. Включите оптимизации компилятора и картина резко изменится. Хотя с моей может на 100% и не совпасть. Я собирал с gcc.

Да, вы правы. Теперь всё стало на свои места. Вот такой результат:
image

А бывает наоборот. Я как-то несколько лет назад находил багу в .NET, когда неудачный алгоритм инлайна в релизном режиме приводил к экспоненциально долгому и также экспоненциально прожорливому потреблению памяти JIT. Сейчас (после моего репорта в core, но проявлялось и в framework) багу уже пофиксили. Хотя, на мой взгляд и неидеально пофиксили, но всяко лучше, чем было.

Запускал из-под 19 студии.

Ээээ, вы тесты гоняли на дебажной сборке?


(судя еще и по Debug на скриншоте)

Ещё такой момент. В том же тимсорте есть режим так называемого «галопа», который ускоряет слияние массивов. В моей реализации сортировки я не стал внедрять его. Но это дало бы доп. прирост к скорости.
Но это дало бы доп. прирост к скорости.

Не дало бы. Ваши сливаемые массивы формируются совершенно иным образом, чем в тимсорт. Там за счет галопа получается выгода от частичной упорядоченности входных данных. У вас же на момент слияния данные из входного массива разбросаны по 3 другим массивам довольно таки произвольным образом.


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

  • Зачем ручное управление памятью? Вектора тормозят? Меряли?
  • Зачем битовая арифметика? У вас простое возведение в степень тормозит? Аналогично с «i & 1».
  • «удаляем массив размером 2 * len элементов » или «2 ^ len»?
  • "/// works only if arr is pointer assigned by new keyword" — вообще грусть какая-то, ИМХО.
  • Оценивали сложность по памяти?
1) Когда я начинал писать этот алгоритм, то не планировал его вообще куда-то выкладывать. Я писал так, как мне было удобно работать в тот момент.
2) Опять же мне приятней писать i & 1 чем i % 2 != 0
3) Мы выделяли 2 * len памяти, соответственно освобождаем тоже 2 * len
4) Это решается простой обёрткой над представленной реализацией. Мы же можем полностью скопировать массив и сортировать копию. Предоставленная здесь функция рекурсивная, и в ней есть хитрые манипуляции с памятью.
5) Без обёртки O (3n). Если написать над ней обёртку, то O (4n).
Без обёртки O (3n). Если написать над ней обёртку, то O (4n).

O(3n) = O(4n) = O(100500n) = O(n)

1). Но ведь выложили
3). Зачем делать len << 1, а не просто len * 2, если в комментариях у вас умножение? Попахивает, простите, байтойобсблудством
5). Хитрые манипуляции имеют смысл? Константы в O-нотации принято игнорировать.

Про сравнение
Вообще, сравнивать in-place алгоритм с O(log n) дополнительной памяти и O(n) странновато.
Тем более, с хитро скопируемой памятью.
Вам намекают, что их ещё и принято опускать
У моего внутримозгового подсвечивателя ошибок и предупреждений загорелась лампочка на результатах тестов по времени. И какого-либо обоснования, почему на сортировку одного элемента при 100 тыс. элементов в массиве уходит больше времени, чем на сортировку при 10 тыс. и при 1 млн., я лично найти не могу (на случайном массиве). Мне очень кажется, что где-то там скрыт косяк.

Я не вникал полностью в предложенный алгоритм, но для квиксорта это однозначно ненормальное поведение.
Резона смотреть на одиночные тесты консольного приложения особо нет, как мне кажется.
Тут снова возникает вопрос о правильной организации бенчмарка.
Иначе получается, что getCPUTime() (кстати, что оно такое?) меряет непонятно что.
Вдруг винда свопнтуься решила/апдейт качает или в телеграм нотификации пришли?
Ну, тестов там не мало. Причем ситуация повторяется в двух случаях, а в третьем с сортированным массивом квиксорт тоже показывает странное изменение по времени. Именно поэтому у меня и возникли подозрения, что имеются какие-то проблемы с измерениями.
Вся любовь к QuickSort в том, что он делает in-place сортировку. Если ваш массив занимает 500 МБ, то вам не нужен новый массив и т.п. И 2-е в среднем статистически QuickSort работает O(n log n), т.е. попасть на O(n^2) случайно достаточно сложно.
Если изобретается новый гибридный алгоритм, то и сравнивать надо с такими же гибридными, а не с древним QuickSort (на минуточку, ему уже 60 лет!). Тут уже упомянули Timsort, который за последние годы стал стандартом во многих языках. Есть и другие.

А какой есть хороший (ну более-менее) набор готовых тестов для оценки сортировок?


Лучше что пока нашел — это "набора сортировок" от Christopher Swenson.

Взял исходники здесь и добавил в обозначенный выше бенчмарк.
Пришлось немного поправить:


  1. Вместо int сортировать int64_t, как в остальных сортировках в бенчмарке.
  2. Увязать delete[] и malloc() в бенчмарке (он примерно на C):
    • каждый delete[] ptr => if (pfr != malloced_array) delete[] ptr;
    • копировать отсортированный массив если он был перевыделен (да, это искажает результаты, но не принципиально).

В сухом остатке у показанной сортировки:


  • большие проблемы с low cardinality, до 1000 раз медленнее
  • на случайных данных вдвое медленнее std::sort
  • на упорядоченных данных часто примерно как timsort, иногда выигрывает в 2 раза, но местами проигрывает в 4-5-6 раз.

В самом бенчмарке я до этого уже добавил несколько тестов (вариантов распределения значений для сортировки) и две свои сортирующие функции (посмотреть можно тут).


gcc версия 9.2.1 20190827 (Red Hat 9.2.1-1) (GCC)
Целевая архитектура: x86_64-redhat-linux
CFLAGS = -Ofast -g -Wall -pedantic

Все результаты (смотрим на sort_rela589n_int64)
Running tests with random:
extra.h yysort1_int64         - ok,   869995.0 usec
extra.h yysort2_int64         - ok,   900374.0 usec
extra.h sort_rela589n_int64   - ok,  1675713.0 usec
sort.h tim_sort               - ok,  1629556.0 usec
sort.h quick_sort             - ok,  1132862.0 usec
extra.h std_sort_int64        - ok,   999762.0 usec
extra.h std_stable_int64      - ok,  1285915.0 usec
sort.h heap_sort              - ok,  1146402.0 usec
sort.h merge_sort             - ok,  1503107.0 usec
sort.h shell_sort             - ok,  2176027.0 usec
sort.h merge_sort_in_place    - ok,  1463473.0 usec
sort.h grail_sort             - ok,  1808871.0 usec
sort.h sqrt_sort              - ok,  1515565.0 usec
stdlib qsort                  - ok,  2027280.0 usec
-------
Running tests with random 50% duplicates:
extra.h yysort1_int64         - ok,   860580.0 usec
extra.h yysort2_int64         - ok,   898808.0 usec
extra.h sort_rela589n_int64   - ok,  1669673.0 usec
sort.h tim_sort               - ok,  1625739.0 usec
sort.h quick_sort             - ok,  1120857.0 usec
extra.h std_sort_int64        - ok,   996282.0 usec
extra.h std_stable_int64      - ok,  1282137.0 usec
sort.h heap_sort              - ok,  1140383.0 usec
sort.h merge_sort             - ok,  1498133.0 usec
sort.h shell_sort             - ok,  2147107.0 usec
sort.h merge_sort_in_place    - ok,  1459049.0 usec
sort.h grail_sort             - ok,  1810396.0 usec
sort.h sqrt_sort              - ok,  1509897.0 usec
stdlib qsort                  - ok,  2040143.0 usec
-------
Running tests with random two values:
extra.h yysort1_int64         - ok,   253859.0 usec
extra.h yysort2_int64         - ok,   128689.0 usec
extra.h sort_rela589n_int64   - ok, 94094388.0 usec
sort.h tim_sort               - ok,   452126.0 usec
sort.h quick_sort             - ok,   115704.0 usec
extra.h std_sort_int64        - ok,   229533.0 usec
extra.h std_stable_int64      - ok,   362162.0 usec
sort.h heap_sort              - ok,   580296.0 usec
sort.h merge_sort             - ok,   525983.0 usec
sort.h shell_sort             - ok,   485811.0 usec
sort.h merge_sort_in_place    - ok,   434436.0 usec
sort.h grail_sort             - ok,   611444.0 usec
sort.h sqrt_sort              - ok,   544184.0 usec
stdlib qsort                  - ok,   811069.0 usec
-------
Running tests with random of 0..9:
extra.h yysort1_int64         - ok,   406745.0 usec
extra.h yysort2_int64         - ok,   315560.0 usec
extra.h sort_rela589n_int64   - ok,  7734804.0 usec
sort.h tim_sort               - ok,   903047.0 usec
sort.h quick_sort             - ok,   288444.0 usec
extra.h std_sort_int64        - ok,   386615.0 usec
extra.h std_stable_int64      - ok,   665899.0 usec
sort.h heap_sort              - ok,   984863.0 usec
sort.h merge_sort             - ok,   851956.0 usec
sort.h shell_sort             - ok,   836794.0 usec
sort.h merge_sort_in_place    - ok,   783045.0 usec
sort.h grail_sort             - ok,  1697737.0 usec
sort.h sqrt_sort              - ok,   881796.0 usec
stdlib qsort                  - ok,  1303944.0 usec
-------
Running tests with mixed-pattern (1/2 sorted, 1/6 reverse, 1/3 random):
extra.h yysort1_int64         - ok,   653220.0 usec
extra.h yysort2_int64         - ok,   709562.0 usec
extra.h sort_rela589n_int64   - ok,   708971.0 usec
sort.h tim_sort               - ok,   646852.0 usec
sort.h quick_sort             - ok,   885766.0 usec
extra.h std_sort_int64        - ok,   789911.0 usec
extra.h std_stable_int64      - ok,   625716.0 usec
sort.h heap_sort              - ok,  1078595.0 usec
sort.h merge_sort             - ok,   788254.0 usec
sort.h shell_sort             - ok,  1972692.0 usec
sort.h merge_sort_in_place    - ok,   678949.0 usec
sort.h grail_sort             - ok,   979281.0 usec
sort.h sqrt_sort              - ok,   769563.0 usec
stdlib qsort                  - ok,  1114687.0 usec
-------
Running tests with mixed-pattern 50% duplicates:
extra.h yysort1_int64         - ok,   651027.0 usec
extra.h yysort2_int64         - ok,   710886.0 usec
extra.h sort_rela589n_int64   - ok,   707473.0 usec
sort.h tim_sort               - ok,   720871.0 usec
sort.h quick_sort             - ok,   868391.0 usec
extra.h std_sort_int64        - ok,   785505.0 usec
extra.h std_stable_int64      - ok,   623365.0 usec
sort.h heap_sort              - ok,  1075865.0 usec
sort.h merge_sort             - ok,   794544.0 usec
sort.h shell_sort             - ok,  1944434.0 usec
sort.h merge_sort_in_place    - ok,   682350.0 usec
sort.h grail_sort             - ok,  1044301.0 usec
sort.h sqrt_sort              - ok,   771323.0 usec
stdlib qsort                  - ok,  1180723.0 usec
-------
Running tests with mixed-pattern two values:
extra.h yysort1_int64         - ok,   212801.0 usec
extra.h yysort2_int64         - ok,    90349.0 usec
extra.h sort_rela589n_int64   - ok, 70880347.0 usec
sort.h tim_sort               - ok,   164127.0 usec
sort.h quick_sort             - ok,   101332.0 usec
extra.h std_sort_int64        - ok,   182024.0 usec
extra.h std_stable_int64      - ok,   235439.0 usec
sort.h heap_sort              - ok,   531226.0 usec
sort.h merge_sort             - ok,   377192.0 usec
sort.h shell_sort             - ok,   429685.0 usec
sort.h merge_sort_in_place    - ok,   187419.0 usec
sort.h grail_sort             - ok,   396649.0 usec
sort.h sqrt_sort              - ok,   381190.0 usec
stdlib qsort                  - ok,   603217.0 usec
-------
Running tests with mixed-pattern of 0..9:
extra.h yysort1_int64         - ok,   301082.0 usec
extra.h yysort2_int64         - ok,   220010.0 usec
extra.h sort_rela589n_int64   - ok,  4049493.0 usec
sort.h tim_sort               - ok,   327608.0 usec
sort.h quick_sort             - ok,   193150.0 usec
extra.h std_sort_int64        - ok,   282951.0 usec
extra.h std_stable_int64      - ok,   348781.0 usec
sort.h heap_sort              - ok,   928633.0 usec
sort.h merge_sort             - ok,   492125.0 usec
sort.h shell_sort             - ok,   653055.0 usec
sort.h merge_sort_in_place    - ok,   343271.0 usec
sort.h grail_sort             - ok,   944962.0 usec
sort.h sqrt_sort              - ok,   517400.0 usec
stdlib qsort                  - ok,   787898.0 usec
-------
Running tests with sorted:
extra.h yysort1_int64         - ok,   140487.0 usec
extra.h yysort2_int64         - ok,    22329.0 usec
extra.h sort_rela589n_int64   - ok,    78184.0 usec
sort.h tim_sort               - ok,    12440.0 usec
sort.h quick_sort             - ok,   259474.0 usec
extra.h std_sort_int64        - ok,   187773.0 usec
extra.h std_stable_int64      - ok,   161101.0 usec
sort.h heap_sort              - ok,  1001063.0 usec
sort.h merge_sort             - ok,   294122.0 usec
sort.h shell_sort             - ok,   349411.0 usec
sort.h merge_sort_in_place    - ok,    28568.0 usec
sort.h grail_sort             - ok,   390306.0 usec
sort.h sqrt_sort              - ok,   240751.0 usec
stdlib qsort                  - ok,   469580.0 usec
-------
Running tests with sorted 93.75%:
extra.h yysort1_int64         - ok,   315492.0 usec
extra.h yysort2_int64         - ok,   332510.0 usec
extra.h sort_rela589n_int64   - ok,   688122.0 usec
sort.h tim_sort               - ok,   527578.0 usec
sort.h quick_sort             - ok,   667057.0 usec
extra.h std_sort_int64        - ok,   343515.0 usec
extra.h std_stable_int64      - ok,   422551.0 usec
sort.h heap_sort              - ok,  1028983.0 usec
sort.h merge_sort             - ok,   549670.0 usec
sort.h shell_sort             - ok,  1811294.0 usec
sort.h merge_sort_in_place    - ok,   614558.0 usec
sort.h grail_sort             - ok,   816988.0 usec
sort.h sqrt_sort              - ok,   669301.0 usec
stdlib qsort                  - ok,   930576.0 usec
-------
Running tests with reverse:
extra.h yysort1_int64         - ok,   272676.0 usec
extra.h yysort2_int64         - ok,   313761.0 usec
extra.h sort_rela589n_int64   - ok,    83115.0 usec
sort.h tim_sort               - ok,    23235.0 usec
sort.h quick_sort             - ok,   315033.0 usec
extra.h std_sort_int64        - ok,   184726.0 usec
extra.h std_stable_int64      - ok,   251171.0 usec
sort.h heap_sort              - ok,   953360.0 usec
sort.h merge_sort             - ok,   401169.0 usec
sort.h shell_sort             - ok,   496127.0 usec
sort.h merge_sort_in_place    - ok,   323996.0 usec
sort.h grail_sort             - ok,   479127.0 usec
sort.h sqrt_sort              - ok,   257342.0 usec
stdlib qsort                  - ok,   535662.0 usec
-------
Running tests with 111......000:
extra.h yysort1_int64         - ok,   164210.0 usec
extra.h yysort2_int64         - ok,    20986.0 usec
extra.h sort_rela589n_int64   - ok, 94005419.0 usec
sort.h tim_sort               - ok,    17461.0 usec
sort.h quick_sort             - ok,    44549.0 usec
extra.h std_sort_int64        - ok,   135267.0 usec
extra.h std_stable_int64      - ok,   171308.0 usec
sort.h heap_sort              - ok,   496364.0 usec
sort.h merge_sort             - ok,   300909.0 usec
sort.h shell_sort             - ok,   359109.0 usec
sort.h merge_sort_in_place    - ok,    61315.0 usec
sort.h grail_sort             - ok,   258391.0 usec
sort.h sqrt_sort              - ok,   309822.0 usec
stdlib qsort                  - ok,   489699.0 usec
-------
Running tests with 999..., 888..., ..., ...000:
extra.h yysort1_int64         - ok,   163028.0 usec
extra.h yysort2_int64         - ok,    21040.0 usec
extra.h sort_rela589n_int64   - ok, 33905532.0 usec
sort.h tim_sort               - ok,    34008.0 usec
sort.h quick_sort             - ok,    99381.0 usec
extra.h std_sort_int64        - ok,   139307.0 usec
extra.h std_stable_int64      - ok,   176955.0 usec
sort.h heap_sort              - ok,   859782.0 usec
sort.h merge_sort             - ok,   312193.0 usec
sort.h shell_sort             - ok,   372891.0 usec
sort.h merge_sort_in_place    - ok,   109063.0 usec
sort.h grail_sort             - ok,   473525.0 usec
sort.h sqrt_sort              - ok,   355316.0 usec
stdlib qsort                  - ok,   513460.0 usec
-------
Running tests with all same:
extra.h yysort1_int64         - ok,   167847.0 usec
extra.h yysort2_int64         - ok,    21873.0 usec
extra.h sort_rela589n_int64   - ok,    78372.0 usec
sort.h tim_sort               - ok,    12293.0 usec
sort.h quick_sort             - ok,    18023.0 usec
extra.h std_sort_int64        - ok,   137651.0 usec
extra.h std_stable_int64      - ok,   161931.0 usec
sort.h heap_sort              - ok,    68188.0 usec
sort.h merge_sort             - ok,   292633.0 usec
sort.h shell_sort             - ok,   348899.0 usec
sort.h merge_sort_in_place    - ok,    28329.0 usec
sort.h grail_sort             - ok,   207439.0 usec
sort.h sqrt_sort              - ok,   255837.0 usec
stdlib qsort                  - ok,   459560.0 usec
-------
Running tests with sorted blocks of length 10:
extra.h yysort1_int64         - ok,   831530.0 usec
extra.h yysort2_int64         - ok,   868527.0 usec
extra.h sort_rela589n_int64   - ok,  1670283.0 usec
sort.h tim_sort               - ok,  1516381.0 usec
sort.h quick_sort             - ok,  1083222.0 usec
extra.h std_sort_int64        - ok,   961874.0 usec
extra.h std_stable_int64      - ok,  1134245.0 usec
sort.h heap_sort              - ok,  1128264.0 usec
sort.h merge_sort             - ok,  1339936.0 usec
sort.h shell_sort             - ok,  2029549.0 usec
sort.h merge_sort_in_place    - ok,  1314025.0 usec
sort.h grail_sort             - ok,  1688303.0 usec
sort.h sqrt_sort              - ok,  1383832.0 usec
stdlib qsort                  - ok,  1808273.0 usec
-------
Running tests with sorted blocks of length 100:
extra.h yysort1_int64         - ok,   698704.0 usec
extra.h yysort2_int64         - ok,   737751.0 usec
extra.h sort_rela589n_int64   - ok,  1664662.0 usec
sort.h tim_sort               - ok,   842203.0 usec
sort.h quick_sort             - ok,   952365.0 usec
extra.h std_sort_int64        - ok,   832443.0 usec
extra.h std_stable_int64      - ok,   832485.0 usec
sort.h heap_sort              - ok,  1100130.0 usec
sort.h merge_sort             - ok,   992872.0 usec
sort.h shell_sort             - ok,  1722663.0 usec
sort.h merge_sort_in_place    - ok,  1054561.0 usec
sort.h grail_sort             - ok,  1375290.0 usec
sort.h sqrt_sort              - ok,  1088504.0 usec
stdlib qsort                  - ok,  1415336.0 usec
-------
Running tests with sorted blocks of length 10000:
extra.h yysort1_int64         - ok,   361783.0 usec
extra.h yysort2_int64         - ok,   429301.0 usec
extra.h sort_rela589n_int64   - ok,   309345.0 usec
sort.h tim_sort               - ok,   153956.0 usec
sort.h quick_sort             - ok,   658700.0 usec
extra.h std_sort_int64        - ok,   492720.0 usec
extra.h std_stable_int64      - ok,   280025.0 usec
sort.h heap_sort              - ok,  1027617.0 usec
sort.h merge_sort             - ok,   413609.0 usec
sort.h shell_sort             - ok,   725664.0 usec
sort.h merge_sort_in_place    - ok,   370757.0 usec
sort.h grail_sort             - ok,   642283.0 usec
sort.h sqrt_sort              - ok,   438289.0 usec
stdlib qsort                  - ok,   664060.0 usec
-------
Running tests with swapped size/2 pairs:
extra.h yysort1_int64         - ok,   749093.0 usec
extra.h yysort2_int64         - ok,   778670.0 usec
extra.h sort_rela589n_int64   - ok,  1602673.0 usec
sort.h tim_sort               - ok,  1356477.0 usec
sort.h quick_sort             - ok,  1042425.0 usec
extra.h std_sort_int64        - ok,   857866.0 usec
extra.h std_stable_int64      - ok,  1045840.0 usec
sort.h heap_sort              - ok,  1123542.0 usec
sort.h merge_sort             - ok,  1233686.0 usec
sort.h shell_sort             - ok,  2143031.0 usec
sort.h merge_sort_in_place    - ok,  1253314.0 usec
sort.h grail_sort             - ok,  1547192.0 usec
sort.h sqrt_sort              - ok,  1259113.0 usec
stdlib qsort                  - ok,  1721907.0 usec
-------
Running tests with swapped size/8 pairs:
extra.h yysort1_int64         - ok,   421713.0 usec
extra.h yysort2_int64         - ok,   449412.0 usec
extra.h sort_rela589n_int64   - ok,   969430.0 usec
sort.h tim_sort               - ok,   749844.0 usec
sort.h quick_sort             - ok,   753447.0 usec
extra.h std_sort_int64        - ok,   460800.0 usec
extra.h std_stable_int64      - ok,   563436.0 usec
sort.h heap_sort              - ok,  1054573.0 usec
sort.h merge_sort             - ok,   706547.0 usec
sort.h shell_sort             - ok,  1947799.0 usec
sort.h merge_sort_in_place    - ok,   783277.0 usec
sort.h grail_sort             - ok,   986328.0 usec
sort.h sqrt_sort              - ok,   803845.0 usec
stdlib qsort                  - ok,  1112677.0 usec
-------
Running tests with known evil data (odd/even hi/low swap):
extra.h yysort1_int64         - ok,   153238.0 usec
extra.h yysort2_int64         - ok,   105138.0 usec
extra.h sort_rela589n_int64   - ok,   145879.0 usec
sort.h tim_sort               - ok,   305719.0 usec
sort.h quick_sort             - ok,   515508.0 usec
extra.h std_sort_int64        - ok,   169849.0 usec
extra.h std_stable_int64      - ok,   243750.0 usec
sort.h heap_sort              - ok,   984348.0 usec
sort.h merge_sort             - ok,   403293.0 usec
sort.h shell_sort             - ok,   624925.0 usec
sort.h merge_sort_in_place    - ok,   431592.0 usec
sort.h grail_sort             - ok,   675009.0 usec
sort.h sqrt_sort              - ok,   380917.0 usec
stdlib qsort                  - ok,   707122.0 usec

Ну, честно могу сказать, что это лучше, чем "Воронки", тут хотя бы есть код реализации чтобы можно было объективно сравнивать.

1) аллокация памяти — зло. Если массив будет размером с гигабайт, получается вы ещё два выделяете? Это в принципе может как затормозить весь процесс, так и просто тут же вывалить bad_alloc
2) Данные бывают абсолютно разные, не только случайные или упорядоченные. Как поведёт себ алгоритм, если данные лежат в обратном порядке, если равны, чередуются с нулем и 1… Много вариантов, и на самом деле все это может непредсказуемо работать по времени. И если вы соревнуетесь с библиотечной версией sort, то должны и их протестировать.
Кстати у Александреску в этом году на cppcon как раз была про это лекция, как он сортировку улучшал (https://youtu.be/FJJTYQYB1JQ)

Здесь всё принципиально от дополнительной памяти зависит. Например алгоритм красно-чёрного дерева позволяет добавлять в сортированное дерево новый элемент (оставляя его сортированным) за O(log(N)) операций. Откуда получим уже сложность O(n*log(N)).


Также есть алгоритм работающий за O(M) операций (M — количество возможных значений в диапазоне значений), но при условии, что диапазон значений ограничен:
1) Присваиваем каждой сущности из диапазона своё число от 1 до M в порядке возрастания
2) Создаём массив из M значений
3) Записываем в этот массив по индексу, равному соответствующему сущности числу (на случай коллизий можно, например, списки по индексу хранить — так как порядок одинаковых значений не важен)
Особенно хорошо работает с короткими диапазонами и большим количеством элементов.
Если сортируются числа — ещё удобнее (вместо списков можно количество записывать)

Sign up to leave a comment.

Articles