Перейти из форума на сайт.

НовостиФайловые архивы
ПоискАктивные темыТоп лист
ПравилаКто в on-line?
Вход Забыли пароль? Первый раз на этом сайте? Регистрация
Компьютерный форум Ru.Board » Компьютеры » Прикладное программирование » Элементарное улучшение QuickSort

Модерирует : ShIvADeSt

 Версия для печати • ПодписатьсяДобавить в закладки

Открыть новую тему     Написать ответ в эту тему

delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Почитав Вики про быструю сортировку можно найти способы улучшения. Меня интересовало элементарное улучшение. Я сортирую уникондые строки - объем выше 3х миллионов. Время приведу в формате минуты:секунды:миллисекунды. Алгоритм быстрой сортировки я пытаюсь улучшить за счёт правильного выбора опорного элемента - медианы. То есть я добавляю два сравнения для трёх элементов и в случае нахождения среднего сдвигаю медиану на него. На уже отсортированных данных я замедления не получил (хотя странно - замедление миллисекунды может я чтото неправильно тестировал). На данных состоящих из сортированных кусков - было 3:34:915 стало 3:30:577. На хаотичных данных было 3:55:911 стало 3:40:451. То есть секунды я действительно выигрываю. У меня установлен порог - дистанция 16. Этим я добиваюсь малого влияния на небольшие объемы данных. Может кто поможет потестировать и любая критика только приветствуется. Любые уточнения и мысли...
 
Вот видоизменённый код
 
В моём коде видно что я изменяю медиану а не делаю сразу перемещение элементов к медиане. Я прововал этот вариант только замедлил алгоритм. Хотя выигрыш кажется не значительным, но всё же зачем компьютеру тратить лишнее время? Пожалуйста не принимайте всё просто на веру, - желательно протестировать, меня интересуют так же Ваши результаты. Вот заготовка для измерения времени:
 
d := Now;
sort;
d := Now - d;
ShowMessage(FormatDateTime('nn:ss:zzz', d));
 
Заранее всем спасибо.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 13:20 20-04-2013 | Исправлено: delover, 12:45 21-04-2013
adasiko



Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
А если ещё стандартную встроенную функцию сортировки попробовать для сравнения?

Всего записей: 1807 | Зарегистр. 30-06-2008 | Отправлено: 13:48 20-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
adasiko
Да стандартную, у меня нестандартный код выделен комментарием. Если что сильно не бейте, меня больше беспокоят уже сортированные данные - то есть плохие случаи.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 14:06 20-04-2013
LadyOfWood

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
меня больше беспокоят уже сортированные данные - то есть плохие случаи.

В смысле? Если отсортированы то может сперва проверить.

Всего записей: 620 | Зарегистр. 16-09-2003 | Отправлено: 16:40 20-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
В смысле? Если отсортированы то может сперва проверить.  

Вы хоть поняли за счёт чего ускорение? За счёт двух дополнительных сравнений для вычисления лучшей медианы. В отсортированных данных медиана уже лучшая - дополнительные сравнения не приносят пользы.  
 
Я поставил счётчик на отсортированные данные в процедуру сравнения:
63544056 - сравнений с моим алгоритмом
63478522 - сравнений стандартного алгоритма
65534 - посчитанная разница. - бесполезные сравнения - медиана не двигалась.
 
Теперь для хаотичных данных (объем данных тот же, только строки задом наперед)  
84100202 - сравнений с моим алгоритмом  
89129019 - сравнений стандартного алгоритма  
-5028817 - посчитанная разница.  
 
То есть я выигрываю примерно 5 миллионов на хаотичных данных и проигрываю 65 тысяч на уже сортированных.
 
Добавлено:
Дело в том что я ипользую сортировку для создания индексов. Эта операция происходит единожды. На индексы созданные сразу когда таблица пустая это не влияет. То есть примарикей на практике не должен попадать в плохой случай. Но другие данные чаще всего не отсортированны и тут я получу реальную выгоду. На маленьких количествах данных отличия не существенны. Но как в моём случае - более 3х миллионов, то 15 секунд мне кажутся важны.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 09:38 21-04-2013 | Исправлено: delover, 12:54 21-04-2013
adasiko



Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
delover

