// example of merge sort where we allocate the working array on the stack |
#include <Arduino.h> |
#include <mem_syms.h> |
|
void merge(int16_t *Left, uint16_t Left_len, int16_t *Right, uint16_t Right_len, int16_t *S) { |
// position of next element to be processed |
uint16_t Left_pos = 0; |
uint16_t Right_pos = 0; |
|
// position of next element of S to be specified |
// note: S_pos = Left_pos+Right_pos |
uint16_t S_pos = 0; |
|
// false, take from right, true take from left |
uint8_t pick_from_left = 0; |
|
while ( S_pos < Left_len + Right_len ) { |
|
// pick the smallest element at the head of the lists |
// move smallest of Left[Left_pos] and Right[Right_pos] to S[S_pos] |
if ( Left_pos >= Left_len ) { |
pick_from_left = 0; |
} |
else if ( Right_pos >= Right_len ) { |
pick_from_left = 1; |
} |
else if ( Left[Left_pos] <= Right[Right_pos] ) { |
pick_from_left = 1; |
} |
else { |
pick_from_left = 0; |
} |
|
if ( pick_from_left ) { |
S[S_pos] = Left[Left_pos]; |
Left_pos++; |
S_pos++; |
} |
else { |
S[S_pos] = Right[Right_pos]; |
Right_pos++; |
S_pos++; |
} |
|
} |
} |
|
|
// sort in place, i.e. A will be reordered |
void merge_sort(int16_t A[], uint16_t A_len) { |
|
// see if we have enough space to sort, keep 128 bytes for working, |
// maybe that's enough! |
|
if ( AVAIL_MEM < A_len * sizeof(int16_t) + 128 ) { |
Serial.print("ACK, no stack available for sorting! Only have "); |
Serial.println(AVAIL_MEM); |
while ( 1 ) {} |
} |
|
if ( A_len < 2 ) { |
return; |
} |
|
if ( A_len == 2 ) { |
if ( A[0] > A[1] ) { |
int16_t temp = A[0]; |
A[0] = A[1]; |
A[1] = temp; |
} |
return; |
} |
|
// split A in half, sort left, sort right, then merge |
// A[0], ..., A[split_point-1] |
uint16_t split_point = A_len / 2; |
|
merge_sort(A, split_point); |
|
merge_sort(A+split_point, A_len-split_point); |
|
{ |
// Now allocate our working array on the stack |
int16_t S[A_len]; |
|
merge(A, split_point, A+split_point, A_len-split_point, S); |
|
// and copy back into the original array |
for (uint16_t i=0; i < A_len; i++) { |
A[i] = S[i]; |
} |
|
// at this point S should be released from the stack |
} |
|
} |
|
void setup() { |
Serial.begin(9600); |
|
Serial.println("********* THIS IS THE BEGINNING *********"); |
randomSeed(analogRead(0)); |
|
uint16_t Test_len = 64; |
int16_t Test[Test_len]; |
|
Serial.print("In: "); |
for (uint16_t i=0; i < Test_len; i++) { |
Test[i] = random(0, 100); |
if ( 1 ) { |
Serial.print(Test[i]); |
Serial.print(" "); |
} |
} |
Serial.println(); |
|
merge_sort(Test, Test_len); |
|
if ( 1 ) { |
Serial.print("Out: "); |
for (uint16_t i=0; i < Test_len; i++) { |
if ( i < Test_len-1 && Test[i] > Test[i+1] ) { |
Serial.print("Out of order!!"); |
} |
|
Serial.print(Test[i]); |
Serial.print(" "); |
} |
Serial.println(); |
} |
} |
|
void loop() { |
} |
// example of merge sort where we allocate the working array with malloc |
#include <Arduino.h> |
#include <mem_syms.h> |
|
void merge(int16_t *Left, uint16_t Left_len, int16_t *Right, uint16_t Right_len, int16_t *S) { |
// position of next element to be processed |
uint16_t Left_pos = 0; |
uint16_t Right_pos = 0; |
|
// position of next element of S to be specified |
// note: S_pos = Left_pos+Right_pos |
uint16_t S_pos = 0; |
|
// false, take from right, true take from left |
uint8_t pick_from_left = 0; |
|
while ( S_pos < Left_len + Right_len ) { |
|
// pick the smallest element at the head of the lists |
// move smallest of Left[Left_pos] and Right[Right_pos] to S[S_pos] |
if ( Left_pos >= Left_len ) { |
pick_from_left = 0; |
} |
else if ( Right_pos >= Right_len ) { |
pick_from_left = 1; |
} |
else if ( Left[Left_pos] <= Right[Right_pos] ) { |
pick_from_left = 1; |
} |
else { |
pick_from_left = 0; |
} |
|
if ( pick_from_left ) { |
S[S_pos] = Left[Left_pos]; |
Left_pos++; |
S_pos++; |
} |
else { |
S[S_pos] = Right[Right_pos]; |
Right_pos++; |
S_pos++; |
} |
|
} |
} |
|
|
// sort in place, i.e. A will be reordered |
void merge_sort(int16_t A[], uint16_t A_len) { |
|
// see if we have enough space to sort, keep 128 bytes for working, |
// maybe that's enough! |
|
if ( AVAIL_MEM < A_len * sizeof(int16_t) + 128 ) { |
Serial.print("ACK, no stack available for sorting! Only have "); |
Serial.println(AVAIL_MEM); |
while ( 1 ) {} |
} |
|
if ( A_len < 2 ) { |
return; |
} |
|
if ( A_len == 2 ) { |
if ( A[0] > A[1] ) { |
int16_t temp = A[0]; |
A[0] = A[1]; |
A[1] = temp; |
} |
return; |
} |
|
// split A in half, sort left, sort right, then merge |
// A[0], ..., A[split_point-1] |
uint16_t split_point = A_len / 2; |
|
merge_sort(A, split_point); |
|
merge_sort(A+split_point, A_len-split_point); |
|
{ |
// Now allocate our working array on the heap |
int16_t *S = (int16_t *) malloc( A_len * sizeof(int16_t) ); |
|
if ( ! S ) { |
Serial.println("ARRGH, No heap memory left."); |
while(1) { } |
} |
|
merge(A, split_point, A+split_point, A_len-split_point, S); |
|
// and copy back into the original array |
for (uint16_t i=0; i < A_len; i++) { |
A[i] = S[i]; |
} |
|
// at this point S can be released from the heap |
free(S); |
} |
|
} |
|
void setup() { |
Serial.begin(9600); |
|
Serial.println("********* THIS IS THE BEGINNING *********"); |
randomSeed(analogRead(0)); |
|
uint16_t Test_len = 64; |
int16_t Test[Test_len]; |
|
Serial.print("In: "); |
for (uint16_t i=0; i < Test_len; i++) { |
Test[i] = random(0, 100); |
if ( 1 ) { |
Serial.print(Test[i]); |
Serial.print(" "); |
} |
} |
Serial.println(); |
|
merge_sort(Test, Test_len); |
|
if ( 1 ) { |
Serial.print("Out: "); |
for (uint16_t i=0; i < Test_len; i++) { |
if ( i < Test_len-1 && Test[i] > Test[i+1] ) { |
Serial.print("Out of order!!"); |
} |
|
Serial.print(Test[i]); |
Serial.print(" "); |
} |
Serial.println(); |
} |
} |
|
void loop() { |
} |
#include <bassert.h> |
#include <mem_syms.h> |
|
int * merge(int *list1, int len1, int *list2, int len2) { |
|
/* |
Pre: list list1 of length len1, list list2 of length len2 |
both sorted in ascending order. |
|
Post: returns a pointer to a list (on heap) of length |
len1+len2 in increasing order resulting from the ordered |
merge of len1 and len2 |
|
Invariant: list list1 and list2 are not modified. |
|
Problem: who is responsible for freeing the merged list later when it is |
not needed? |
*/ |
|
|
int lenr = len1 + len2; |
int *result; |
int next1 = 0; // index of next element to take from from list 1 |
int next2 = 0; // index of next element to take from from list 1 |
int nextr = 0; // index of next element to insert into the result list |
|
bassert( len1 > 0 || len2 > 0, 2); |
|
result = (int *) malloc( lenr * sizeof(int)); |
bassert(result != 0, 5); |
|
while ( len1 + len2 > 0 ) { |
// select smaller of next element of list1 or list2 if there is one |
|
|
byte take_from_1 = |
(len2 == 0) || (len1 > 0 && list1[next1] <= list2[next2]); |
|
if ( take_from_1 ) { |
result[nextr] = list1[next1]; |
len1--; |
next1++; |
} else { |
result[nextr] = list2[next2]; |
len2--; |
next2++; |
} |
nextr++; |
|
} |
return result; |
|
} |
|
int * mergesort(int *l, int len) { |
/* |
|
Pre: l is a list of len elements. |
|
Post: returns a pointer to a new list of len elements that is l sorted in |
ascending order. |
|
Invariant: list l is not modified. |
|
Problem: Since we never free the intermediate storage we use for |
the recursion we get a memory leak. Also, how much storage do we |
allocate? Each level of the recursion allocates n (across the |
width of the recursion) so we allocate an extra O(n log n) of space. |
*/ |
|
// split the list roughly in half |
if ( len <= 1 ) { return l; } |
int len1 = len / 2; |
return merge( |
mergesort(l, len1), len1, |
mergesort(l+len1, len-len1), len-len1); |
} |
|
|
void setup() { |
Serial.begin(9600); |
|
int len = 32; |
int l[32]; |
int *r; |
|
for (int i=0; i < len; i++ ) { l[i] = random(100); } |
|
Serial.println("In:"); |
for (int i=0; i < len; i++ ) { |
Serial.print(l[i]); Serial.print(" "); |
} |
Serial.println(""); |
|
Serial.println("Out:"); |
r = mergesort(l, len); |
for (int i=0; i < len; i++ ) { |
Serial.print(r[i]); Serial.print(" "); |
} |
|
} |
|
void loop() { |
} |
It has a serious problem. Since we never free the intermediate storage we use for the recursion we get a memory leak. And how much storage do we allocate? Each level of the recursion allocates n (across the width of the recursion) so we allocate an extra O(n log n) of space. We don't have this kind of space to waste on the Arduino.
#include <bassert.h> |
#include <mem_syms.h> |
|
void merge(int *lout, int *list1, int len1, int *list2, int len2) { |
/* |
|
Merge from 2 input lists into a user supplied output list. |
|
Pre: list list1 of length len1, list list2 of length len2 |
both sorted in ascending order. |
|
list lout is at least length len1+len2 |
|
Post: merges list1 and list2 by increasing order into lout, which will |
have len1+len2 elements |
|
Invariant: list1 and list2 are unchanged. |
|
This version solves the memory leak problem. It also uses pointer |
arithmetic which is faster than array indexing. |
*/ |
|
while ( len1 > 0 || len2 > 0 ) { |
|
// pick from list 1 if list 2 is empty, or list 1 < list 2 |
if ( (len2 == 0) || (len1 > 0 && *list1 <= *list2) ) { |
// note use of post increment, which performs the assignment, |
// *lout = *list1, // then increments the pointers |
*lout++ = *list1++; |
len1--; |
} else { |
*lout++ = *list2++; |
len2--; |
} |
} |
} |
|
/* |
pre: listin is a pointer to a list of len elements |
needs sufficient stack space to allocate a temporary list, plus |
work variables. |
|
post: the contents of listin will be rearranged to be sorted in |
ascending order. |
|
The merge sort works by breaking the input into blocks of length |
block_len, each of which is ordered. This is the source of the |
merger. |
|
Then adjacent pairs of blocks are merged into a block of size |
2*block_len that is placed in the destination. |
|
The source and destination are swapped and the process continues |
util the block_len is bigger than the list to be sorted. |
*/ |
|
void mergesort(int *listin, int len) { |
int block_len; // current length of merge block |
|
// listin and tmp alternate roles as source and destination |
// 0 => destination is tmp, 1 => destination is listin |
byte buf_parity; |
|
// a pair of adjacent blocks to be merged has a left and right block |
|
int *src; // current position of left array in source |
int *dst; // current position in destination |
|
int remain; // elements remaining to be processed |
int left_len; // length of left block to merge |
int right_len; // length of right block to merge |
int merged_len; // length of destination merge |
|
Serial.print("Avail mem: "); Serial.println(AVAIL_MEM); |
bassert( AVAIL_MEM > len * sizeof(*listin) + 200, 7); |
|
int tmp[len]; |
|
if ( len <= 1 ) { return; } |
|
/* go through the list working on blocks of length block_len = 2 ** k |
|
src - pointer to source data, in blocks of length block_len, except |
possibly for the last block which could be partial. |
|
dst - pointer to destination where merged blocks are placed |
|
buf_parity |
true => merging from listin into tmp |
false => merging from tmp into listin |
*/ |
|
// keep sorting so long as block size is less than list length |
// if = or greater, list is sorted |
buf_parity = 1; |
block_len = 1; |
|
while ( block_len < len ) { |
|
// merge from src into dst |
if ( buf_parity ) { |
src = listin; |
dst = tmp; |
} |
else { |
src = tmp; |
dst = listin; |
} |
|
// number of elements remaining to be merged |
remain = len; |
|
while ( remain > 0 ) { |
|
// merge the next two blocks together |
if ( remain <= block_len ) { |
// 1 block or less remaining |
left_len = remain; |
right_len = 0; |
} |
else if ( remain < block_len + block_len ) { |
// 1 full block, and a partial last block |
left_len = block_len; |
right_len = remain - block_len; |
} |
else { |
// 2 full blocks to merge |
left_len = block_len; |
right_len = block_len; |
} |
|
// left list starts at current src, right list at src+left_len, |
// right list ends at src+left_len+right_len -1 |
|
merged_len = left_len + right_len; |
|
merge(dst, src, left_len, src+left_len, right_len); |
|
// move along both src and desitination to next pair of blocks |
dst += merged_len; |
src += merged_len; |
remain = remain - merged_len; |
} |
|
// swap src and dst |
buf_parity = ! buf_parity; |
block_len = block_len + block_len; |
} |
|
// if sorted list is in tmp, copy back into input list |
if ( ! buf_parity ) { |
int tlen = len; |
src = tmp; |
dst = listin; |
while ( tlen-- ) { *dst++ = *src++; } |
} |
return; |
} |
|
unsigned long start_time; |
unsigned long stop_time; |
|
void start_clock() { |
start_time = micros(); |
} |
|
unsigned long int stop_clock() { |
unsigned long elapsed_time; |
stop_time = micros(); |
elapsed_time = stop_time - start_time; |
Serial.print("Elapsed time "); |
Serial.print(elapsed_time); |
Serial.println(" uS"); |
return elapsed_time; |
} |
|
byte is_sorted(int *l, int len) { |
for (int i=0; i < len-1; i++) { |
if ( l[i] > l[i+1] ) { return 0; } |
} |
return 1; |
} |
|
void display_list(int *l, int len) { |
for (int i=0; i < len; i++ ) { |
Serial.print(l[i]); Serial.print(" "); |
} |
} |
|
void setup() { |
Serial.begin(9600); |
|
for (int len = 100; len < 10000; len += 200) { |
Serial.print("Testing with len "); Serial.println(len); |
|
// make sure we have enough memory |
bassert(AVAIL_MEM > len * sizeof(int) + 256, 3); |
|
int l[len]; |
|
for (int i=0; i < len; i++ ) { l[i] = random(100); } |
|
Serial.println("In:"); |
// display_list(l, len); |
Serial.println(""); |
|
Serial.println("Out:"); |
|
start_clock(); |
mergesort(l, len); |
stop_clock(); |
if ( is_sorted(l, len) ) { |
Serial.println("Success: list is sorted"); |
} |
else { |
Serial.println("Error: list not sorted!"); |
display_list(l, len); |
} |
|
// display_list(l, len); |
} |
} |
|
void loop() { |
} |
/* |
Outline of Quick Sort |
*/ |
|
/* partition |
|
pre: list is an array of len elements. |
post: list is rearranged such that (pivot is a randomly chosen element) |
list[0] ... list[pivot-1] < list[pivot] |
list[pivot+1] ... list[len-1] > list[pivot] |
|
returns the index of the pivot. |
*/ |
|
int partition(int *list, int len) |
{ |
// select a pivot index and initalize the final_pivot index |
int final_pivot = 0; |
|
// move pivot index to end of the list |
|
// move items less than pivot_val to front of array and |
// keep advancing the final_pivot point |
|
// move pivot to final place |
|
// return the final_pivot index |
return final_pivot; |
} |
|
/* quicksort |
|
pre: list is an array of len elements. |
post: list is in sorted (ascending) order |
*/ |
|
void quicksort(int *list, int len) |
{ |
// If len is greater than one, recursively call quicksort |
} |
|
unsigned long start_time; |
unsigned long stop_time; |
|
void start_clock() { |
start_time = micros(); |
} |
|
unsigned long int stop_clock() { |
unsigned long elapsed_time; |
stop_time = micros(); |
elapsed_time = stop_time - start_time; |
Serial.print("Elapsed time "); |
Serial.print(elapsed_time); |
Serial.println(" uS"); |
return elapsed_time; |
} |
|
byte is_sorted(int *l, int len) { |
for (int i=0; i < len-1; i++) { |
if ( l[i] > l[i+1] ) { return 0; } |
} |
return 1; |
} |
|
void display_list(int *l, int len) { |
for (int i=0; i < len; i++ ) { |
Serial.print(l[i]); Serial.print(" "); |
} |
} |
|
void setup() { |
Serial.begin(9600); |
|
for (int len = 100; len < 3000; len += 200) { |
Serial.print("Testing with len "); Serial.println(len); |
|
int l[len]; |
|
for (int i=0; i < len; i++ ) { l[i] = random(100); } |
|
//Serial.println("In:"); |
//display_list(l, len); |
//Serial.println(""); |
|
start_clock(); |
quicksort(l, len); |
stop_clock(); |
|
//Serial.println("Out:"); |
//display_list(l, len); |
//Serial.println(""); |
|
if ( is_sorted(l, len) ) { |
Serial.println("Success: list is sorted"); |
} |
else { |
Serial.println("Error: list not sorted!"); |
} |
|
} |
} |
|
void loop() { |
} |
|
The completed code is