• No results found

Some practical variants of sorting algorithms are described, like sorting index arrays, pointer sorting, and sorting with a supplied comparison function.

3.3.1

Index sorting

With normal sorting we order the elements of an array f so that f[k] ≤ f[k+ 1]. The index-sort

routines order the indices in an array x so that the sequence f[x[k]] is in ascending order, we have f[x[k]]≤f[x[k+ 1]]. The implementation for the selection sort algorithm is [FXT: sort/sortidx.h]:

1 template <typename Type>

2 void idx_selection_sort(const Type *f, ulong n, ulong *x)

3 // Sort x[] so that the sequence f[x[0]], f[x[1]], ... f[x[n-1]] is ascending.

3.3: Variants of sorting methods 143

5 {

6 for (ulong i=0; i<n; ++i)

7 {

8 Type v = f[x[i]];

9 ulong m = i; // position-ptr of minimum

10 ulong j = n;

11 while ( --j > i ) // search (index of) minimum

12 { 13 if ( f[x[j]]<v ) 14 { 15 m = j; 16 v = f[x[m]]; 17 } 18 } 19 20 swap2(x[i], x[m]); 21 } 22 }

The verification code is

1 template <typename Type>

2 bool is_idx_sorted(const Type *f, ulong n, const ulong *x)

3 // Return whether the sequence f[x[0]], f[x[1]], ... f[x[n-1]] is ascending order.

4 {

5 for (ulong k=1; k<n; ++k) if ( f[x[k-1]] > f[x[k]] ) return false;

6 return true;

7 }

The transformation of thepartition() routine is straightforward:

1 template <typename Type>

2 ulong idx_partition(const Type *f, ulong n, ulong *x)

3 // rearrange index array, so that for some index p

4 // max(f[x[0]] ... f[x[p]]) <= min(f[x[p+1]] ... f[x[n-1]])

5 {

6 // Avoid worst case with already sorted input:

7 const Type v = median3(*x[0], *x[n/2], *x[n-1], cmp);

8 9 ulong i = 0UL - 1; 10 ulong j = n; 11 while ( 1 ) 12 { 13 do ++i; 14 while ( f[x[i]]<v ); 15 16 do --j; 17 while ( f[x[j]]>v ); 18 19 if ( i<j ) swap2(x[i], x[j]); 20 else return j; 21 } 22 }

The index-quicksort itself deserves a minute of contemplation comparing it to the plain version:

1 template <typename Type>

2 void idx_quick_sort(const Type *f, ulong n, ulong *x)

3 // Sort x[] so that the sequence f[x[0]], f[x[1]], ... f[x[n-1]] is ascending.

