Revision as of 20:41, 13 May 2024 by Admin (Created page with "One possible solution , after reading online descriptions of recursive merge sort, e.g. Dasgupta : <syntaxhighlight lang="C"> // to compile: gcc -Wall rmergesort.c -lm -o rmergesort /* ============================================================================ Name : rmergesort.c Author : Anon Version : 0.1 Copyright : (C)2013 under CC-By-SA 3.0 License Description : Recursive Merge Sort, Ansi-style =============================================...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Exercise


ABy Admin
May 13'24

Answer

One possible solution , after reading online descriptions of recursive merge sort, e.g. Dasgupta :

// to compile: gcc -Wall rmergesort.c -lm -o rmergesort

/*
 ============================================================================
 Name        : rmergesort.c
 Author      : Anon
 Version     : 0.1
 Copyright   : (C)2013 under CC-By-SA 3.0 License
 Description : Recursive Merge Sort, Ansi-style
 ============================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//const int MAX = 200;
const int MAX = 20000000;

int *b;

int printOff = 0;

// this debugging print out of the array helps to show
// what is going on.
void printArray(char* label, int* a, int sz) {
	int h = sz/ 2;
	int i;

	if (printOff) return;

	printf("\n%s:\n", label);

	for (i = 0; i < h; ++i ) {

		printf("%d%c", a[i],
					// add in a newline every 20 numbers
					( ( i != 0 && i % 20 == 0 )? '\n': ' ' ) );
	}

	printf(" | ");
	for (;i < sz; ++i) {
		printf("%d%c", a[i],
					( ( i != 0 && i % 20 == 0 )? '\n': ' ' ) );
	}

	putchar('\n');


}

void mergesort(int* a, int m ) {

	printArray("BEFORE", a, m);

	if (m > 2) {
		// if greater than 2 elements, then recursive
		mergesort(a, m / 2);
		mergesort(a + m / 2, m - m / 2);


	} else if (m == 2 && a[0] > a[1]) {
		// if exactly 2 elements and need swapping, swap
		int t = a[1];
		a[1] = a[0];
		a[0] = t;
		goto end;

	}

	// 1 or greater than 2 elements which have been recursively sorted

	// divide the array into a left and right array
	// merge into the array b with index l.

	int n = m/2;
	int o = m - n;

	int i = 0, j = n;
	int l = 0;
	// i is left, j is right ;
	// l should equal m the size of the array
	while (i < n) {
		if ( j >= m) {
			// the right array has finished, so copy the remaining left array
			for(; i < n; ++i) {
				b[l++] = a[i];
			}

		// the merge operation is to copy the smaller current element and
		// increment the index of the parent sub-array.
		} else if(  a[i] < a[j] ) {
			b[l++] = a[i++];
		} else {
			b[l++] = a[j++];
		}
	}

	while ( j < m) {
		// copy the remaining right array , if any
		b[l++] = a[j++];
	}

	memcpy(a, b, sizeof(int) * l );

end:
	printArray("AFTER", a, m);

	return;

}

void rand_init(int* a, int n) {
	int i;
	for (i = 0; i < n; ++i ) {

		a[i] = rand() % MAX;

	}
}

int main(void) {
	puts("!!!Hello World!!!"); /* prints !!!Hello World!!! */

//	int N = 20;
//    int N = 1000;
//    int N = 1000000;
	int N = 100000000;  // still can't make a stack overflow on ubuntu,4GB, phenom
	printOff = 1;

	int *a;

	b = calloc( N, sizeof(int));

	a = calloc( N, sizeof(int));

	rand_init(a, N);

	mergesort(a, N);

	printOff = 0;

	printArray("LAST", a, N);

	free(a);
	free(b);

	return EXIT_SUCCESS;
}


/* Having failed to translate my concept of non-recursive merge sort,
 * I tackled the easier case of recursive merge sort.
 * The next task is to translate the recursive version to a non-recursive
 * version. This could be done by replacing calls to mergesort, with
 * pushes onto a stack of
 * tuples of ( <array start address>, <number of elements to process> )
 */

/* The central idea of merging, is that two sorted lists can be
 * merged into one sorted list, by comparing the top of each list and
 * moving the lowest valued element onto the end of the new list.
 *  The other list which has the higher valued element keeps its top
 *  element unchanged. When a list is exhausted, copy the remaining other list
 *  onto the end of the new list.
 */

/* The recursive part, is to defer any work in sorting an unsorted list,
 * by dividing it into two lists until there is only 1 or two elements,
 * and if there are two elements, sort them directly by swapping if
 * the first element is larger than the second element.
 *
 * After returning from a recursive call, merge the lists, which will
 * begin with one or two element sorted lists. The result is a sorted list
 * which will be returned to the parent of the recursive call, and can
 * be used for merging.
 */

/* The following is an imaginary discussion about what a programmer
 * might be thinking about when programming:
 *
 * Visualising recursion in terms of a Z80 assembly language, which
 * is similar to most assembly languages, there is a data stack (DS) and
 * a call stack (CS) pointer, and each recursive call to mergesort
 * pushes the return address , which is the program address of the instruction
 * after the call , onto the stack pointed to by CS and CS is incremented,
 * and the address of the array start and integer which is the subarray length
 * onto the data stack pointed to by DS, which will be incremented twice.
 *
 * If the number of recursive , active calls exceed the allowable space for either the call stack
 * or the data stack, then the program will crash , or a process space protection
 * violation interrupt signal will be sent by the CPU, and the interrupt vector
 * for that signal will jump the processor's current instruction pointer to the
 * interrupt handling routine.
 */
00