Цитата:
Да стандартную, у меня нестандартный код выделен комментарием

Не, я то имел ввиду стандартную библиотечную функцию. Не очень знаток дельфей и паскалей, но у типа данных список (List) наверняка есть сортировка или просто есть общая сортировка. Интересно как там наоптимизировали, а вдруг лучше будет
PS: в С вот такая есть http://www.cplusplus.com/reference/cstdlib/qsort/

Всего записей: 1807 | Зарегистр. 30-06-2008 | Отправлено: 11:29 21-04-2013 | Исправлено: adasiko, 11:33 21-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
Не очень знаток дельфей и паскалей, но у типа данных список (List) наверняка есть сортировка или просто есть общая сортировка

Да есть. Я брал стандартную сортировку которая используется везде в Delphi. Замечу что аналогичная и в СиБилдере. Выделенный кусок я вставил после вычисления медианы. В своём приложении вы можете поступить так же, правда выделенный кусок вам надо будет немного причесать ручками под свой синтаксис.  
 

Цитата:
Интересно как там наоптимизировали, а вдруг лучше будет

Сомневаюсь. Везде обычно используется стандартный метод. Улучшение методом сравнения 3х элементов придумал не я. Можете прочитать в Вики
http://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0
 
Добавлено:
adasiko
Я решил на всякий случай расписать каждую строчку. Я понимаю - синтаксис бывает непривычен. Вам следует найти операцию вычисления медианы - опорного элемента. Берутся границы сортируемого участка и делятся на 2.
 
    P := (L + R) shr 1;  
 
Инструкция SHR это и есть деление на 2 - то есть это битовый сдвиг в право. Далее мой код.
 
    T := (P - I) shr 2;  
 
Я делю растояние до середины видимо на 4.
 
    if T > 16 then  
    begin  
 
Тут я уменьшаю воздействие алгоритма на участке меньше 16и элементов. Так как мне всё равно обязательно проверить T. Это обязательно чтобы я не сравнивал T=0 и T=1. Но для сортированных уже данных это приносит большие плоды.
 
      if SCompare(SortList[P - T], SortList[P]) < 0 then  
      begin  
        if SCompare(SortList[P], SortList[P + T]) > 0 then  
          Inc(P, T);  
 
Сравнение с левым значением и если всё стоит правильно то сравнение с правым элементом. И если правый меньше то инструкция inc - это увеличение P=P+T на Си. Для Delphi inc компилируется и оптимизируется лучше.
 
      end else  
      begin  
        Dec(P, T);  
        if SCompare(SortList[P], SortList[P + (T shl 1)]) > 0 then  
          Inc(P, T shl 1); //<-- Тут была ошибка увеличивал в 4 раза.
      end;  
    end;  
 
Сравнение 2,1,3. Медиана уже не правильно, и я уменшаю P инструкцией Dec. Далее если всё же 2 > 3 то увеличиваю T в 2 раза (была ошибка увеличивал в 4).  
 
Я предупреждал - сильно не бейте. Придётся заного тестировать, так результаты могут быть другими.
 
Добавлено:
Протестил исправление
Теперь для хаотичных данных (объем данных тот же, только строки задом наперед)  
84100202 - сравнений с моим алгоритмом  
89129019 - сравнений стандартного алгоритма  
-5028817 - посчитанная разница.  

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 11:59 21-04-2013
LadyOfWood

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
delover
Проблема в том что мало кому надо сортировать милионны строк, а вот на небольших массивах разница может не быть или будет слабо заметна.

