С
CleverSort
CleverSort - це найсучасніший (тобто надмірно сконструйований та недооптимальний) двоступеневий алгоритм сортування рядків.
На кроці 1 він починається попередньою сортуванням вхідних рядків за допомогою радіоскопічного сортування та перших двох байтів кожного рядка. Сорт Radix є порівняльним і дуже добре працює для рядків.
На кроці 2 використовується сортування вставок у попередньо відсортованому списку рядків. Оскільки список майже відсортований після кроку 1, сортування вставки є досить ефективним для цього завдання.
Код
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Convert first two bytes of Nth line into integer
#define FIRSTSHORT(N) *((uint16_t *) input[N])
int main()
{
char **input = 0, **output, *ptemp;
int first_index[65536], i, j, lines = 0, occurrences[65536];
size_t temp;
// Read lines from STDIN
while(1)
{
if(lines % 1000 == 0)
input = realloc(input, 1000 * (lines / 1000 + 1) * sizeof(char*));
if(getline(&input[lines], &temp, stdin) != -1)
lines++;
else
break;
}
output = malloc(lines * sizeof(char*));
// Radix sort
memset(occurrences, 0, 65536 * sizeof(int));
for(i = 0; i < lines; i++) occurrences[FIRSTSHORT(i)]++;
first_index[0] = 0;
for(i = 0; i < 65536 - 1; i++)
first_index[i + 1] = first_index[i] + occurrences[i];
memset(occurrences, 0, 65536 * sizeof(int));
for(i = 0; i < lines; i++)
{
temp = FIRSTSHORT(i), output[first_index[temp] + occurrences[temp]++] = input[i];
}
// Insertion sort
for(i = 1; i < lines; i++)
{
j = i;
while(j > 0 && strcmp(output[j - 1], output[j]) > 0)
ptemp = output[j - 1], output[j - 1] = output[j], output[j] = ptemp, j--;
}
// Write sorted lines to STDOUT
for(i = 0; i < lines; i++)
printf("%s", output[i]);
}
Платформи
Усі ми знаємо, що машини з великим ендіаном набагато ефективніші, ніж їхні маломісткі колеги. Для порівняльного аналізу ми складемо CleverSort з увімкненими оптимізаціями та створимо випадковим чином величезний список (трохи більше 100 000 рядків) 4-байтових рядків:
$ gcc -o cleversort -Ofast cleversort.c
$ head -c 300000 /dev/zero | openssl enc -aes-256-cbc -k '' | base64 -w 4 > input
$ wc -l input
100011 input
Еталон великого рівня
$ time ./cleversort < input > /dev/null
real 0m0.185s
user 0m0.181s
sys 0m0.003s
Не надто пошарпаний.
Мало-ендіанський візит
$ time ./cleversort < input > /dev/null
real 0m27.598s
user 0m27.559s
sys 0m0.003s
Бу, маленький Ендіан! Бу!
Опис
Сортування вставки насправді досить ефективне для майже відсортованих списків, але жахливо неефективне для випадково відсортованих.
Нижня частина CleverSort - це макрос FIRSTSHORT :
#define FIRSTSHORT(N) *((uint16_t *) input[N])
На машинах з великим ендіаном впорядкування рядка з двох 8-бітних цілих чисел лексикографічно або перетворення їх у 16-бітні цілі числа та впорядкування їх після цього дає ті ж результати.
Звичайно, це можливо і на малоінтенсивних машинах, але макрос повинен був бути
#define FIRSTSHORT(N) (input[N][0] | (input[N][1] >> 8))
яка працює, як очікувалося, на всіх платформах.
Наведений вище показник "великого ендіану" насправді є результатом використання відповідного макросу.
З неправильним макросом і малоінтенсивним машиною список попередньо сортується за другим символом кожного рядка, в результаті чого відбувається випадкове впорядкування з лексикографічної точки зору. Різновиди введення в цьому випадку ведуть себе дуже погано.