Part F - Refinements

Algorithms

Design procedures using selection and iteration constructs to solve a programming task
Design data collections using arrays to manage information efficiently

"Algorithms are essential to the way computers process data"
(Wikipedia, 2016)

The central aspect of solving a programming task is the design of an appropriate algorithm.  An algorithm is the set of rules that define the sequence of operations required to complete the task.  Examples of tasks that require algorithms include finding the elements in a list satisfying a specified condition, sorting the elements of a list in a specified order and mixing the elements of a list.  The design of an algorithm typically involves selections and iterations; in some cases, nested selections and nested iterations.  Often, there is more than one algorithm that solves the programming task.  Different algorithms exhibit different efficiencies.

This chapter introduces the implementations of a few more common algorithms.  We implement them in function form for use as black boxes in other applications.

Searching

Search algorithms finds the index of one or more array elements that satisfy a specified condition or set of conditions.  These algorithms work with key-value pairs.  Each key is unique while the values are not necessarily unique.

Two Algorithms

Unsorted Key Array

Given an unsorted key array, we start our search at the first element and progress through the array element by element until we find a match.  This algorithm involves an iteration and a selection.

 ``` // Find Unit Price // find.c #include // find returns the index of the first element in a[n] // that contains the value 'item'; -1 if not found // int find(int a[], int n, int item) { int i, rc = -1; for (i = 0; i < n; i++) if (item == a[i]) { rc = i; // save the index i = n; // stop the iteration  } return rc; } int main(void) { int i, item; int sku[] = { 2156, 4633, 3122, 5611}; double price[] = {12.34, 7.89, 6.56, 9.32}; const int n = 4; printf("SKU : "); scanf("%d", &item); i = find(sku, n, item); if (i >= 0 && i < n) printf("Price : \$%0.2lf\n", price[i]);  else printf("%d not in system\n", item); return 0; }``` ``` SKU : 4633 Price : \$7.89  ```

Note that we check the value returned by find() to ensure that it is within the bounds of the key array (that is, we check that it is not -1).

Sorted Key Array

Given a sorted key array, we start our search in the middle of the array and at each step discard the half that doesn't contain the search key.  Although this algorithm is slightly more complicated than the unsorted one, it is significantly faster, which is important with a large number of elements.

 ``` // Find Unit Price - Sorted Keys Ascending Order // find_ascend.c #include // find_ascend returns the index of the first element // in ascending order a[n] that contains the value 'item'  // returns -1 if not found // int find_ascend(int a[], int n, int item) { int i, low = 0, high = n - 1, rc = -1; do { i = (low + high) / 2; if (a[i] < item) low = i + 1; else if (a[i] > item) high = i - 1; else rc = i; } while (low <= high && rc == -1); return rc; } int main(void) { int i, item; int sku[] = { 2156, 3122, 4633, 5611}; double price[] = {12.34, 6.56, 7.89, 9.32}; const int n = 4; printf("SKU : "); scanf("%d", &item); i = find_ascend(sku, n, item); if (i >= 0 && i < n) printf("Price : \$%0.2lf\n", price[i]);  else printf("%d not in system\n", item); return 0; }``` ``` SKU : 4633 Price : \$7.89  ```

Masking algorithms distinguish certain array elements from all other elements.  The masking array is a parallel array with respect to the other arrays in the set.  The elements of the masking array are flags that identify inclusion or exclusion.

Consider the following program, which calculates the total purchase price for a set of products.  The user enters the sku for each product purchased and the quantity.  Some products attract HST (Harmonized Sales Tax), while others do not.  We store the skus, unit prices and tax status in three parallel arrays.  The tax status array is the masking array.  The user enters 0 to terminate input.

 ``` // Total Purchase Price // masking.c #include #define HST 0.13 int find(int a[], int n, int item) { int i, rc = -1; for (i = 0; i < n; i++) if (item == a[i]) { rc = i; i = n; } return rc; } int main(void) { int i, item, n; int sku[] = { 2156, 3122, 4633, 5611}; double price[] = {12.34, 6.56, 7.89, 9.32}; int tax[] = { 1, 0, 0, 1}; const int nitems = 4; double sum = 0.0; do { printf("SKU : "); scanf("%d", &item); if (item != 0) { i = find(sku, nitems, item); if (i >= 0 && i < nitems) { printf("NO : "); scanf("%d", &n); if (tax[i] == 1) sum += n * price[i] * (1.0 + HST);  else sum += n * price[i]; } else { printf("%d not in" " system\n", item); } } } while (item != 0); printf("Total is \$%0.2lf\n", sum); return 0; }``` ``` SKU : 2156 NO : 3 SKU : 3121 SKU not in system  SKU : 3122 NO : 2 SKU : 5611 NO : 1 SKU : 0 Total is \$65.48 ```

Sorting

Sorting algorithms rearrange the elements of an array according to a pre-defined rule.  Typically, this rule is ascending or descending order.  The sorting criterion may be numeric or based upon a collating sequence such as ASCII or EBCDIC.

