C
CleverSort
CleverSort เป็นอัลกอริธึมการเรียงสตริงแบบสองขั้นตอนที่ทันสมัย
ในขั้นตอนที่ 1 จะเริ่มต้นด้วยการเรียงลำดับบรรทัดอินพุตล่วงหน้าโดยใช้การเรียง Radixและสองไบต์แรกของแต่ละบรรทัด การเรียงลำดับ 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
ไม่โทรมเกินไป
เครื่องหมายเล็ก ๆ น้อย ๆ ของ endian
$ time ./cleversort < input > /dev/null
real 0m27.598s
user 0m27.559s
sys 0m0.003s
Boo, Endian น้อย! Boo!
ลักษณะ
การเรียงลำดับการแทรกนั้นค่อนข้างมีประสิทธิภาพสำหรับรายการที่เกือบจะเรียงลำดับแล้ว แต่มันไม่มีประสิทธิภาพมากนักสำหรับการเรียงลำดับแบบสุ่ม
ส่วนที่ underhanded ของ CleverSort คือมาโครFIRSTSHORT :
#define FIRSTSHORT(N) *((uint16_t *) input[N])
บนเครื่องที่มีขนาดใหญ่การสั่งซื้อสตริงจำนวนเต็ม 8 บิตสองรูปแบบทางพจนานุกรมหรือแปลงเป็นจำนวนเต็ม 16 บิตและสั่งซื้อในภายหลังให้ผลลัพธ์เดียวกัน
โดยธรรมชาติแล้วสิ่งนี้สามารถเกิดขึ้นได้กับเครื่องจักรเล็ก ๆ น้อย ๆ เช่นกัน แต่ควรมีมาโคร
#define FIRSTSHORT(N) (input[N][0] | (input[N][1] >> 8))
ซึ่งทำงานได้ตามที่คาดหวังในทุกแพลตฟอร์ม
"เกณฑ์มาตรฐานขนาดใหญ่" ข้างต้นเป็นผลมาจากการใช้มาโครที่เหมาะสม
ด้วยแมโครที่ไม่ถูกต้องและเครื่องเล็ก ๆ น้อย ๆ รายการจะถูกจัดเรียงไว้ล่วงหน้าด้วยอักขระตัวที่สองของทุกบรรทัดทำให้เกิดการเรียงลำดับแบบสุ่มจากมุมมองของพจนานุกรม การเรียงลำดับการแทรกทำงานได้แย่มากในกรณีนี้