Я реализовал алгоритм быстрой сортировки на C и правильно работает с векторами, содержащими менее 30 000 элементов. Но у него ошибка сегментации в соответствии
swap(&vet[sup], &vet[(sup-inf+1)/2]);
с большими векторами. Вот полный алгоритм:
void swap(int* a, int* b){
int temp = *a;
*a = *b;
*b = temp;
}
int partition(int inf, int sup, int vet[]){
while (inf<sup){
while (vet[inf]<=vet[sup] && inf<sup){inf++;}
while (vet[inf]<=vet[sup] && inf<sup){sup--;}
if (inf<sup){swap(&vet[inf], &vet[sup]);}
}
return inf;
}
void median (int inf, int sup, int vet[]){ //select the median of the first, last and midle value of the vector
if (vet[inf]>vet[(sup-inf+1)/2])
swap(&vet[inf], &vet[(sup-inf+1/2)]);
if (vet[sup]>vet[(sup-inf+1)/2])
swap(&vet[sup], &vet[(sup-inf+1)/2]);
else if (vet[sup]<vet[inf])
swap(&vet[sup], &vet[inf]);
}
void quicksort(int inf, int sup, int vet[]){
if (inf<sup){
median(inf, sup, vet);
int piv=partition(inf, sup, vet);
quicksort(inf, piv-1, vet);
quicksort(piv+1, sup, vet);
}
}
Я пытался написать быструю сортировку с минимальным объявлением переменных, но все равно получал ошибку сегментации. Я не знаю, является ли это переполнением стека или проблемой с указателем, которая возникает только с большими векторами.
Извините за мой плохой английский, я все еще учусь
Объясните, пожалуйста, ближайшей резиновой утке, зачем вам sup/2+1.
При векторе из 50 000 элементов возникает ошибка сегментации с sup=9996. sup/2+1 — это средний элемент вектора, он необходим, потому что ось — это медиана первого, последнего и среднего элемента.
«Sup/2+1 — это средний элемент вектора». Правда? Какого вектора? Пожалуйста, скажите об этом очень четко.
Вектор результата каждого вызова быстрой сортировки
Итак, вы вызываете quicksort с помощью inf=10000 и sup=10020. Середина какого вектора sup/2+1?
Объясните далее, что именно делает median. В комментарии написано, что он "выбирает медиану первого, последнего и среднего значения". Что он делает с выбранным значением? Как это влияет на последующие операции?
Кстати, если inf==sup, то (inf+sup)/2+1 посередине чего?
Медиана работает, выбирая медианный элемент и заменяя его последним. Раздел работает, выбирая последний элемент в качестве опорного. inf==sup никогда не появится из-за if (inf<sup) в начале быстрой сортировки.
«Раздел работает, выбирая последний элемент в качестве опорного». Вы действительно это проверяли? Добавьте printf ("Pivot index %d between %d and %d\n", piv, inf, sup);, запустите и посмотрите, что произойдет.
Я уже делал это раньше, и да, все работает правильно. Код отлично работает с векторами, содержащими 30 000 и менее элементов. Проблема в том, что большие векторы