The two simplest algorithms are

• selection sort
• bubble sort

Selection Sort

A selection sort selects a reference element and steps through the rest of the elements looking for any one with a value that does not meet the test condition.  If found, the algorithm swaps that element with the reference element.

The following program sorts the array in ascending order.  Starting with the first element in the array, it picks the first unsorted element as the reference element, swaps it with the smallest element in the unsorted part of the array, and iterates until it reaches the last element in the array.

 ``` // Selection Sort // selectionSort.c #include // selectSort sorts the elements of a[n] in ascending order // void selectSort(int a[], int n) { int i, j, m; int temp; for (i = 0; i < n; i++) { m = i; for (j = i + 1; j < n; j++) if (a[j] < a[m]) { m = j; } if (m != i) { temp = a[i]; a[i] = a[m]; a[m] = temp; } } } int main(void) { int i; int sku[] = {2156, 4633, 3122, 5611}; const int n = 4; selectSort(sku, n); for (i = 0; i < n; i++ ) printf("%6d\n", sku[i]); return 0; }``` ``` 2156 3122 4633 5611  ```

Bubble Sort

A bubble sort moves through the array element by element swapping elements if the next one does not satisfy the sort condition.  The algorithm repeats this process for each unsorted subset of the array starting with the first element.  The algorithm moves elements to their terminal positions just like bubbles rising through a liquid - hence the name bubble sort.

 ``` // Bubble Sort // bubbleSort.c #include // bubbleSort sorts the elements of a[n] in ascending order  // void bubbleSort(int a[], int n) { int i, j; int temp; for (i = n - 1; i > 0; i--) { for (j = 0; j < i; j++) { if (a[j] > a[j+1]) { temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } } } int main(void) { int i; int sku[] = { 2156, 4633, 3122, 5611}; const int n = 4; bubbleSort(sku, n); for (i = 0; i < n; i++ ) printf("%6d\n", sku[i]); return 0; }``` ``` 2156 3122 4633 5611  ```

Sorting Strings

The following program accepts a list of names, sorts them in alphabetic order and displays the sorted list:

 ``` // Sort a List of Names // sortNames.c #include #include #define MN 10 // maximum number of names in the list #define MC 30 // maximum number of characters in a name #define FM "30" // used in the format string of scanf() void bubble(char a[][MC+1], int size); int main(void) { char name[MN][MC+1]; int i, n, keepgoing; // Input the list of names printf("Enter names (^ to stop)\n"); i = 0; do { printf("? "); scanf(" %"FM"[^\n]", name[i]); keepgoing = strcmp(name[i], "^") != 0; i++; } while (keepgoing == 1 && i < MN); if (keepgoing == 1) n = MN; else n = i - 1; // sort the names bubble(name, n); // display thesorted list for (i = 0; i < n; i++) printf("%s\n", name[i]); return 0; } // bubbleSort sorts the elements of a[size] in ascending order  // void bubble(char a[][MC+1], int size) { int i, j; char temp[MC+1]; for (i = size - 1; i > 0; i--) { for (j = 0; j < i; j++) { if (strcmp(a[j],a[j+1]) > 0) { strcpy(temp, a[j]); strcpy(a[j], a[j+1]); strcpy(a[j+1], temp); } } } }```

Note the concatenation of string literals in the format string of the call to scanf().  This lets us set the maximum number of input characters alongside the maximum number of characters in the array of strings at the head of the program code.  The C compiler converts a concatenation of string literals into a single literal removing the intermediate pairs of double quotes; that is, to the compiler "a""b""c" is the same as "abc".

Mixing

Mixing algorithms have applications in games of chance.  Examples include shuffling the cards in a deck or tumbling numbered balls into a lottery chute.  The algorithm depends on the extent to which we seek to generate a truly fair result.

Consider the following program, which tumbles 10 balls into a lottery chute.  To simulate mixing, the algorithm picks the index of a reference element, randomly picks the index of another element further along in the array and swap the values stored in the two elements.  This algorithm is attributed to Donald Knuth, a pioneer of computer science.

 ``` // Mix Lottery Balls // mix.c #include #include #include // mix mixes the elements in a[n] randomly  // void shuffle(int a[], int n) { int i, left, j, temp; left = n; for (i = 0; i < n; i++) { j = i + rand() % left; temp = a[i]; a[i] = a[j]; a[j] = temp; left--; } } int main(void) { int i, n = 10; int ball[n]; for (i = 0; i < n; i++) ball[i] = i + 1; srand(time(NULL)); shuffle(ball, n); for (i = 0; i < n; i++) printf("%5d\n", ball[i]); return 0; }``` ``` 1 8 10  3 5 9 6 2 7 4```

Exercises

• Upgrade bubbleSort.c to sort unit prices alongside the key array
• Upgrade masking.c to keep track of the number of items in stock
• Complete the following practice problems