4 {

5 start:

6 if ( n<8 ) // parameter: threshold for nonrecursive algorithm

7 { 8 idx_selection_sort(f, n, x); 9 return; 10 } 11 12 ulong p = idx_partition(f, n, x); 13 ulong ln = p + 1; 14 ulong rn = n - ln; 15

16 if ( ln>rn ) // recursion for shorter sub-array

17 { 18 idx_quick_sort(f, rn, x+ln); // f[x[ln]] ... f[x[n-1]] right 19 n = ln; 20 } 21 else 22 {

23 idx_quick_sort(f, ln, x); // f[x[0]] ... f[x[ln-1]] left 24 25 n = rn; 26 x += ln; 27 } 28 29 goto start; 30 }

Note that the index-sort routines work perfectly for non-contiguous data. The index-analogues of the binary search algorithms are again straightforward, they are given in [FXT: sort/bsearchidx.h].

The sorting routines do not change the arrayf, the actual data is not modified. To bring f into sorted order, apply theinverse permutation ofxto f (see section 2.4 on page 109):

apply_inverse_permutation(x, f, n);

To copyf in sorted order intog, use:

apply_inverse_permutation(x, f, n, g);

Input: After sort_by_key(f, n, key, 1):

f[] key[] f[] key[] A 0 A 0 B 1 E 1 C 1 C 1 D 3 B 1 E 1 D 3 F 3 F 3 E 3 E 3 G 7 G 7

Figure 3.3-A:Sorting an array according to an array of keys.

The arrayxcan be used forsorting by keys, see figure 3.3-A. The routine is [FXT: sort/sortbykey.h]:

1 template <typename Type1, typename Type2>

2 void sort_by_key(Type1 *f, ulong n, Type2 *key, bool skq=true)

3 // Sort f[] according to key[] in ascending order:

4 // f[k] precedes f[j] if key[k]<key[j].

5 // If skq is true then key[] is also sorted.

6 { 7 ALLOCA(ulong, x, n); 8 for (ulong k=0; k<n; ++k) x[k] = k; 9 idx_quick_sort(key, n, x); 10 apply_inverse_permutation(x, f, n); 11 if ( skq ) apply_inverse_permutation(x, key, n); 12 }

3.3.2

Pointer sorting

Pointer sorting is similar to index sorting. The array of indices is replaced by an array of pointers [FXT: sort/sortptr.h]:

1 template <typename Type>

2 void ptr_selection_sort(/*const Type *f,*/ ulong n, const Type **x)

3 // Sort x[] so that the sequence *x[0], *x[1], ..., *x[n-1] is ascending.

4 {

5 for (ulong i=0; i<n; ++i)

6 {

7 Type v = *x[i];

8 ulong m = i; // position-ptr of minimum

9 ulong j = n;

10 while ( --j > i ) // search (index of) minimum

11 { 12 if ( *x[j]<v ) 13 { 14 m = j; 15 v = *x[m]; 16 } 17 } 18 swap2(x[i], x[m]);

3.3: Variants of sorting methods 145

19 }

20 }

The first argument (const Type *f) is not necessary with pointer sorting, it is indicated as a comment to make the argument structure uniform. The verification routine is

1 template <typename Type>

2 bool is_ptr_sorted(/*const Type *f,*/ ulong n, Type const*const*x)

3 // Return whether the sequence *x[0], *x[1], ..., *x[n-1] is ascending.

4 {

5 for (ulong k=1; k<n; ++k) if ( *x[k-1] > *x[k] ) return false;

6 return true;

7 }

The pointer versions of the search routines are given in [FXT: sort/bsearchptr.h].

3.3.3

Sorting by a supplied comparison function

The routines in [FXT: sort/sortfunc.h] are similar to the C-quicksortqsortthat is part of the standard library. A comparison functioncmp has to be supplied by the caller. This allows, for example, sorting compound data types with respect to some key contained within them. Citing the manual page forqsort:

The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second. If two members compare as equal, their order in the sorted array is undefined.

As a prototypical example we give the selection sort routine:

1 template <typename Type>

2 void selection_sort(Type *f, ulong n, int (*cmp)(const Type &, const Type &))

3 // Sort f[] (ascending order) with respect to comparison function cmp().

4 {

5 for (ulong i=0; i<n; ++i)

6 {

7 Type v = f[i];

8 ulong m = i; // position of minimum

9 ulong j = n;

10 while ( --j > i ) // search (index of) minimum

11 { 12 if ( cmp(f[j],v) < 0 ) 13 { 14 m = j; 15 v = f[m]; 16 } 17 } 18 19 swap2(f[i], f[m]); 20 } 21 }

The other routines are rather straightforward translations of the (plain) sort analogues. Replace the comparison operations involving elements of the array as follows:

(a < b) cmp(a,b) < 0 (a > b) cmp(a,b) > 0 (a == b) cmp(a,b) == 0 (a <= b) cmp(a,b) <= 0 (a >= b) cmp(a,b) >= 0

The verification routine is

1 template <typename Type>

2 bool is_sorted(const Type *f, ulong n, int (*cmp)(const Type &, const Type &))

3 // Return whether the sequence f[0], f[1], ..., f[n-1]

4 // is sorted in ascending order with respect to comparison function cmp().

5 {

6 for (ulong k=1; k<n; ++k) if ( cmp(f[k-1], f[k]) > 0 ) return false;

7 return true;

8 }

The numerous calls tocmp()do have a negative impact on the performance. With C++ you can provide a comparison ‘function’ for a class by overloading the comparison operators<,<,<=,>=, and==and use

the plain sort version. That is, the comparisons are inlined and the performance should be fine.

3.3.3.1 Sorting complex numbers

You want to sort complex numbers? Fine with me, butdon’t tell your local mathematician. To see the mathematical problem, we ask whetheriis less than or greater than zero. Assumingi >0 it follows that i·i >0 (we multiplied with a positive value) which is−1>0 and that is false. So, isi <0? Theni·i >0 (multiplication with a negative value, as assumed), thereby−1>0. Oops! The lesson is that there is no way to impose an order on the complex numbers that would justify the usage of the symbols ‘<’ and ‘>’ consistent with the rules to manipulate inequalities.

Nevertheless we can invent a relation for sorting: arranging (sorting) the complex numbers according to their absolute value (modulus) leaves infinitely many numbers in one ‘bucket’, namely all those that have the same distance from zero. However, one could use the modulus as themajor ordering parameter, the argument (angle) as theminor. Or the real part as the major and the imaginary part as the minor. The latter is realized in

1 static inline int

2 cmp_complex(const Complex &f, const Complex &g)

3 {

4 const double fr = f.real(), gr = g.real();

5 if ( fr!=gr ) return (fr>gr ? +1 : -1);

6

7 const double fi = f.imag(), gi = g.imag();

8 if ( fi!=gi ) return (fi>gi ? +1 : -1);

9

10 return 0;

11 }

This function, when used as comparison with the following routine, can indeed be the practical tool you had in mind:

1 void complex_sort(Complex *f, ulong n)

2 // major order wrt. real part

3 // minor order wrt. imag part

4 {

5 quick_sort(f, n, cmp_complex);

6 }

3.3.3.2 Index and pointer sorting

The index sorting routines that use a supplied comparison function are given in [FXT: sort/sortidxfunc.h]:

1 template <typename Type>

2 void idx_selection_sort(const Type *f, ulong n, ulong *x,

3 int (*cmp)(const Type &, const Type &))

4 // Sort x[] so that the sequence f[x[0]], f[x[1]], ... f[x[n-1]]

5 // is ascending with respect to comparison function cmp().

6 {

7 for (ulong i=0; i<n; ++i)

8 {

9 Type v = f[x[i]];

10 ulong m = i; // position-ptr of minimum

11 ulong j = n;

12 while ( --j > i ) // search (index of) minimum

13 { 14 if ( cmp(f[x[j]], v) < 0 ) 15 { 16 m = j; 17 v = f[x[m]]; 18 } 19 } 20 21 swap2(x[i], x[m]); 22 } 23 }

The verification routine is:

1 template <typename Type>

2 bool is_idx_sorted(const Type *f, ulong n, const ulong *x,

3 int (*cmp)(const Type &, const Type &))