В вашем коде есть несколько проблем:
ваш стиль кодирования трудно читать и отлаживать: например, если втиснуть тест и код замены в одну строку, невозможно определить, какая часть вызывает сбой. Пишите по одному оператору в строке и разбивайте управляющие операторы на несколько строк.
трюк с xor подстановки кода устарел: он загадочный, потенциально неопределенный и неэффективный. Хуже того: он не работает, если a и b указывают на одно и то же место. Используйте более простой код:
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
код раздела неверен: необходимо сохранить начальное значение sup, чтобы все сравнения выполнялись со значением опорной точки, и вам необходимо заменить опорную точку на окончательный индекс inf:
int partition(int inf, int sup, int vet[]) {
int pivot = sup;
while (inf < sup) {
while (vet[inf] <= vet[pivot] && inf < sup) {
inf++;
}
while (vet[sup] >= vet[pivot] && inf < sup) {
sup--;
}
swap(&vet[inf], &vet[sup]);
}
swap(&vet[inf], &vet[pivot]);
return inf;
}
это возможно, хотя и маловероятно, что набор данных, предоставленный вашему коду, будет создан так, чтобы вызвать переполнение стека. Возможно, вам захочется изучить классическую статью Дуга Макилроя: убийственный противник быстрой сортировки, в которой объясняется, как построить такой вектор. Простую контрмеру, которая предотвращает потенциальное переполнение стека, но не уменьшает квадратичную временную сложность, легко реализовать: рекурсия только на меньшей половине и цикл на большей половине.
более вероятная причина сбоя в том, что вы не выбираете средний элемент в срезе с помощью sup/2+1, вам следует написать (inf + (sup - inf + 1) / 2, следовательно, опорная точка выбрана неправильно, поэтому алгоритм может рекурсировать слишком много раз даже для случайного вектора.
Вот модифицированная версия:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int partition(int inf, int sup, int vet[]) {
int pivot = sup;
while (inf < sup) {
while (vet[inf] <= vet[pivot] && inf < sup) {
inf++;
}
while (vet[sup] >= vet[pivot] && inf < sup) {
sup--;
}
swap(&vet[inf], &vet[sup]);
}
swap(&vet[inf], &vet[pivot]);
return inf;
}
// Select the median of the first, last and middle value of the vector
// and move it to vet[sup]
void median(int inf, int sup, int vet[]) {
int m = inf + (sup - inf + 1) / 2;
// set vet[m] = min(vet[inf], vet[m])
if (vet[inf] > vet[m]) {
swap(&vet[inf], &vet[m]);
}
if (vet[sup] > vet[m]) {
// set vet[sup] = min(vet[sup], min(vet[m], vet[inf]))
swap(&vet[sup], &vet[m]);
} else
if (vet[sup] < vet[inf]) {
// set vet[sup] = min(vet[sup], max(vet[m], vet[inf]))
swap(&vet[sup], &vet[inf]);
}
}
void quicksort(int inf, int sup, int vet[]) {
while (inf < sup) {
median(inf, sup, vet);
int piv = partition(inf, sup, vet);
// recurse on the smaller half, loop on the other half
if (piv - inf < sup - piv) {
quicksort(inf, piv - 1, vet);
inf = piv + 1;
} else {
quicksort(piv + 1, sup, vet);
sup = piv - 1;
}
}
}
int main(int argc, char *argv[]) {
size_t n = argc < 2 ? 30000 : strtol(argv[1], NULL, 0);
int *array = calloc(n, sizeof(*array));
int res = 0;
if (array == NULL) {
fprintf(stderr, "cannot allocate memory for %zu elements\n", n);
return 1;
}
srand(time(NULL));
for (size_t i = 0; i < n; i++) {
array[i] = rand();
}
quicksort(0, n - 1, array);
for (size_t i = 1; i < n; i++) {
if (array[i] < array[i - 1]) {
printf("sort error at %zu: %d > %d\n",
i, array[i - 1], array[i]);
res = 1;
break;
}
}
free(array);
return res;
}
Спасибо за ответ, я ценю это. Но я не смог это проверить, потому что тестировать слишком дорого. При 30000 элементах он выполняется больше минуты и даже не завершается, старый алгоритм делает это за 2,5 секунды.
Но я редактирую код, чтобы облегчить чтение
@Thallysson: извините, у меня была ошибка: sup = piv; должно было быть sup = piv - 1;, и я пропустил еще одну ошибку в вашей функции partition, вызывающую огромную неэффективность. Я дополнил ответ простым тестом, использующим случайный массив. Эта версия сортирует 100 миллионов случайных целых чисел за 1,4 секунды на моем ноутбуке M2.
Что вам говорит отладчик?
supза пределами поля? Как оно вышло за пределы?vetнедействителен? Как оно стало недействительным?sup/2+1за пределами поля? Как оно вышло за пределы? (Я бы присмотрелsup/2+1.)