Всего записей: 620 | Зарегистр. 16-09-2003 | Отправлено: 19:23 21-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
LadyOfWood
Да Вы правы, это слабое улучшение, но оно сказывается так же и на использование стека. 1)Если Ваше приложение серверное или имеет тип служба и предполагает потоковые обработки, то использование стека может быть весьма существенным.
2)Такие среды разработки как Сишарп (интерпретируемые) сильно уступают в скорости сортировки, для них улучшение весьма существенное.
3)В моём случае существует Сишарп служба которая тратит на один из своих шагов 6 часов. Там цикл внутри которого работа с 400 тысячами наименований продуктов поступивших от точек. Создание индекса занимает на Delphi 2 минуты для трёх миллионов. Значит вместо службы я создам локальный индекс за 40 секунд и проделаю работу за 3 минуты. 3 минуты против 6 часов Сишарпа.
 
Я тут выяснил, что для обратной сортировки - случай 3 2 1, выбирается медианой 1. В хаотичных данных это иногда верно иногда не верно, по этому не особо заметно. Однако в обратно сортированных данных лучше возвращать медиану на место. В этом случае я бы даже предусмотрел счётчик, который либо обнуляется если мы не возвращали медиану либо накапливается. При набранном пороговом значении можно отключать выбор медианы так как огромный шанс, что мы включили обратную сортировку. Это так же актуально в локальных гридинах. Однако меня интересует именно элементарное улучшение алгоритма и глобальный счётчик мне кажется избыточен. Если 5 миллионов это 15 секунд выигрыша, то 65 тысяч это примерно 150 миллисекунд. По отношению к двум минутам меня более интересуют 15 секунд, так как если я ждал 2 минуты - то я не замечу 150 миллисекунд.
 
Добавлено:
Улучшение 2.

Код:
T := (P - I) shr 2;
    if T > 16 then
    begin
      if SCompare(SortList[P - T], SortList[P]) < 0 then
      begin
        if SCompare(SortList[P], SortList[P + T]) > 0 then
          Inc(P, T);
      end else
      begin
        Dec(P, T);
        if SCompare(SortList[P], SortList[P + (T shl 1)]) > 0 then
        begin
          Inc(P, T);
          if SCompare(SortList[P], SortList[P + T]) < 0 then //<-- третье сравнение
            Inc(P, T);
        end;
      end;
    end;

Оно принесло уже выигрыш 7 миллионов - 7478839. А так же облегчило обратную сортировку.
 
Добавлено:
LadyOfWood

Цитата:
Проблема в том что мало кому надо сортировать миллионны строк, а вот на небольших массивах разница может не быть или будет слабо заметна.

В большинстве случаев в гридинах не более 5000 записей и на этих данных моё улучшение просто безобидно. Но в тех случаях когда Вы начинаете сортировать по 60 тысяч, Вы постоянно выигрываете миллисекунды на хороших компах - как у меня, а на плохих ещё больше выигрыш.
 
Добавлено:
Изменений в глубине используемого стека я не заметил. Странно но 1320 байт в обоих случаях - мизер какой-то. Да и ещё странно - дома комп новый процессор + 64 система + 8Гб памяти - сортирует 3 с половиной минуты. На работе комп старый + 4гб + 32 система  сортирует 2 с половиной минуты. 64 ещё сыровата для сортировки строк.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 08:58 22-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору


При сортировке 60 тысяч с завидным постоянством ускорение в 100 миллисекунд. То есть общее время в моём случае 1 секунда 310 мс. Стандартный 1 сек 450 мс. На сколько знаю - человеческий глаз за это время замечает 3 кадра. То есть ускорение 60 тысяч заметно на глаз...

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 11:50 22-04-2013
LadyOfWood

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
Значит вместо службы я создам локальный индекс за 40 секунд и проделаю работу за 3 минуты. 3 минуты против 6 часов Сишарпа.  

Что мне кажется тут проблема в коде, а не в языке.

Всего записей: 620 | Зарегистр. 16-09-2003 | Отправлено: 15:14 22-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
LadyOfWood

Цитата:
Что мне кажется тут проблема в коде, а не в языке.

