Jump to content

Спред-сортировка

Spreadsort — это алгоритм сортировки, изобретенный Стивеном Дж. Россом в 2002 году. [1] Он сочетает в себе концепции сортировки на основе распределения, такие как поразрядная сортировка и сортировка сегментами , с концепциями секционирования из сравнительных сортировок, таких как быстрая сортировка и сортировка слиянием . Результаты экспериментов показали, что он очень эффективен, часто превосходя традиционные алгоритмы, такие как быстрая сортировка, особенно в распределениях, демонстрирующих структуру и сортировку строк. Существует реализация с открытым исходным кодом с анализом производительности и тестами, [2] и HTML-документация. [3]

Быстрая сортировка идентифицирует опорный элемент в списке, а затем разделяет список на два подсписка: элементы меньше опорного и элементы выше опорного. Распространенная сортировка обобщает эту идею, разбивая список на разделы n / c на каждом этапе, где n — общее количество элементов в списке, а c — небольшая константа (на практике обычно от 4 до 8, когда сравнения медленные, или намного больше). в ситуациях, когда они быстры). Для этого он использует методы, основанные на распределении: сначала находит минимальное и максимальное значение в списке, а затем разделяет область между ними на n / c интервалы одинакового размера.Там, где кэширование является проблемой, может помочь наличие максимального количества ячеек на каждом этапе рекурсивного деления, в результате чего этот процесс разделения будет выполняться в несколько шагов. Хотя это приводит к увеличению количества итераций, это уменьшает количество промахов в кэше и может ускорить работу алгоритма в целом.

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

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

Производительность

[ редактировать ]

Наихудшая производительность расширенной сортировки составляет O( n log n используется интросорт ) для небольших наборов данных, поскольку в качестве резервного варианта . В случае распределений, где размер ключа в битах k, умноженный на 2, примерно равен квадрату журнала размера списка n или меньше (2 k < (log n ) 2 ), в худшем случае он работает лучше, достигая времени O( n k - log n ) наихудшего случая для первоначально опубликованной версии и O( n ·(( k / s ) + s)) для версии с поддержкой кэша. . Для многих реальных задач сортировки с более чем 1000 элементами, включая сортировку строк, этот асимптотический худший случай лучше, чем O( n log n ).

Были проведены эксперименты по сравнению оптимизированной версии расширенной сортировки с высокооптимизированной версией C++. std::sort, реализованный с помощью интросорта. В списках целых чисел и чисел с плавающей точкой расширенная сортировка показывает улучшение времени выполнения случайных данных примерно в 2–7 раз в различных операционных системах. [1]

По производительности в пространстве расширенная сортировка хуже, чем большинство алгоритмов на месте: в своей простейшей форме это не алгоритм на месте, использующий O( n ) дополнительного пространства; в экспериментах примерно на 20% больше, чем при быстрой сортировке с использованием переменного тока 4–8. При использовании формы с поддержкой кэша (как включено в Boost.Sort) используется меньше памяти, и существует верхняя граница использования памяти, равная максимальному количеству ячеек, умноженному на максимальное количество рекурсий, что в конечном итоге оказывается на несколько килобайт, умноженных на размер. ключа в байтах. Хотя он использует асимптотически больше места, чем издержки O(log n ) быстрой сортировки или издержки O(1) пирамидальной сортировки, он использует значительно меньше места, чем базовая форма сортировки слиянием, которая использует вспомогательное пространство, равное пространству, занимаемому списком. .

Выполнение

