
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.21] |
![]() |
|
![]() |
|
|
Всем программистам привет! Respect!
Помогите разобраться с алгоритмом (две реализации уже написал, но в обоих случаях не укладываюсь в отведенный лимит 1 секунды времени работы). Условие: с клавиатуры вводится до 500000 строк (строка состоит ТОЛЬКО из латинских букв - большие и малые). Общая длина символов во всех строках не должна превышать 10 миллионов!! Нужно отсортировать строки в лексикографическом порядке по правилу: заглавная буква БОЛЬШЕ малой, если буквы одного регистра по та буква меньше, которая идет раньше по алфавиту (стандарт). Также: "Ivanov" < "Ivanova" (стандарт). Время ограничения работы программы 1 секунда! реализовал через динамический массив + qsort получил что на 10000 строк и длиной в 7000 символов работает около 5 - 6 секунд, а ведь предельно допустимое значение могут быть в ТЫСЯЧИ раз больше!..Пробовал через ЛОС, тоже по лимиту не проходит серьезно... подскажите оптимальный алгоритм для обработки сортировки таких строк.... подскажите как быть то??...буду очень признателен... ![]() N.B. реализую на С, VS 2010, console application... |
Сообщ.
#2
,
|
|
|
Могу предложить в процессе считывания входных строк строить бинарное дерево. Таким образом, решением будет трасса его обхода Лево-Центр-Право.
|
Сообщ.
#3
,
|
|
|
Цитата Grab[SSAU] @ строить бинарное дерево. Таким образом, решением будет трасса его обхода Лево-Центр-Право. именно двоичное (бинарное) дерево (не avl)? Корень дерева будет ПЕРВАЯ введенная строка??... Добавлено еще хочется до конца понять что в подобных задачах считается временем работы программы!...т к ввод с клавиатуры может занимать +бесконечность времени, вывод на экран отсортированных строк - безумно долгая операция, поэтому может длиться недели по сути... кстати, обход (просто обход без печати даже) разве уложиться в 1 секунду?... Добавлено т е обход бинарного дерева (прыгая чисто указателями) из 500 000 узлов пройдет за 1 секунду?...если нет, тогда вообще непонятно, что считать за 1 секунду работы... |
Сообщ.
#4
,
|
|
|
FasterHarder, секундочку, я не успеваю читать
![]() Цитата FasterHarder @ именно двоичное (бинарное) дерево (не avl)? Корень дерева будет ПЕРВАЯ введенная строка??... Да, бинарная. Предлагаю начать с него, потому что оно попроще АВЛ или красно-чёрного. Да, корнем будет первая введённая строка. Цитата FasterHarder @ еще хочется до конца понять что в подобных задачах считается временем работы программы!...т к ввод с клавиатуры может занимать +бесконечность времени, вывод на экран отсортированных строк - безумно долгая операция, поэтому может длиться недели по сути... кстати, обход (просто обход без печати даже) разве уложиться в 1 секунду?... Вас проверяет автоматизированная система. "Ввод с клавиатуры" там максимально быстр, равно как и "вывод на экран". Как она замеряет время мне неведомо, но подозреваю, что по-честному ![]() Уложится ли обход в секунду не знаю, предполагаю, что да. Ведь это всего лишь рекурсия с глубиной не более log2(500 000) < 19 вызовов. |
Сообщ.
#5
,
|
|
|
Цитата Grab[SSAU] @ Уложится ли обход в секунду не знаю, предполагаю, что да. Ведь это всего лишь рекурсия с глубиной не более log2(500 000) < 19 вызовов. при некоторых входных данных (каждое слово меньше предыдущего) би-дерево вырождается в список!..., т е будет рекурсия в 500 000 - 1 вложенности, правда на сколько это на скорость повлияет не знаю... в общем я тоже думал про деревья, но думал что через списки 100% отработает... в общем нужно проверить... |
Сообщ.
#6
,
|
|
|
Цитата FasterHarder @ при некоторых входных данных (каждое слово меньше предыдущего) би-дерево вырождается в список!..., т е будет рекурсия в 500 000 - 1 вложенности, вы правы. я совершенно забыл про такие подлянки. нужно порешать тимус что ли... |
![]() |
|
|
в общем реализовал следующее: поскольку не важно ЧТО хранится в узлах дерева, составил би-дерево из целых чисел. Ведь важна скорость ОБХОДА дерева а информационное поле не влияет на скорость обхода!...
заполнял случайными числами от -100 до +100, заполнялось минуты 3, а скорость обхода показала 0 секунд (что радует безусловно)...НО когда стал тестировать граничный вариант (заполнял только фиксированное информационное поле = 1), то программа вылетает при количестве узлов 4700, а при 4699 работает корректно, следовательно максимальное количество вложенности рекурсии 4699???.... понятно почему при 50000 узлов отработало корректно, потому что числа были равномерно распределены на диапазоне от -100 до 100 и дерево "росло" одинаково по всем направлениям.... можно как-нибудь обойти проблему с добавлением одинаковых узлов??? N.B. Реализация алгоритма через би-дерево: ![]() ![]() #include "stdafx.h" #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <time.h> typedef struct Telem { int inf; struct Telem *left; struct Telem *rigth; } Telem; // добавление узла в дерево (если значение РАВНО узел "уходит" в правую часть) Telem * addNode(Telem *proot, int pinf) { if(!proot) { proot = (Telem *)malloc(sizeof(Telem)); if(!proot) { printf("\n\nError memory!"); getch(); exit(1); } proot->inf = pinf; proot->left = proot->rigth = NULL; } else { if(proot->inf > pinf) proot->left = addNode(proot->left, pinf); else proot->rigth = addNode(proot->rigth, pinf); } return proot; } // обход дерева слева направо: левый узел - корень - правый узел void lkr(Telem * proot) { if(proot) { lkr(proot->left); //printf("%d ", proot->inf); lkr(proot->rigth); } } // удаление дерева из памяти Telem* delTree(Telem *p) { if(p) { if(p->left != NULL) p->left = delTree(p->left); if(p->rigth != NULL) p->rigth = delTree(p->rigth); free(p); } return NULL; } int main() { Telem *root = NULL; long i, n, x; int start, end; printf("Vvedite kolichestvo elementov: "); scanf("%d", &n); printf("\n\nVhodnie znacheniya: "); for(i = 0; i < n; i++) { x = 1; // rand() % 201 - 100; // [-100 ... 100] //printf("%d ", x); root = addNode(root, x); } printf("\n\nDerevo imeet vid: "); // засекаем время исключительно обхода без печати start = time(NULL); lkr(root); end = time(NULL); printf("\n\nVremya obhoda (sek): %d", (end - start)); getch(); delTree(root); return 0; } |
Сообщ.
#8
,
|
|
|
FasterHarder, я ошибся с деревом, забыв про граничные случаи
![]() |
Сообщ.
#9
,
|
|
|
Цитата Grab[SSAU] @ А этой задачи в открытом доступе нет? у меня на бумажке написана...наверное в public-доступе нет... я вроде условие четко сформулировал, все ясно...если что непонятно могу конечно пояснить... N.B. может hash - таблицу ввести, а ключом будет первая буква, правда опять таки при идентичных строках вырождается в ЛОС...-> full scan... Добавлено еще очень хочется понять следующее: если имеется набор таких НЕ отсортированных строк в количестве 500 000 штук, то даже теоретически НЕ успеть их отсортировать за 1 - 2 секунды любым существующим способом сортировки?? если да, тогда однозначно нужно сортировать при вводе сразу и в этом случае скорость будет зависит ТОЛЬКО от просмотра всех элементов структуры данных.... вообще, простой цикл от 1 до 500 000 работает точно быстрее 1 сек.... |
Сообщ.
#10
,
|
|
|
А где это будет выполняться?
У меня стабильно выдаёт 800+-50: ![]() ![]() #include "stdafx.h" #include <vector> #include <algorithm> #include <stdlib.h> #include <iostream> #include <time.h> struct lpstr_less { bool operator ()(const TCHAR *_left, const TCHAR *_right) { return (_tcscmp( _left, _right ) < 0); } }; void main() { std::vector<TCHAR*> v(500000); std::vector<TCHAR*>::iterator i = v.begin(); for ( ; i != v.end(); i++ ) { *i = new TCHAR[28]; _stprintf_s( *i, 28, _T("%d%d%d"), rand()*rand(), rand()*rand(), rand()); } long c; c = clock(); std::sort( v.begin(), v.end(), lpstr_less() ); std::cout << clock() - c; getchar(); } Естественно, std::sort придётся заменить на свою реализацию quicksort'а. Но факт: на некоторых машинах (на моей, например) вполне себе без дерева обходимся. Добавлено Вообще, это сильно зависит от контекста задачи: она на умение писать на чистом C быстрые алгоритмы, или на знание структур данных, или на знание STL, или ещё чего?.. |
Сообщ.
#11
,
|
|
|
я понял почему так долго работал мой вариант, еще через qsort, потому что тестировал на строках до 10 млн, а ведь по условию ОБЩАЯ длина всех строк не превышает 10 млн, т е если 500 000 строк, то их МАКС. длина = 20!...в этом случае программа работает 2 секунды...нужно малость оптимизировать сортировку, а особенно "сравнение символов"...
Добавлено Цитата ss @ или на знание STL ну это явно НЕ для СИ!... а задача совокупно на все...в этой задачи простые структуры данных, поэтому основное - написать оптимальный вариант сравнения строк при сортировке... |
Сообщ.
#12
,
|
|
|
![]() ![]() int __cdecl strcmp ( const char * src, const char * dst ) { int ret = 0 ; while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) ++src, ++dst; if ( ret < 0 ) ret = -1 ; else if ( ret > 0 ) ret = 1 ; return( ret ); } ![]() |
Сообщ.
#13
,
|
|
|
у меня нечто подобное:
![]() ![]() int compstr(char *ps1, char *ps2) { for( ; *ps1 == *ps2; ++ps1, ++ps2 ) if( *ps1 == 0 ) return 0; if(*ps1 >= 'a' && *ps2 <= 'Z') // ps2 > ps1 return 1; if(*ps1 <= 'Z' && *ps2 >= 'a') // *ps1 > *ps2 return -1; return *ps1 < *ps2 ? 1 : -1; } |
Сообщ.
#14
,
|
|
|
Поразрядная сортировка
Неприятно только, что заглавные буквы старше, но это обходится пред- и постобработкой |
Сообщ.
#15
,
|
|
|
Цитата FasterHarder @ если имеется набор таких НЕ отсортированных строк в количестве 500 000 штук, то даже теоретически НЕ успеть их отсортировать за 1 - 2 секунды любым существующим способом сортировки?? Совершенно верно - отсортировать такой объем данных невозможно в принципе. Но решить задачу можно! Как? Да не сортировать ничего, надо завести суффиксное дерево (в правильности термина не уверен), пихать в него то, что вводится, а потом тупо вывести то, что в него окажется запихнутым в лексигрофическом порядке. Вычислительная стоимость такого решения будет порядка стоимости ввода-вывода из файла/в файл. Цитата FasterHarder @ ввод с клавиатуры может занимать +бесконечность времени, вывод на экран отсортированных строк - безумно долгая операция, поэтому может длиться недели по сути... кстати Гонзалес, ты что, никогда не слышал про переназначение стандартных входных/выходных потоков консольных приложений на файлы? Открой любой букварь, там это где-то в начале есть. Да, собственно, на Тимусе объяснено, как организовать программу, чтобы отлаживать ее у себя на компе, со своими данными, и отправлять на проверку без внесения изменений. |
Сообщ.
#16
,
|
|
|
Цитата FasterHarder @ еще очень хочется понять следующее: если имеется набор таких НЕ отсортированных строк в количестве 500 000 штук, то даже теоретически НЕ успеть их отсортировать за 1 - 2 секунды любым существующим способом сортировки?? если да, тогда однозначно нужно сортировать при вводе сразу и в этом случае скорость будет зависит ТОЛЬКО от просмотра всех элементов структуры данных.... Ну если не рассматривать вырожденные\худшие варианты, то на современных компах можно и реально отсортировать список менее чем за 1 сек. Однако, то, что в условии говорится 1) именно о вводе с клавиатуры (несмотря на явно нереальное число строк для ручного ввода) и 2) об абсолютном времени в 1 сек безотносительно к компу - наталкивает на мысль, что скорее всего это подвох и сортировать\упорядочивать строки можно\нужно именно в процессе их ввода Цитата FasterHarder @ в этой задачи простые структуры данных, поэтому основное - написать оптимальный вариант сравнения строк при сортировке... Условие слишком мудреное, поэтому существенно\кардинально ускорить сравнение непосредственно самих строк вряд ли получится. Явное ускорение может дать пред\пост-обработка, например, замена регистра символов в самих строках, либо еще лучше использование хэшей, учитывающих эту замену. Например, можно прогнать расчет и быструю предсортировку по 4 или 8 байтному хэшу, а затем итоговую постсортировку участков с совпадающими хэшами (типа прямых вставок). PS: Ес-но подразумевается, что хэш расчитывается один раз и сохраняется в "простой структуре данных" в месте с указателем на строку. При этом достигается существенное ускорение как за счет упрощения сравнений на каждом шаге, так и за счет улучшения условий кэширования данных в процессоре (поскольку при сортировке обращение происходит только к массиву структур, а не к куче строк в 10 млн.символов) |
![]() |
|
|
Цитата leo @ наталкивает на мысль, что скорее всего это подвох и сортировать\упорядочивать строки можно\нужно именно в процессе их ввода безусловно, подобные мысли возникали!...Тогда логически появляется следующая диллема: должна ли ТОЛЬКО что введенная строка УСПЕВАТЬ позиционироваться в нужную позиции динамической структуры или нет за ОДНУ секунду...если НЕТ, тогда получается, что в такой программе НЕ будет фрагмента куда ПОТРАТИТЬСЯ отведенная секунда...если да, то даже full scan по ЛОС со всеми проверками должен успеть отработать за 1 сек. (у меня кстати, такая реализация есть, она не прошла ПО ЛИМИТУ в одну секунду).... P.S. Цитата leo @ , но ведь это приведет к их модификации и тогда каков в этом смысл...непонятно...замена регистра символов в самих строках, N.B. существующая программа работает около 2 секунд (миллисекунды не засекал)... Цитата FasterHarder @ максимальное количество вложенности рекурсии 4699???.... |
Сообщ.
#18
,
|
|
|
Цитата FasterHarder @ но ведь это приведет к их модификации и тогда каков в этом смысл...непонятно... Смысл в том, что эта модификация обратимая и производится всего 2 раза (1 до и 1 после сортировки), т.е. имеет сложность 2*N, что несущественно по сравнению со сложностью самой сортировки N*log(N) ~ 19*N при N=500тыс. Однако такая модификация должна упростить\ускорить каждое сравнение (особенно на современных камнях, тормозящих на хаотических условных переходах), и в итоге общая скорость может быть выше, чем при непосредственных хитроумных сравнениях |
Сообщ.
#19
,
|
|
|
Цитата leo @ производится всего 2 раза (1 до и 1 после сортировки) я в общем не понимаю смысл такой пре / пост обработки...например, есть 4 слова: 1) hello 2) heLlo of 3) Hello o 4) hellO of что будет происходить с каждой строкой по вашей задумке??... |
Сообщ.
#20
,
|
|
|
Цитата leo @ 1) именно о вводе с клавиатуры (несмотря на явно нереальное число строк для ручного ввода) leo, ты то чего тормозишь? Совершенно понятно, что ввод с клавиатуры - это отсебятина "переводчика", имеется в виду ввод со стандартного входного потока. Собственно запуск на тестирование будет осуществляться так: myprog.exe <data.in >data.out ну, естественно, из тестирующей программы через exec(...). Цитата leo @ 2) об абсолютном времени в 1 сек безотносительно к компу Это тоже отсебятина - задачка явно с какой либо системы автоматической онлайн проверки программ типа Тимуса, возможно именно с него - т.е. комп предполагается достаточно производительный. Цитата leo @ Явное ускорение может дать пред\пост-обработка, например, замена регистра символов в самих строках, Ни понял - сортировку по условию надо с учетом регистра осуществлять ![]() Добавлено позже: Тьфу ты блин, старость не радость, дошло. |
Сообщ.
#21
,
|
|
|
Короче, проверил на компе 4-летней давности Athlon 3500+ (2.2 ГГц).
500 000 строк длиной от 15 до 20 символов. Предобработка и постобработка - просто XOR символов с $60 (0x60) Предобработка + обыкновенный Quicksort + постобработка ~ 800-850 мс Предобработка + RadixSort MSD c обработкой малых кусков вставками, оптимизацией не занимался + постобработка - те же яйца |
Сообщ.
#22
,
|
|
|
Цитата FasterHarder @ что будет происходить с каждой строкой по вашей задумке??... Инверсия регистра, т.е. малые буквы станут прописными и наоборот - как при наборе текста при нажатой Caps Lock, например, "Hello" -> "hELLO". Цитата MBo @ Предобработка и постобработка - просто XOR символов с $60 (0x60) Почему 0x60, а не 0x20 ? А если строка содержит пробелы и знаки препинания ? В стандартных ASCII и Unicode кодировках малые и прописные латинские буквы различаются на 'a'-'A' = 'a'^'A' = 32 = 0x20, поэтому инверсию регистра можно делать операцией s[i] ^= 0x20. Разумеется, если строка кроме букв может содержать пробелы и знаки препинания, то инверсию нужно делать только для букв. |
Сообщ.
#23
,
|
|
|
Цитата leo @ Инверсия регистра, т.е. малые буквы станут прописными и наоборот - как при наборе текста при нажатой Caps Lock, например, "Hello" -> "hELLO". поясните плиз смысл подобного преобразования?? ну вот никак не могу понять, зачем менять регистр слов... Цитата leo @ А если строка содержит пробелы и знаки препинания ? строка по условию исключительно состоит из латинских букв и другие символы де-факто невозможны... Цитата MBo @ Предобработка + обыкновенный Quicksort + постобработка опять таки, какой выйгрыш дает пред / пост обработка?... |
Сообщ.
#24
,
|
|
|
Цитата MBo @ Короче, проверил на компе 4-летней давности Athlon 3500+ (2.2 ГГц). 500 000 строк длиной от 15 до 20 символов. Предобработка и постобработка - просто XOR символов с $60 (0x60) Предобработка + обыкновенный Quicksort + постобработка ~ 800-850 мс Т.е. получается, что задача тупо на инвертирование? Мдя, на всякого мудреца довольно простоты ©. |
Сообщ.
#25
,
|
|
|
Цитата FasterHarder @ поясните плиз смысл подобного преобразования?? ну вот никак не могу понять, зачем менять регистр слов... Для того, чтобы при сортировке использовать более простую и соотв-но более быструю функцию сравнения типа strcmp вместо твоей "навороченной" compstr из #13 с кучей условий. PS: Инверсия регистра как раз удовлетворяет условию сравнения #1, т.к. 1) заглавные и малые меняются местами, 2) соотношение между буквами одного регистра не изменяется, т.к. сохраняется их алфавитный порядок Добавлено Цитата Bug Hunter @ Т.е. получается, что задача тупо на инвертирование? Если вместо инверсии самих строк использовать int хэш (те же 4 первых символа строки с инверсией регистра и порядка следования), то должно быть еще раза в 1.5-2 быстрее (если конечно не брать тупой случай, когда у всех строк первые 4 символа одинаковые) |
Сообщ.
#26
,
|
|
|
leo, все, теперь понял, пасибо...проверю как это повысит производительность, правда буду использовать не 0X60, а функции поднимающие в верхний / нижний регистр...
Добавлено Цитата leo @ если конечно не брать тупой случай, когда у всех строк первые 4 символа одинаковые скорость работы также тестируется на варианте, когда максимальное количество строк и все строки равны, т е НЕ только первые 4 символа, а абсолютно все...Если тест НЕ пройдет, считается программа не прошла по лимиту... |
Сообщ.
#27
,
|
|
|
Цитата leo @ чтобы при сортировке использовать более простую и соотв-но более быструю функцию сравнения типа strcmp вместо твоей "навороченной" compstr из #13 с кучей условий. А можно оптимизировать compstr как-то так: ![]() ![]() int compstr(char *ps1, char *ps2) { for( ; *ps1 == *ps2; ++ps1, ++ps2 ) if( *ps1 == 0 ) return 0; return ((*ps1)^0x20) < ((*ps2)^0x20) ? -1 : 1; } - тогда предобработка+постобработка не понадобятся. |
Сообщ.
#28
,
|
|
|
Цитата leo @ Если вместо инверсии самих строк использовать int хэш (те же 4 первых символа строки с инверсией регистра и порядка следования), то должно быть еще раза в 1.5-2 быстрее (если конечно не брать тупой случай, когда у всех строк первые 4 символа одинаковые) При равномерном распределении большинство строк (51/52) будут различаться по первому символу и подавляющее большинство (99.963%) - по первому или второму, так что дополнительные изыски тут в среднем вряд ли помогут, а вот в этом предельном плохом случае: Цитата FasterHarder @ скорость работы также тестируется на варианте, когда максимальное количество строк и все строки равны, т е НЕ только первые 4 символа, а абсолютно все... дополнительные действия будут явно мешать. Кста, в этом предельном случае стандартный qsort должен тупо вывалиться по переполнению стэка! Возможно, задача еще и на это... ![]() |
Сообщ.
#29
,
|
|
|
Цитата Bug Hunter @ вывалиться по переполнению стэка! да, так и есть на самом деле!... Добавлено да, и еще, поскольку сортировка ПОСЛЕ ввода на граничных случаях НЕ проходит, то наталкивает на мысль, что сортировать нужно СРАЗУ при вводе!... вижу только такой вариант: ввели строку, бинарным поиском определили позицию вставки, после этого сместили строки вправо на одну позицию (правда, насколько тяжелая операция может быть), в освобожденное место вставили вводимую строку... конечно, бинарный поиск будет работать почти мгновенно: 2^x >= 500 000 (уже говорили, что 19 вроде), а вот сдвиг...максимальный сдвиг будет в том случае, когда добавляется ПОСЛЕДНЯЯ строка и она попадает в первую позицию, в этом случае 499 999 строк, смещаются вправо на одну позицию..если эта операция отработает быстрее 1 сек. то задача думаю будет решена точно... |
Сообщ.
#30
,
|
|
|
Цитата Bug Hunter @ можно оптимизировать compstr как-то так: Sorry, напортачил, не послушавшись совета от leo (использовать xor 0x20, а не 0x60), исправил, смотрите исправленный вариант (#27). |
Сообщ.
#31
,
|
|
|
Цитата FasterHarder @ да, так и есть на самом деле!... да, и еще, поскольку сортировка ПОСЛЕ ввода на граничных случаях НЕ проходит, то наталкивает на мысль, что сортировать нужно СРАЗУ при вводе!... вижу только такой вариант: ввели строку, бинарным поиском определили позицию вставки, после этого сместили строки вправо на одну позицию (правда, насколько тяжелая операция может быть), в освобожденное место вставили вводимую строку... То, что ублюдочная реализация qsort ввиду поголовного обкура разработчиков систем разработки программ на основе "самого лучшего (и постоянного улучшающегося!) языка программирования всех стран и народов на все времена" 30 лет кочует от версии к версии, еще не повод отказаться от быстрого алгоритма - надо просто спросить у Яндекса, как зарамсить проблемму и написать для себя устойчивую реализацию. Но если хочется помучиться... Цитата FasterHarder @ конечно, бинарный поиск будет работать почти мгновенно: 2^x >= 500 000 (уже говорили, что 19 вроде), а вот сдвиг... Можно заполнять массив указателей с хвоста и сдвигать элементы вреред - тогда можно воспользоваться memcpy или как его там - это должно быть быстро. Было бы любопытно сравнить с qsort. |
Сообщ.
#32
,
|
|
|
Цитата Bug Hunter @ При равномерном распределении большинство строк (51/52) будут различаться по первому символу и подавляющее большинство (99.963%) - по первому или второму, так что дополнительные изыски тут в среднем вряд ли помогут Еще как помогут, и в #18 я уже объяснил почему. Сделав один единственный проход по массиву для вычисления int-хэша (что кстати можно сделать и одновременно со считыванием строк) затем при сортировке все сравнение сводится к элементарной inline-операции сравнения двух int. А твоя compstr из #27, даже если и заинлайнится, то потребует кучу непроизводительных операций по сохранению, инициализации и восстановлению регистров, не говоря уже об условных переходах, а если не заинлайнится, то плюс нехилые затраты на вызов и возврат из функции. И все это прямые потери на каждом из ~N*log(N) сравнений (в лучшем случае). Плюс к этому значительно (если не сказать кардинально) улучшаются условия кэширования данных в процессоре, т.к. int-хэш сохраняется в одной 8-байтовой структуре вместе с указателем на строку и соотв-но фактически сортируется сам массив, содержащий эти хэши (без обращения к отдельно лежащим строкам). Во-первых, этот массив по размеру в 2-3 раза меньше чем сами строки, во-вторых, что более существенно, за счет сортировки самого массива на каждой итерации qsort'а идет последовательное обращение только к двум участкам памяти (концам текущего диапазона сортировки) и при этом современные процессоры прекрасно справляются с предсказанием и упреждающей подгрузкой данных из ОЗУ в кэш (hardware prefetch). А если сравнивать не хэши, а отдельно лежащие строки, то только на первой итерации доступ к этим строкам может быть квазипоследовательным, а на последующих итерациях он все более становится случайным (ес-но предполагаентся, что сортируется массив указателей, а сами строки остаются на своих местах). А в современных компах случайный доступ к ОЗУ на один-два порядка (!) медленнее последовательного - вот и считай "вряд ли помогут" или нет. Ну, а что касается пост-сортировки, то исходя из твоих же предположений о различиях строк, простейшая повторная сортировка вставками должна отработать за O(N), что также значительно быстрее сортировки "в лоб" |
Сообщ.
#33
,
|
|
|
Цитата leo @ Еще как помогут, и в #18 я уже объяснил почему. У тебя там небольшая неточность - имперически установлено, что кол-во сравнений в быстрой сортировке для хорошо перемешанных данных сост. ~2NLnN, т.е. 26N для 500 000, т.е. 26 сравнений на строку. Да, заморочится с int-кэш явно стоит, причем ввиду того, что большинство строк расходится по первым символам, доколачивать прикладом после сортировки хэшей всего ничего останется. Все, полностью понял твою мысль. ![]() |