Я писал в другом топике. Я оптимизировал тот фрагмент на ничего не меняющий - выполняется меньше 2х часов. А я хочу без оптимизации - делать то же самое - интересно на сколько быстрее. Но средствами Сишарп+System.List этого никак не сделать. -Минимум 2 часа против моих 3х минут.
 
Добавлено:
LadyOfWood
Предлагаю удалить все индексы в Вашей базе и насладиться результатом. List и LinQ безиндексные. Но программист Сишарп знает LinQ и его не волнует что он делает 6 цыклов вместо одного. )))
 
Добавлено:
Решил запостить что я намеревался оптимизировать -  
 
Приведу пример, того что писал спесиалист С#
Подробнее...
В переменной list у него более 400 тысяч элементов. Далее идёт точно такой же блок.
Так как чтение не индексное - list.Count в цикле пробегает все 400 тысяч. В результате служба выполняла блок около 6-и часов (commName тоже большой).
 
Я написал программу EndWithIndex. Она делает за 2 минуты индекс (3 миллиона - я решил с запасом) на строки задом наперёд. Естественно индексная отборка занимает миллисекунды. Замерьте EndWith на списке более 400 тысяч в сишарпе.
 
Пока ещё нет чёткого определения что Сишарп не подходит для таких обработок, если вам не плевать на серверную машину на которой крутится сайт и все базы.
 
Добавлено:

Цитата:
На сколько знаю - человеческий глаз за это время замечает 3 кадра.

Тут я просто прибавил 25-ый кадр. 1000/24 = 41, я привык брать в ум 33 миллисекунды.
 
Добавлено:
Я всё ещё нуждаюсь в поддержке, а так же рекоммендую русскую Википедия.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 19:52 22-04-2013 | Исправлено: delover, 20:11 22-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору


Когда речь о 7 миллионов извинте я пас. Кто изобрёл сравнение 3х элементов - я не узнал, не знаю кому обязан, но моя честь.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 22:24 22-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Да жаль что самому приходится всё тестировать. Обнаружил ещё неправильный случай - 2,3,1. Последний вариант:

Код:
    T := (P - I) shr 2;
    if T > 16 then
    begin
      if SCompare(SortList[P - T], SortList[P]) < 0 then
      begin
        if SCompare(SortList[P], SortList[P + T]) > 0 then
        begin
          if SCompare(SortList[P - T], SortList[P + T]) < 0 then //тут тоже третье
            Inc(P, T)
          else
            Dec(P, T);
        end;
      end else
      begin
        Dec(P, T);
        if SCompare(SortList[P], SortList[P + (T shl 1)]) > 0 then
        begin
          Inc(P, T);
          if SCompare(SortList[P], SortList[P + T]) < 0 then  
            Inc(P, T);
        end;
      end;
    end;

 
Теперь в моём случае -9 миллионов сравнений. Это уже экономия 30 секунд.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 07:49 25-04-2013
data man



Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Ещё на тему изобретения велосипедов О QuickSort не говори

----------
Любой достаточно развитый тролль неотличим от подлинно помешанного на какой-либо идее.
Кекс. Антибиотики. Ламбада.

Всего записей: 1696 | Зарегистр. 13-10-2005 | Отправлено: 16:33 25-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
data man
И так ещё раз. Моя задача найти единый способ ускорить 32 64 и .NET. Первое условие - это просто должно быть. Второе условие - мало влияет на сортированные данные.
 
 
 

 
 
 
 
 
 
data man
Спасибо. Но -

Цитата:
Ещё на тему изобретения велосипедов  

Велосипедов не было. Во первых - статья старая. Автор толкает ассемблер который с опцией оптимизации мне ничего не даёт. Таких моментов я нашел минимум 4. Во вторых - сравнение трёх - это не научно. Я 3 раза исправлял свой блок. Количество сравнений всегда уменьшалось, но скорость во всех четырёх случаях возрастала только на 15 секунд. Это значит что среднее значение не панацея. Скорость после этого замедляется за счёт количества перемещений. В третих после сравнения трёх он пледлагает сразу их менять местами - мои тесты показали - это замедляет алгоритм. Ну и в четвёртых - читайте иногда название темы - элементарное ускорение Quicksort.  
 
 if (List<>nil) and (Count>1) then begin; - тут я ржал рекурсивно, он позволил начать сортировку List = nil. Идиотская проверка.
 