[ редактировать ]
unsigned   RoughLog2  (  DATATYPE   вход  )   { 	 unsigned   char   cResult   =   0  ; 	 // && необходим в некоторых компиляторах, чтобы избежать бесконечных циклов; это // существенно не 	 ухудшает производительность 	 if   (  input   >=   0  ) 		 while   ((  input   >>   cResult  )   &&   (  cResult   <   DATA_SIZE  ))   cResult  ++  ; 	 else 		 while   (((  input   >>   cResult  )   <   -1  )   &&   (  cResult   <   DATA_SIZE  ))   cResult  ++  ; 	 вернуть   cResult  ;  }  SIZETYPE  GetMaxCount  (  беззнаковый   logRange  ,   беззнаковый   uCount  )  { 	 беззнаковый   logSize   =   RoughLog2Size  (  uCount  ); 	 unsigned   uRelativeWidth   =   (  LOG_CONST   *   logRange  )  /  ((  logSize   >   MAX_SPLITS  )   ?   MAX_SPLITS   :   logSize  ); 	 // Не пытайтесь сдвинуть бит больше, чем размер элемента 	 if   (  DATA_SIZE   <=   uRelativeWidth  ) 		 uRelativeWidth   =   DATA_SIZE   -   1  ; 	 return   1   <<   ((  uRelativeWidth   <   (  LOG_MEAN_BIN_SIZE   +   LOG_MIN_SPLIT_COUNT  ))   ?  		 (  LOG_MEAN_BIN_SIZE   +   LOG_MIN_SPLIT_COUNT  )   :    uRelativeWidth  );  }  void   FindExtremes  (  DATATYPE   *  Array  ,   SIZETYPE   uCount  ,   DATATYPE   &   piMax  ,   DATATYPE   &   piMin  )  { 	 SIZETYPE   u  ; 	 piMin   =   piMax   =   Массив  [  0  ]; 	 for   (  u   =   1  ;   u   <   uCount  ;   ++  u  )   { 		 if   (  Array  [  u  ]   >   piMax  ) 			 piMax   =   Array  [  u  ]; 		 иначе,   если   (  Массив  [  u  ]   <   piMin  ) 			 piMin   =   Массив  [  u  ]; 	 }  } 	 //---------------------SpreadSort Source-----------------  Bin   *  SpreadSortCore (  DATATYPE   *  Array  ,   SIZETYPE   uCount  ,   SIZETYPE   &   uBinCount  ,   DATATYPE   &  iMax  ,   DATATYPE   &  iMin  )  { 	 // Этот шаг занимает примерно 10% времени выполнения, но помогает избежать худшего 	 // поведения и улучшает поведение при работе с реальными данными. Если вы 	 заранее знаете // максимум и минимум, вы можете передать эти значения 	 // и пропустить этот шаг для первой итерации 	 FindExtremes  ((  DATATYPE   *  )   Array  ,   uCount  ,   iMax  ,   iMin  ); 	 если   (  iMax   ==   iMin  ) 		 вернуть   NULL  ; 	 ТИП ДАННЫХ   divMin  ,  divMax  ; 	 РАЗМЕР ТИП   u  ; 	 int   ЛогДивизор  ; 	 Бин   *   БинАррай  ; 	 Бин  *   ТекущийБин  ; 	 беззнаковый   logRange  ; 	 logRange   =   RoughLog2Size  ((  SIZETYPE  )  iMax  -  iMin  ); 	 если   ((  LogDivisor   =   logRange   -   RoughLog2Size  (  uCount  )   +   LOG_MEAN_BIN_SIZE  )   <   0  ) 		 LogDivisor   =   0  ; 	 // Приведенный ниже оператор if необходим только в системах с большим объемом памяти 	 // задержка относительно скорости процессора (большинство современных процессоров) 	 if   ((  logRange   -   LogDivisor  )   >   MAX_SPLITS  ) 		 LogDivisor   =   logRange   -   MAX_SPLITS  ; 	 divMin   =   iMin   >>   LogDivisor  ; 	 divMax   =   iMax   >>   LogDivisor  ; 	 uBinCount   =   divMax   -   divMin   +   1  ; 		 // Выделяем контейнеры и определяем их размеры 	 BinArray   =   calloc  (  uBinCount  ,   sizeof  (  Bin  )); 	 // Проверка ошибки выделения памяти и чистый возврат с отсортированными результатами 	if   (  !  BinArray  )   { 		 printf  (  «Использование std::sort из-за ошибки выделения памяти  \n  »  ); 		 std  ::  sort  (  Array  ,   Array   +   uCount  ); 		 вернуть   НУЛЬ  ; 	 } 			 // Вычисление размера каждого контейнера; это занимает примерно 10% времени выполнения 	 для   (  u   =   0  ;   u   <   uCount  ;   ++  u  ) 		 BinArray  [(  Array  [  u  ]   >>   LogDivisor  )   -   divMin  ].  uCount  ++  ; 	 // Назначаем позиции бинов 	 BinArray  [  0  ].  CurrentPosition   =   (  ТИП ДАННЫХ   *  )  Массив  ; 	 для   (  ты знак   равно   0  ;   ты   <   uBinCount   -   1  ;   ты  ++  )   { 		 BinArray  [  ты   +   1  ].  CurrentPosition   =   BinArray  [  u  ].  CurrentPosition   +   BinArray  [  u  ].  uCount  ; 		 Бинарный массив  [  u  ].  uCount   =   BinArray  [  u  ].  ТекущаяПозиция    Массив  ; 	 } 	 BinArray  [  u  ].  uCount   =   BinArray  [  u  ].  ТекущаяПозиция    Массив  ; 		 // Перестановка на место. Это доминирует во время выполнения, особенно в подкачке; 	 // Вызовы std::sort являются еще одним основным пользователем времени. 	 for   (  u   =   0  ;   u   <   uCount  ;   ++  u  )   { 		 for   (  CurrentBin   =   BinArray   +   ((  Array  [  u  ]   >>   LogDivisor  )   -   divMin  );    (  CurrentBin  ->  uCount   >   u  );  			 CurrentBin   =   BinArray   +   ((  Array  [  u  ]   >>   LogDivisor  )    divMin  )) 				 SWAP  (  Array   +   u  ,   CurrentBin  ->  CurrentPosition  ++  ); 		 // Теперь, когда мы нашли элемент, принадлежащий этой позиции, 		 // увеличиваем счетчик сегментов 		 if   (  CurrentBin  ->  CurrentPosition   ==   Array   +   u  ) 			 ++  (  CurrentBin  -> ТекущаяПозиция  ); 	 } 		 // Если мы отсортировали сегменты, массив отсортирован, и нам следует пропустить рекурсию 	 if   (  !  LogDivisor  )   { 		 free  (  BinArray  ); 		 вернуть   НУЛЬ  ; 	 } 	 Вернуть   BinArray  ;  }  void  SpreadSortBins  (  DATATYPE   *  Array  ,   SIZETYPE   uCount  ,   SIZETYPE   uBinCount  ,   const   DATATYPE   &  iMax 				 ,   const   DATATYPE   &  iMin  ,   Bin   *   BinArray  ,   SIZETYPE   uMaxCount  )  { 	 SIZETYPE   u  ; 	 for   (  u   =   0  ;   u   <   uBinCount  ;   u  ++  )   { 		 SIZETYPE   count   =   (  BinArray  [  u  ].  CurrentPosition   -   Array  )   -   BinArray  [  u  ].  uCount  ; 		 // Не выполнять сортировку, если нет хотя бы двух элементов для сравнения 		 if   (  count   <   2  ) 			 continue  ; 		 if   (  count   <   uMaxCount  ) 			 std  ::  sort  (  Array   +   BinArray  [  u  ].  uCount  ,   BinArray  [  u  ].  CurrentPosition  ); 		 еще 			 SpreadSortRec  (  Array   +   BinArray  [  u  ].  uCount  ,   count  ); 	 } 	 бесплатно  (  BinArray  );  }  void   SpreadSortRec  (  DATATYPE   *  Array  ,   SIZETYPE   uCount  )  { 	 if   (  uCount   <   2  ) 		 return  ; 			 ТИП ДАННЫХ   iMax  ,   iMin  ; 	 РАЗМЕР ТИП   uBinCount  ; 	 Bin   *   BinArray   =   SpreadSortCore  (  Array  ,   uCount  ,   uBinCount  ,   iMax  ,   iMin  ); 	 если   (  !  BinArray  ) 		 return  ; 	 SpreadSortBins  (  Array  ,   uCount  ,   uBinCount  ,   iMax  ,   iMin  ,   BinArray  , 	                GetMaxCount  (  RoughLog2Size  ((  SIZETYPE  )  iMax  -  iMin  ),   uCount  ));  } 

Два уровня так же хороши, как и любой другой

[ редактировать ]

Интересный результат для алгоритмов этого общего типа (разделение по основанию, затем сортировка на основе сравнения) состоит в том, что они имеют O( n ) для любой ограниченной и интегрируемой функции плотности вероятности . [4] Этот результат можно получить, заставив Spreadsort всегда выполнять еще одну итерацию, если размер ячейки после первой итерации превышает некоторое постоянное значение. Если известно, что функция плотности ключей интегрируема и ограничена по Риману, эта модификация Spreadsort может обеспечить некоторое улучшение производительности по сравнению с базовым алгоритмом и будет иметь лучшую производительность в наихудшем случае. Если на это ограничение обычно нельзя положиться, это изменение добавит к алгоритму небольшие дополнительные накладные расходы во время выполнения и мало что принесет. Другими похожими алгоритмами являются Flashsort (который проще) и Adaptive Left Radix. [5] Адаптивное левое счисление, очевидно, очень похоже, основное отличие заключается в рекурсивном поведении: Spreadsort проверяет наихудшие ситуации и использует std::sort, чтобы избежать проблем с производительностью там, где это необходимо, а адаптивное левое счисление непрерывно рекурсивно до тех пор, пока не будет выполнено или пока данные не станут достаточно маленькими. использовать сортировку вставками.

  1. ^ Стивен Дж. Росс. Spreadsort Высокопроизводительный алгоритм сортировки общего случая. Методы и приложения параллельной и распределенной обработки , том 3, стр. 1100–1106. Лас-Вегас Невада. 2002.
  2. ^ «Репозиторий Boost.Sort на GitHub» . бустерг/сортировка .
  3. ^ «Документация по расширенной сортировке HTML» . Проверено 30 августа 2017 г.
  4. ^ Тамминен, Маркку (март 1985 г.). «Два уровня лучше любого». Дж. Алгоритмы . 6 (1): 138–144. дои : 10.1016/0196-6774(85)90024-0 .
  5. ^ Маус, Арне (2002). ARL, более быстрый алгоритм сортировки на месте, дружественный к кэшу (PDF) (технический отчет). CiteSeerX   10.1.1.399.8357 .
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 083699d9e92d1d4c46a51afc7c3e55ff__1715704860
URL1:https://arc.ask3.ru/arc/aa/08/ff/083699d9e92d1d4c46a51afc7c3e55ff.html
Заголовок, (Title) документа по адресу, URL1:
Spreadsort - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)