The La Plata Mountains as seen from above the author痴 home.
Durango Bill痴
鼎? Program to implement Sort Algorithms
(Source Code)
Return
to
the main Sort page
Web page generated via Sea Monkey's Composer HTML editor
within a Linux Cinnamon Mint 18 operating system.
(Goodbye Microsoft)
//*************
********************* *************************
*****************
//
//
Sort Algorithms
//
by
//
Bill Butler
//
// This program
can execute various sort algorithms to test how fast they run.
//
// Sorting
algorithms include:
// Bubble sort
// Insertion sort
// Median-of-three
quicksort
// Multiple link
list sort
// Shell sort
//
// For each of the
above, the user can generate up to 10 million random
// integers to
sort. (Uses a pseudo random number generator so that the list
// to sort can be
exactly regenerated.)
// The program
also times how long each sort takes.
//
//************
********************** ********************
*******************
#include
<stdheaders.h>
// The usual
stdio.h, etc.
void
GenRandom(void);
// Generate a list of
random nbrs.
void
BubbleSort(void);
//
Bubble Sort
void
InsertionSort(void);
//
Insertion Sort
void MLLsort(void);
//
Multiple Link List Sort
void QuickSort(void);
// Median-of-three
Quicksort
void
ShellSort(void);
// Choose from 3 gap sequences
void
PauseMsg(void);
// Pause the screen display
unsigned
RandNbrs[10000004];
// The list
of random numbers
// will be generated here. Note:
// RandNbrs[0] is used as a sentinel
int MLLlinks[26777220];
// Used by
MLLsort. Will use up to
// 10,000,000 + 2^24 + 1 of this
int
QSleft[30];
// Stack
arrays for the left/right
int
QSright[30];
// ends of
subgroups within
// QuickSort()
int Gaps13[24] = {0, 1,
3, 7, 21, 48, 112, // Three possible gap
sequences
336,
861, 1968, 4592, 13776, 33936, //
may be used for experiments
86961, 198768, 463792, 1391376,
// with Shell Sort.
3402672, 8382192, 21479367, 49095696,
// See Sedgewick & ATT database
114556624, 343669872};
// of
integers for more info
int Gaps18[24] = {0, 1,
8, 23, 77, 281, //
Gaps[i+2] = 4^(i+1) + 3*(2^i) + 1
1073,
4193, 16577, 65921, 262913,
1050113, 4197377, 16783361, 67121153,
268460033, 1073790977};
int Gaps14[24] = {0, 1,
4, 13, 40, 121, 364, // Gaps[i+1] = 3*Gaps[i] + 1
1093,
3280, 9841, 29524, 88573, 265720,
797161, 2391484, 7174453, 21523360,
64570081, 193710244, 581130733, 1743392200};
int
Gaps[24];
// Will copy one of the above into
// here.
int
Nbr2Sort;
// Number of items to sort.
char
Databuff[100];
// Buffer for user input
(Assumes
// user does not abuse size)
int main(void) {
int choice;
puts("\nThis
program demonstrates the performance of various sort
algorithms.");
puts("You may run
time trials to compare results.\n");
puts("Note: If
you want to use identical inputs for the time trials,");
puts("just use
the same seed number for the random number generator.\n");
PauseMsg();
while(1)
{
// Loop until user
ends program
puts("\n\n
***** Enter number for menu
choice *****\n");
puts("1) Bubble sort");
puts("2) Insertion sort");
puts("3) Median-of-three Quicksort");
puts("4) Multiple link list sort");
puts("5) Shellsort (Choice of 3 different shell
definitions)");
puts("6) End the program\n");
gets(Databuff);
choice = atoi(Databuff);
if
(choice == 6)
break;
GenRandom();
// Generate a list
of random nbrs
if
(choice == 1) BubbleSort();
else
if (choice == 2) InsertionSort();
else
if (choice == 3) QuickSort();
else
if (choice == 4) MLLsort();
else
if (choice == 5) ShellSort();
}
return 0;
}
//**************
********************* ***********************
******************
//
//
GenRandom
//
// This routine
generates a list of unsigned random integers in the range 0 to
// 4,294,967,295.
An exact repetion of any list can be regenerated by using
// the same seed
number and the same quantity of numbers. Up to 10 million
// numbers can be
generated in the array RandNbrs[].
//
// The random
numbers will occupy positions RandNbrs[1] to
RandNbrs[Nbr2Sort].
// Nothing is
placed in RandNbrs[0], but RandNbrs[0] will be used by some of
// the sort
routines to optimize execution speed.
//
//**************
******************** **************************
*****************
void GenRandom(void) {
int i;
unsigned seed;
unsigned
Multiplier;
do {
puts("\nEnter number of elements to sort (3 - 10,000,000)");
gets(Databuff);
Nbr2Sort = atoi(Databuff);
} while
((Nbr2Sort < 3) || (Nbr2Sort > 10000000));
puts("\nEnter a
seed number for the random number generator");
gets(Databuff);
seed =
atoi(Databuff);
Multiplier =
3141592621;
for (i =
Nbr2Sort; i; i--) {
// Fill array with
random
seed
*= Multiplier;
// numbers.
seed++;
RandNbrs[i] = seed;
}
puts("\nDo you
want to see the random numbers before they are sorted (Y or
N)?");
gets(Databuff);
if
(tolower(Databuff[0]) == 'y') {
puts("");
for
(i = 1; i <= Nbr2Sort; i++) {
printf("%4d) %'16u\n",i , RandNbrs[i]);
if (!(i%20))
// Keeps list from
running
PauseMsg();
// off top of screen.
}
PauseMsg();
}
}
//****************
********************** ********************** ***************
//
//
Bubble Sort
//
// Bubble sort is
not exactly the world's fastest sorting algorithm, but for
// some reason,
everyone seems to like it. Maybe it's related to:
//
// "Tiny Bubbles"
// "In the wine"
// "Make you feel
happy"
// "Make you feel
fine"
//
// This version of
Bubble Sort will sort the array RandNbrs[] in ascending
// order. When the
routine is finished, the lowest number in the RandNbrs[]
// array will be
found in position RandNbrs[1] while the largest number will
// be in position
RandNbrs[Nbr2Sort].
//
// The algorithm
starts by looking at positions 1 and 2 in the array. If the
// number at
position 1 is larger than the number at position 2, then the
two
// numbers are
exchanged. The end result will leave the larger of the two
// numbers at
position 2 and the smaller at position 1.
//
// After the above
step, the algorithm looks at the numbers at positions 2 and
// 3. If the
number at position 2 is larger, then it is exchanged so that
the
// larger number
will now be in position 3.
//
// The "j" loop
continues this process until it reaches the bottom (highest
// index location)
of the array. At this point the largest number in the array
// has been moved
to the bottom of the array, and everything else has "bubbled"
// up one level.
Then, the "i" loop exerts control and the whole process
// repeats.
However, this time through, the 2nd largest number in the
array
// will be shifted
down to the 2nd lowest position in the array. (Again this
// stopping point
is controled by the "i" loop.)
//
// By the time "i"
decreases to 1, the whole array is sorted.
//
//****************
********************* ***********************
*****************
void BubbleSort(void) {
int i, j, k;
unsigned temp;
double Time1,
Time2;
puts("\nStarting
Bubble Sort");
Time1 =
(double)clock();
// Save time to 0.001 sec.
Time1 =
Time1/(double)CLOCKS_PER_SEC;
for (i = Nbr2Sort
- 1; i; i--) {
for
(j = 1, k = 2; j <= i; j++, k++) {
if (RandNbrs[j] > RandNbrs[k]) {
temp = RandNbrs[j];
RandNbrs[j] = RandNbrs[k];
RandNbrs[k] = temp;
}
}
}
Time2 =
(double)clock();
Time2 =
Time2/(double)CLOCKS_PER_SEC;
printf("\nThe
time to sort %'d items via Bubble Sort was %g seconds.\n",
Nbr2Sort, Time2 - Time1);
PauseMsg();
puts("\nDo you
want to see the random numbers after they were sorted (Y or
N)?");
gets(Databuff);
if
(tolower(Databuff[0]) == 'y') {
puts("");
for
(i = 1; i <= Nbr2Sort; i++) {
printf("%4d) %'16u\n", i, RandNbrs[i]);
if (!(i%20))
PauseMsg();
}
PauseMsg();
}
}
//******************
******************* *********************** ***************
//
//
Insertion Sort
//
// Insertion sort
is simple to code and difficult to beat if you are sorting
// a short list of
elements. It is also very good at sorting a much larger
// list that is
nearly in sorted order. It is thus used as the basis of Shell
// Sort and the
final stage of "median-of-three Quicksort".
//
// Computer
processors usually have "on board" cache memories that provide
an
// added boost to
Insertion Sort. Portions of the array to be sorted will be
// stored in the
(faster access) cache memory. If an array is nearly sorted
// when Insertion
Sort is called, then most of Insertion Sort's operations
// will take place
inside this high speed cache memory.
//
// This version of
Insertion Sort will sort the array RandNbrs[] in ascending
// order. When the
routine is finished the lowest number in the RandNbrs[]
// array will be
found in position RandNbrs[1] while the largest number will
// be in position
RandNbrs[Nbr2Sort].
//
// The algorithm
starts with some number in place at RandNbrs[1]. Then it
// moves down to
array position 2. The number at position 2 is copied to a
// temporary
holding position in variable "temp". Then all numbers that are
in
// lower index
positions in the array and are also larger than what is in
// "temp" are
moved down one position. When a smaller number is encountered,
// then the number
in "temp" is inserted back into the array.
//
// Next the
algorithm works on the number in array position 3. Again all
// numbers that
are larger than what is in "temp" are moved down one position.
// When a smaller
(or equal) number is encounter, the number in "temp" is
// inserted back
into the empty space.
//
// The algorithm
continues in this fashion until it reaches the bottom of
// the list to be
sorted.
//
//************
**************************** *******************
******************
void InsertionSort(void)
{
int i, j, k;
unsigned temp;
double Time1,
Time2;
puts("\nStarting
Insertion Sort");
Time1 =
(double)clock();
// Save time to 0.001 sec.
Time1 =
Time1/(double)CLOCKS_PER_SEC;
RandNbrs[0] =
0;
// Sentinel for sort. Must be <= the lowest
// value that will be sorted. Using a sentinel
// speeds up the algorithm since an additional
// run-off-the-"0"-end-of-the-array test will
// not be needed.
for (i = 2; i
<= Nbr2Sort; i++) {
k =
i;
j = i
- 1;
temp
= RandNbrs[k];
while
(RandNbrs[j] > temp) {
RandNbrs[k] = RandNbrs[j];
j--;
k--;
}
RandNbrs[k] = temp;
}
Time2 =
(double)clock();
Time2 =
Time2/(double)CLOCKS_PER_SEC;
printf("\nThe
time to sort %'d items via Insertion Sort was %g seconds.\n",
Nbr2Sort, Time2 - Time1);
PauseMsg();
puts("\nDo you
want to see the random numbers after they were sorted (Y or
N)?");
gets(Databuff);
if
(tolower(Databuff[0]) == 'y') {
for
(i = 1; i <= Nbr2Sort; i++) {
printf("%4d) %'16u\n", i, RandNbrs[i]);
if (!(i%20))
PauseMsg();
}
PauseMsg();
}
}
//**************
******************** *********************** ***************
//
//
MLLsort
//
// If the numbers
to be sorted are within a known range, and if on average
// they are
distributed approximately evenly, and if you have lots of
extra
// random access
memory, then a multiple link list sort may be faster than
// all other
sorting algorithms. In this application, the numbers to be
sorted
// are in the
array RandNbrs[]. The sorting algorithm never moves them.
//
// Instead, for
each element to be sorted, its proper sort location is
// calculated as
an index into the MLLlinks[] array. This initial calculated
// link head
address is equal to the sum of the Nbr2Sort plus a calculated
// distance beyond
this index number. The calculated distance can be varied to
// see what
produces the best result.
//
// The correlation
between the RandNbrs[] array and the MLLlinks[] array
// looks like:
//
//
------------------------
// RandNbrs
| | |
| | |
//
------------------------
//
0 1 2
Nbr2Sort
//
//
---------------------------
-----------------------------------
// MLLlinks |Link
lists for each grp | Link
head for each group |
//
----------------------------
----------------------------------
//
0
1 2 Nbr2Sort
Calculated pos. for each random nbr.
//
// Once a
calculated address is known, a link list is started using this
// number's
calculated addess. If any subsequent RandNbrs[] element ends
up
// using this same
calculated address, it is added to the link list for this
// address. The
links in any link list are adjusted for these additions such
// that the access
order will be in sorted order.
//
//***************
********************** *********************** *************
void MLLsort(void) {
int HeadBase, i,
count;
unsigned
NbrHeads, ShiftAmt, value;
unsigned ptr1,
ptr2;
double Time1,
Time2;
puts("\nStarting
Multiple Link List Sort");
Time1 =
(double)clock();
// Save
time to 0.001 sec.
Time1 =
Time1/(double)CLOCKS_PER_SEC;
RandNbrs[0] =
4294967295;
// Sentinel
for sort.
// Must be >= the largest
// number to be sorted.
HeadBase =
Nbr2Sort + 1;
NbrHeads =
4;
// Calculate how many list
ShiftAmt =
30;
// heads and how many bit
while (NbrHeads
< Nbr2Sort) {
// positions to shift for
ShiftAmt--;
// indexing.
NbrHeads <<= 1;
}
// Optional debug info
// printf("With
%'d numbers to sort the calculated value for NbrHeads is
%'d\n",
//
Nbr2Sort, NbrHeads);
for (i = Nbr2Sort
+ NbrHeads; i >= HeadBase; i--)
MLLlinks[i] = 0;
// Zero out the link heads. Note:
// If you are only going to run
// this routine once, you can take
// advantage of the built in
// initialization routines in C
// and ignore this step.
for (i =
Nbr2Sort; i; i--) {
// For all input numbers.
value
= RandNbrs[i];
// Will calculate where it should
ptr1
= value;
// go. Construct index.
ptr1
>>= ShiftAmt;
ptr1
+= HeadBase;
// Search link list to see where
// to insert this element. Most of
// the time the new value will be
// the 1st in the list.
for
(ptr2 = MLLlinks[ptr1]; RandNbrs[ptr2] < value;
ptr1 = ptr2, ptr2 = MLLlinks[ptr2]);
// Note: The
average length of these link lists does not increase as
// "Nbr2Sort"
increases. The processing time per sort element is
// a constant that
is independent of "Nbr2Sort". Thus the algorithm
// runs in pure
linear time and not something slower such as
// N*log(log(N))
time.
MLLlinks[ptr1] = i;
// Insert location of new
MLLlinks[i] = ptr2;
// value in link list.
}
Time2 =
(double)clock();
Time2 =
Time2/(double)CLOCKS_PER_SEC;
printf("\nThe
time to sort %'d items via MLL Sort was %g seconds.\n",
Nbr2Sort, Time2 - Time1);
PauseMsg();
puts("\nDo you
want to see the random numbers after they were sorted (Y or
N)?");
gets(Databuff);
if
(tolower(Databuff[0]) == 'y') {
count
= 0;
ptr2
= Nbr2Sort + NbrHeads;
for
(i = HeadBase; i <= ptr2; i++) {
for (ptr1 = MLLlinks[i]; ptr1; ptr1 = MLLlinks[ptr1]) {
count++;
printf("%4d) %'16u\n", count,
RandNbrs[ptr1]);
if (!(count%20))
PauseMsg();
}
}
PauseMsg();
}
}
//***************
******************** ************************
******************
//
//
QuickSort
//
// QuickSort has
long had a reputation for being the fastest general purpose
// sort algorithm.
It is also perhaps the most difficult to code, and is
// subject to
sharply adverse execution time if the "pivot values" are
picked
// poorly - which
can happen if the data to be sorted is already partially
// sorted.
//
// The algorithm works by picking one of the elements to
be sorted as a "pivot
// value". The list of items to be sorted is then
partitioned so that all
// elements that have a value less than the pivot end up
in the front portion
// of the array while all elements that are greater than
the pivot value end
// up in the other end. (Elements that are equal to the
pivot could end up in
// either section.) After the first pass, the array of
items to be sorted
// looks like:
//
//
Pivot
//
Low elements are here Item
Higher elements are here
//
-----------------------------------------------------------
// RandNbrs | |
| | |
| | |
| | |
| |
//
-----------------------------------------------------------
//
1 2 3
4
Nbr2Sort
//
// After round 1,
the QuickSort process is applied to both of the 2 subgroups.
// Whichever
subgroup was smaller is processed immediately while the
location
// of the left and
right ends of the larger subgroup are placed on a stack
// for later
processing. This processing order will guarantee that the
stack
// will never
exceed Log2(Nbr2Sort) items.
//
// The repetitive
processing of subgroups continues until the size of a
// subgroup falls
below a size defined by "MinGroup". Once a subgroup is
// smaller than
this, it is not sorted further by QuickSort. Small groups can
// be processed
faster by Insertion Sort. When Quicksort has reduced all
// subgroups to
< "MinGroup" size, control passes to "Insertion Sort" for a
// final pass
through the entire array.
//
// In the
"old days", the optimal size for "MinGroup" was about 18. The
cache
// memory on
current processor chips reduces the time to access anything in
// the cache -
which includes the part of the array that is currently
residing
// in the cache.
This greatly increases the efficiency of the final "Insertion
// Sort" relative
to the quicksort portion. Thus, significantly larger values
// for "MinGroup"
work better when a cache is being used. (You can experiment
// with the value
that is assigned to "MinGroup".)
//
// Selection of
the "pivot value" is crucial to the efficiency of Quicksort.
// If the pivot
value is selected so that it evenly partitions a subgroup,
// then Quicksort
is very efficient. On the other hand, if the value of the
// "Pivot item" is
near either the lowest or highest values that are going to
// be partitioned
within any subgroup, that particular round of Quicksort will
// not do its job
of quickly spliting the subgroups into ever smaller sizes.
//
// The "median of
three" portion of the routine is an effort to pick a good
// "pivot value".
If a "pivot value" can be picked so that it exactly splits a
// subgroup into 2
equal portions, then Quicksort will be as efficient as
// possible. An
effort is made to do this by trying to find a value which is
// close to the
median of the subgroup. This is done by checking the values at
// the second,
last, and middle positions within a subgroup. The middle value
// of these three
is used as the "pivot value" while the two extremes are
// placed at the
two ends of the subgroup.
//
// The code given
here is based on a flier that Robert Sedgewick (author of
// "Algorithms")
handed out "a few years ago" during a 2-semester sequence of
// "Analysis of
Algorithms". (Professor Sedgewick is 2nd from the left in the
// center photo at
http://groups.yahoo.com/group/CSAtrium/)
//
//****************
********************* ***********************
*****************
void QuickSort(void) {
int i, j, k, StackPtr;
int LeftEnd, RightEnd, LeftPtr, RightPtr,
MidPtr, MinGroup;
unsigned Pvalue,
temp;
double Time1,
Time2;
puts("\nStarting
QuickSort");
Time1 =
(double)clock();
// Save time to 0.001 sec.
Time1 =
Time1/(double)CLOCKS_PER_SEC;
RandNbrs[0] =
0;
// Sentinel for sort - used by
// by the Insertion Sort
// portion.
// Initialize left end, right end, stack pointer,
// and minimum size for subgroups.
LeftEnd =
1;
// For the first
round, the 2
RightEnd =
Nbr2Sort;
// ends will be the whole array
MinGroup =
65;
// Years ago this would
be ~18
if (Nbr2Sort >
MinGroup)
// Run quicksort until no
StackPtr = 1;
// subgroup
remains larger
else StackPtr =
0;
// than "MinGroup" elements.
// Start quicksort. First, set the pivot value equal to
the median of the
// array values at RandNbrs[LeftEnd+1],
RandNbrs[(LeftEnd+RightEnd)/2],
// and RandNbrs[RightEnd]. The minimum of these 3 is
placed at
// RandNbrs[LeftEnd+1] while the maximum is placed at
RandNbrs[RightEnd].
// The value at RandNbrs[LeftEnd] is moved to
// RandNbrs[(LeftEnd+RightEnd)/2].
while (StackPtr)
{
// Loop until all subgroups
// are partitioned down to
// <= "MinGroup" size.
LeftPtr = LeftEnd + 1;
// Ptr to left end.
RightPtr = RightEnd;
// Ptr to right end.
MidPtr = (LeftEnd + RightEnd)/2;
// Point to middle
// Start sort of these 3
if
(RandNbrs[LeftPtr] > RandNbrs[RightPtr]) {
temp = RandNbrs[LeftPtr];
//
elements
RandNbrs[LeftPtr] = RandNbrs[RightPtr];
RandNbrs[RightPtr] = temp;
}
if
(RandNbrs[MidPtr] > RandNbrs[RightPtr]) {
Pvalue = RandNbrs[RightPtr];
RandNbrs[RightPtr] = RandNbrs[MidPtr];
}
else
if (RandNbrs[MidPtr] < RandNbrs[LeftPtr]) {
Pvalue = RandNbrs[LeftPtr];
RandNbrs[LeftPtr] = RandNbrs[MidPtr];
}
else
Pvalue = RandNbrs[MidPtr];
// The 3 values are sorted and
// and the median is in Pvalue
RandNbrs[MidPtr] = RandNbrs[LeftEnd];
// Fill in hole with LeftEnd
// Start the main loop. Move pointers inward until
// we find 2 elements that have to be exchanged.
while
(RandNbrs[++LeftPtr] < Pvalue);
// Set up pointers
while
(RandNbrs[--RightPtr] > Pvalue); //
for 1st exchange
while
(LeftPtr < RightPtr) {
// Make these
temp = RandNbrs[LeftPtr];
//
statements as
RandNbrs[LeftPtr] = RandNbrs[RightPtr]; //
efficient as
RandNbrs[RightPtr] = temp;
//
possible.
while (RandNbrs[++LeftPtr] < Pvalue);
// Continue this loop until
while (RandNbrs[--RightPtr] > Pvalue);
// the pointers cross.
}
RandNbrs[LeftEnd] = RandNbrs[RightPtr]; //
After pointers cross, fill
RandNbrs[RightPtr] = Pvalue;
//
left end and middle hole.
// All values to the left of RandNbrs[RightPtr] are
<= Pvalue while all to
// the right are >= Pvalue. Next, test the 2
subgroups on either side to
// see if they are still larger than the minimum
efficient size. If both
// are still too large, then place the larger one on the
stack and
// partition the smaller. If only one needs
partitioning, then partition
// it, otherwise get the left and right ends of a
subgroup stored on the
// stack in an earlier operation.
// Move RightPtr into
RightPtr--;
//
unsorted left subgroup
if
(RightPtr < MidPtr) {
// If left
SubGroup is smaller
if (RightPtr - LeftEnd > MinGroup)
{ // If both are large then put
QSleft[StackPtr] =
LeftPtr;
// right side on the stack
QSright[StackPtr] =
RightEnd; // and
sort the left side.
RightEnd = RightPtr;
++StackPtr;
//
Ready for next subgroup
}
else if (RightEnd - LeftPtr > MinGroup) //
Else if just have to
LeftEnd = LeftPtr;
// sort the right side
else {
// Else neither gets sorted. Get a
LeftEnd =
QSleft[--StackPtr];
// prior subgroup from the stack.
RightEnd =
QSright[StackPtr];
// (Will be garbage if all
}
// subgroups are
sorted)
}
// End of "if left is smaller"
else
{
// Else left side is larger
if (RightEnd - LeftPtr > MinGroup)
{ // If both sides are large
QSleft[StackPtr] =
LeftEnd;
// then put left side on
QSright[StackPtr] =
RightPtr; // the
stack
LeftEnd = LeftPtr;
// and sort the right side
++StackPtr;
// Ready for next subgroup
}
else if (RightPtr - LeftEnd > MinGroup) //
else if left side is
RightEnd = RightPtr;
// too large, then sort it.
else {
// Else neither gets sorted. Get a
LeftEnd =
QSleft[--StackPtr];
// prior subgroup from the stack
RightEnd =
QSright[StackPtr];
// (Will be garbage if all
}
// subgroups are
sorted).
}
// End of "if left is larger"
}
// Repeat until all
subgroups are
// small.
// Finish up with "Insertion Sort"
for (i = 2; i
<= Nbr2Sort; i++) {
k =
i;
j = i
- 1;
temp
= RandNbrs[k];
while
(RandNbrs[j] > temp) {
RandNbrs[k] = RandNbrs[j];
j--;
k--;
}
RandNbrs[k] = temp;
}
Time2 =
(double)clock();
Time2 =
Time2/(double)CLOCKS_PER_SEC;
printf("\nThe
time to sort %'d items via QuickSort was %g seconds.\n",
Nbr2Sort, Time2 - Time1);
PauseMsg();
puts("\nDo you
want to see the random numbers after they were sorted (Y or
N)?");
gets(Databuff);
if
(tolower(Databuff[0]) == 'y') {
for
(i = 1; i <= Nbr2Sort; i++) {
printf("%4d %'16u\n", i, RandNbrs[i]);
if (!(i%20))
PauseMsg();
}
PauseMsg();
}
}
//***************
******************* *************************
******************
//
//
ShellSort
//
// If you are only
going to learn how one sorting algorithm works, concentrate
// on Shell Sort.
On most arrays that you are ever going to work with it is
// nearly as fast
as Quicksort, and it is much easier to code
// (and
understand).
//
// Shell Sort is
based on Insertion Sort. In fact, if you are working on a
// very small
group of numbers, it is exactly the same as Insertion Sort. If
// you are sorting
a larger group, then it is just an expansion of Insertion
// Sort. Shell
Sort breaks down large arrays into a series of small sections
// which it then
treats as though they were "Insertion Sort".
//
//
//
--------------------------
-------------------------------------
// RandNbrs | A |
B | C | D | A | B | C | D | A | B | C | D | A | B | C |
D |
//
--------------------------
-------------------------------------
//
1 2 3 4
5 6 7 8
9 10 11 12 13 14
15 16
//
// For example,
let's assume you are going to sort 16 elements and are using
// the 1, 4, 13,
etc. sequence for "gap" sizes. The algorithm will first use a
// "Gap" size of
4. It will run 4 separate simultaneous "Insertion Sorts". One
// of these will
be an "Insertion Sort" on the numbers in the "A" positions.
// The other 3
"Insertion Sorts" will use the "B", "C", and "D" groups. When
// the "Gap = 4"
process is finished, the array will be roughly sorted with
// most of the
small value numbers concentrated near the low end of the
array.
// Similarly, most
of large value numbers will be concentrated near the high
// end of the
array. The algorithm then continues with a "Gap" size of 1.
This
// is essentially
an ordinary "Insertion Sort", and "Insertion Sort" is very
// efficient on
arrays that are nearly sorted.
//
// If the number
of elements to be sorted is still larger, then the elements
// are initially
sorted using a "gap" size of 13. This is followed by a "gap"
// of 4 (as above)
and finally a "gap" of 1. Still larger lists that are to be
// sorted will use
larger initial "Gap" sizes, and then gradually decrease the
// "Gap" size as
progress is made.
//
// The efficiency
of Shell Sort can be fine tuned by changing the sequence of
// "gap" sizes.
The 1, 4, 13, 40... series was originally sugested by Knuth.
// Two other
series are better candidates for the gap sizes. The user can
// experiment with
a choice of:
// 1) 1, 3,
7, 21, 48...
// 2) 1, 4,
13, 40...
// 3) 1, 8,
23, 77, 281, 1073...
//
//****************
*********************** ***********************
***************
void ShellSort(void) {
int choice, i, j,
k, GapPtr, Gap;
unsigned temp;
double Time1,
Time2;
puts("\nYou may
experiment with the gap sizes that are used in Shell Sort");
puts("\nPick one
of the following sequences for the gap size.");
puts("(Any other
number cancels Shell Sort)\n");
puts("1) 1,
3, 7, 21, 48, 112,...");
puts("2) 1,
4, 13, 40, 121, 364,...");
puts("3) 1,
8, 23, 77, 281, 1073,...");
gets(Databuff);
choice =
atoi(Databuff);
if (choice == 1)
{
// Copy one of the
three gap sequences
for
(i = 1; i <= 20; i++)
Gaps[i] = Gaps13[i];
}
else if (choice
== 2) {
for
(i = 1; i <= 20; i++)
Gaps[i] = Gaps14[i];
}
else if (choice
== 3) {
for
(i = 1; i <= 20; i++)
Gaps[i] = Gaps18[i];
}
else return;
puts("\nStarting
Shell Sort");
Time1 =
(double)clock();
// Save time to 0.001 sec.
Time1 =
Time1/(double)CLOCKS_PER_SEC;
temp =
Nbr2Sort/3;
// Set up
GapPtr
for (GapPtr = 1;
Gaps[GapPtr] < temp; GapPtr++);
GapPtr--;
Gap =
Gaps[GapPtr];
do {
for
(i = Gap + 1; i <= Nbr2Sort; i++) {
temp = RandNbrs[i];
k = i;
for (j = i - Gap; j > 0; j -= Gap) {
if (RandNbrs[j] <= temp)
break;
RandNbrs[k] = RandNbrs[j];
k = j;
}
RandNbrs[k] = temp;
}
} while (Gap =
Gaps[--GapPtr]);
Time2 =
(double)clock();
Time2 =
Time2/(double)CLOCKS_PER_SEC;
printf("\nThe
time to sort %'d items via Shell Sort was %g seconds.\n",
Nbr2Sort, Time2 - Time1);
PauseMsg();
puts("\nDo you
want to see the random numbers after they were sorted (Y or
N)?");
gets(Databuff);
if
(tolower(Databuff[0]) == 'y') {
for
(i = 1; i <= Nbr2Sort; i++) {
printf("%4d) %'16u\n", i, RandNbrs[i]);
if (!(i%20))
PauseMsg();
}
PauseMsg();
}
}
//***************
******************** ****************** **************
//
//
Misc routines
//
//***************
******************** ****************** **************
void PauseMsg(void) {
puts("\nPress
RETURN to continue.");
gets(Databuff);
}