В пятых я привожу код 100% совместимый с 64 разряда версией - его алгоритмы не совместимы с 64 разряда. В шестых использование интерсорт только замедлило на моих первых тестах - то есть до того как я пришол к сравнению трёх. В седьмых его сортировка не учитывает тот факт что более затратная операция у меня - это сравнение, так как оно используется для уникальных индексов по трём четырём полям.
 

Цитата:
Ещё на тему изобретения велосипедов  

Спасибо статья действительно интересная, но! Во первых меня интересует именно элементарное ускорение. У меня есть базы которым больше пяти лет и создание нового индекса бывает затратно. Во вторых сначала протестируйте кто сортирует быстрее, а потом уже великами хвастайтесь.)
 
Добавлено:
data man
Да и пока я не нашел по теме "элементарное ускорение" ничего. Я буду изучать статью дополнительно, надеюсь ещё что ни-будь выцедить для своей темы
 
Добавлено:
data man
 Да и забыл про свою изюминку. В выборе трёх я выбираю  элементы с разным расстоянием от середины - ни в коем случае не крайние. За это я плачу одной ассемблерной инструкцией. У меня как бы конус над медианой который сужается при сужении расстояний. И ограничение 16 так же ускоряет мой алгоритм для уже отсортированных в любом порядке данных. Так что без серьёзного сравнения я даже не берусь утверждать что смогу достигнуть ускорения хотя бы 100 миллисекунд на объёме 3 миллиона. Выгодного выигрыша не вижу.
 
Добавлено:
В десятых они пишут про помещение в стек списка - я компилировал с опцией оптимизации - ничего она в стек не помещает - статья устарела морально и физически. Да и мои замеры глубины стека - 1200 байт для трёх миллионов. - Ниочём.
 
Добавлено:
Да и ещё - я даю возможность задавать свою функцию сравнения пользователям. Как правило - у новичков эта функция может работать в 4 раза медленнее - чем у профессионала, от этого интерес только к функциям сравнения - перемещения я реализую сам.
 
Добавлено:
data man
В одинадцатых. Какое отношение статья имеет к .NET? Там абсолютно другие критерии. Извините - велосипед то старый, на таких уже не ездят.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 18:29 25-04-2013 | Исправлено: delover, 21:22 25-04-2013
delover

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
но скорость во всех четырёх случаях возрастала только на 15 секунд

Не совсем верно, очевидно я тестировал на разных машинах, сейчас тестирую на одной. Я подверг сомнению:
T := (P - I) shr 2;  
if T > 16 then  
Действительно shr 1 принёс выигрыш в 4 секунды на хаотичных данных, но принёс проигрыш на сортированных данных как положено в 2 раза. Если мы сделаем T>8 то ещё выиграем секунду на хаотичных. А на сортированных потеряем ещё в 2 раза - то есть уже теряем 4 секунды. Для создания индексов можно использовать shr 1, но я сделал выбор в пользу единого алгоритма. У пользователей сортировки Grid часто случается обратная сортировка по этому я не усложняю сортировку упорядоченных данных. В общем случае -
T := (P - I) shr 2;  
if T > 16 then  
нельзя оптимизировать.

Всего записей: 1395 | Зарегистр. 25-06-2007 | Отправлено: 15:29 02-05-2013
Открыть новую тему     Написать ответ в эту тему

Компьютерный форум Ru.Board » Компьютеры » Прикладное программирование » Элементарное улучшение QuickSort


Реклама на форуме Ru.Board.

Powered by Ikonboard "v2.1.7b" © 2000 Ikonboard.com
Modified by Ru.B0ard
© Ru.B0ard 2000-2024

BitCoin: 1NGG1chHtUvrtEqjeerQCKDMUi6S6CG4iC

Рейтинг.ru