It's much faster than quicksort!
.
.
.
.
.
.
because it copies it's homework.
How it works:
It basically assumes that it would only ever get used in testing like other funny sorting algorithms like bogosort. When testing sorting algorithms, you usually also run 'classic' algs. like quicksort to compare times.
Pirate sort exploits this, because instead of sorting anything, it just tries to find an array the algorithms that ran before it sorted and copies it. It might work on other machines / compilers, but trying to get variables from the stack or heap like this doesn't seem like it would be stable. There are some magic numbers to prevent most of the crashing. I don't really recommend running it, but it's funny that it works at all! (don't nitpick the code, I was going for an "it just works" approach, ok?)
Btw the name comes from both copying something like piracy and the shortening of array to arr.
Code:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int compare(const void* a, const void* b) {
return (*(int*)a - *(int*)b);
}
//by nanisukoshi 2025
int pirate_sort(int *p, int n, int **return_arr) {
//assumptions:
//in a test program being compared to other sort algs.
//a sorting alg already sorted the array and saved it before pirate_sort was called somewhere
//assumes the result is either in teh stack or in heap and a ptr is in stack
//plundering time
*return_arr = (int*)calloc(n,sizeof(int));
int min = p[0];
for (int i = 0; i < n; i++) {
if (min > p[i])
min = p[i];
}
int min2 = p[0];
for (int i = 0; i < n; i++) {
if (min2 > p[i] && p[i] != min)
min2 = p[i];
}
int max = p[0];
for (int i = 0; i < n; i++) {
if (max < p[i])
max = p[i];
}
int max2 = p[0];
for (int i = 0; i < n; i++) {
if (max2 < p[i] && p[i] != max)
max2 = p[i];
}
if (n < 200000) {
//stack approach, look after array
int* p1 = p + n;
for (int i = 0; i < 196; i++) {
if (
*((int*)(p1 + i)) == min
&&
*(((int*)(p1 + i)) + 1) == min2
) {
memcpy(*return_arr, ((int*)(p1 + i)), n * sizeof(int));
return 0;
}
}
//stack approach, look before array
for (int i = 0; i < 256; i++) {
if (
*((int*)(p - i)) == max
&&
*(((int*)(p - i)) - 1) == max2
) {
memcpy(*return_arr, (((int*)(p - i)) - n + 1), n * sizeof(int));
return 0;
}
}
}
//might be better to put stack and dynamic into 1 for loop, but meh
//dyn. allocation yoinkery
//
//malloc approach, look after array
long long ours = (((unsigned long long) * return_arr) & 0xFFFFFFFF);
int* p1 = p + n;
for (int i = 0; i < 196; i++) {
long long target = (long long)*(p1 + i);
target &= 0xFFFFFFFF;
if (abs(target-ours) < 0x1000000) {
long long target_upper = (long long)*(p1 + i+1);
int* test = (int*)(target | target_upper << 32);
if (*test == min && *(test + 1) == min2) {
memcpy(*return_arr, test, n * sizeof(int));
return 0;
}
}
}
//malloc approach, look before array
p1 = p;
for (int i = 0; i < 256; i++) {
long long target = (long long)*(p1 - i);
target &= 0xFFFFFFFF;
if (abs(target - ours) < 0x1000000) {
long long target_upper = (long long)*(p1 - i + 1);
int* test = (int*)(target | target_upper << 32);
if (*test == min && *(test + 1) == min2) {
memcpy(*return_arr, test, n * sizeof(int));
return 0;
}
}
}
return -1;
}
int pa(int* a, int n) {
printf("array:\n");
for (int i = 0; i < n; i++) {
printf("%d\n", a[i]);
}
}
int smallexample() {
//opt. A)
//int a_s[] = {1,2,3,4,5,6}; // will steal
//opt. D)
//int* a1 = (int*)malloc(6 * sizeof(int)); //will steal if qsort sorts it
int a[] = { 4,6,2,3,1,5 };
int n = sizeof(a) / sizeof(a[0]);
//opt. B)
//int a0[] = {4,6,2,3,1,5 }; //will steal if qsort sorts it
//qsort(a0, n, sizeof(int), compare);
//pa(a1, n);
//opt. C)
int* a1 = (int*)malloc(n * sizeof(int)); //will steal if qsort sorts it
//opt. C-D)
memcpy(a1, a, sizeof(a));
qsort(a1, n, sizeof(int), compare);
//pa(a1, n);
int* result_ts;
int err = pirate_sort(a, n, &result_ts);
printf("pirate sorted ");
pa(result_ts, n);
free(result_ts);// a pirate is free()
}
void generate_array(int* a, int n) {
for (int i = 0; i < n; i++)
a[i] = rand() % 100000 + 1; // 1..100000
}
void timecompare(int n) {
srand((unsigned)time(NULL));
int* a = malloc(n * sizeof(int));
int* a_q = malloc(n * sizeof(int));
if (!a || !a_q) exit(1);
// generate original
generate_array(a, n);
memcpy(a_q, a, n * sizeof(int));
clock_t t1 = clock();
qsort(a_q, n, sizeof(int), compare);
clock_t t2 = clock();
double qsort_ms = 1000.0 * (t2 - t1) / CLOCKS_PER_SEC;
int* result_ts = NULL;
clock_t t3 = clock();
int err = pirate_sort(a, n, &result_ts);
clock_t t4 = clock();
double pirate_ms = 1000.0 * (t4 - t3) / CLOCKS_PER_SEC;
if (err != 0) {
printf("pirate fail\n");
}
printf("qsort time: %.3f ms\n", qsort_ms);
printf("pirate_sort time: %.3f ms\n", pirate_ms);
//pa(result_ts, n);
free(a);
free(a_q);
free(result_ts);
}
int main() {
//smallexample();
printf("100 elements:\n");
timecompare(100);
printf("1000 elements:\n");
timecompare(1000);
printf("10000 elements:\n");
timecompare(10000);
printf("100000 elements:\n");
timecompare(100000);
printf("200000 elements:\n");
timecompare(200000);
return 0;
}
I didn't write it to be readable (cursed code was kind of the passive goal). I was 'speedrunning' because I had this pirate sort idea and couldn't get it out of my head unless I made it.
But to explain it:
int pirate_sort(int *p, int n, int **return_arr)
int - return value for errors, 0 -> ok, not 0 -> error number
int *p - pointer to the original array, it could have been "int original_array[]", but I'm too lazy to write 'original_array' and adding something to a pointer to move in memory outside the bounds of an array feels more natural to me when it's an 'int*' than an 'int a[]'
int n - number of elements
int **return_arr - it's a pointer to a pointer, which you need when you want to change a pointer outside the function (or the whole array).
-You don't need int ** for this: a[0] = 2; // int *a;
-But you do need it for this: (make a new array and have the reference to it outside of the function)
*return_arr = (int*)calloc(n,sizeof(int));