it-swarm.com.ru

Почему функция сравнения, переданная qsort (), должна возвращать три разных значения?

Я прочитал, что функция сравнения, требуемая для qsort(), должна иметь 3 результата:

  • отрицательный результат, если val1 < val2
  • 0 if val1 == val2
  • положительный результат, если val1 > val2

Насколько я знаю, сортировка массива требует только предикат, который возвращает истину или ложь. Возьмите пузырьковую сортировку, например:

int compare(int a, int b)
{
    if(a>b) return 1;
    return 0;
}
void bubbleSort(int arr[], int n) 
{
    int i, j;
    for (i = 0; i < n-1; i++)  
        for (j = 0; j < n-i-1; j++)  
            if ( compare(arr[j],arr[j+1]) ) 
                swap(&arr[j], &arr[j+1]);
}

Так почему функция сравнения qsort() должна иметь 3 возможных результата, а не 2?

7
MihaiM

Основываясь на комментарии @Matteo Italia, существует проблема эффективности числа сравнений, и вы можете уменьшить число сравнений, используя равенство, так как a == b и !(a < b) && !(b < a) эквивалентны в некоторых случаях (например, когда значения целые).

Кроме того, в более общем случае (не в qsort, как указано в комментариях), он необходим для стабильности функции сортировки. В случаях равенства, если сортировка хочет быть устойчивой, она должна знать о равенстве в сравнении. Вы можете узнать больше о стабильности в сортировке здесь . Следовательно, возвращение трех значений требуется для стабильных методов сортировки

1
OmG

qsort может быть написана с использованием булевой функции сравнения вместо трехстороннего сравнения, но в спецификации говорится, что для этого требуется трехсторонняя функция сравнения, а некоторые (не все) реализации используют преимущества трех возможных случаев. Если ваша функция сравнения не соответствует спецификации, результатом будет неопределенное поведение. В этом случае неопределенное поведение может включать в себя ошибку в сортировке или переполнение буфера в очень редких угловых случаях, которые вы можете не заметить, пока космический челнок не вернется с орбиты. Так что не делай этого.

Что касается того, почему реализации может потребоваться трехстороннее сравнение, здесь есть одна возможность: для входных данных с большим количеством повторяющихся значений быстрая сортировка может быть значительно ускорена с помощью трехстороннего разбиения. Это можно сделать с помощью двустороннего сравнения, выполнив сравнение дважды в противоположных направлениях, но разработчик, зная, что требуется трехсторонний компаратор, может проверить на равенство, когда это то, что он хочет знать.

4
rici

Если элемент A == элемент B, но функция компаратора сообщает qsort, что B > A, то qsort подумает, что могут быть другие элементы, которые должны находиться между A и B, когда это не так, и поэтому выполняют ненужные проверки.

3
Weather Vane

Технически, вы можете делать только с меньшим, чем, потому что a == b эквивалентен !(a < b) && !(b < a).

2
Yves Daoust

Если внутренний цикл быстрой сортировки записан эквивалентно этому:

while(x[i] < pivot) i++;  // less-than
while(pivot < x[j]) j--;  // less-than

тогда вы можете обойтись только с помощью <

В любом случае, придерживайтесь принципа наименьшего удивления сделав более очевидным, что должен делать вызывающий объект - если указатель вашей функции сравнения называется compare и функция не должна вести себя как обычный stdlib qsort compare делегат, тогда это плохая идея. 

С другой стороны, если ваш параметр назван как-то вроде less_than или isLessThan, для вызывающей стороны должно быть гораздо более очевидно, что ожидается от функции сравнения:

 void sort(
     const void * arr,
     size_t num_items,
     size_t element_size, 
     bool (*is_less_than)(const void*, const void*)
 );
1
Groo