290 likes | 421 Views
Recursion -Data. Recursion -Data. Recursion -Data. Recursion -Data. Recursion -Data. Recursion - Control. Given recursive data (types), we can define operations also recursively! Consider computing the length of a sequential access list: Length of an empty list is 0.
E N D
Recursion - Control • Given recursive data (types), we can define operations also recursively! • Consider computing the length of a sequential access list: • Length of an empty list is 0. • Length of a non-empty list ( contains at least one element) is 1 more than the length of the list from which this was obtained by adding an element. Length(L) =Length(prevList) +1
Recursive Invocation of Functions len=0 Is list empty? Stop yes no len=len+1 List=delete(List)
Link ls; … int len=0; while (ls!=NULL) { len++; ls = ls->next; } Define function f: int f (Link ls) { if (ls == NULL) return 0; else return 1 + f(ls->next); } Recursive Invocation of Functions
Correctness Arguments • For the recursive version: • Prove hypothesis is true for base case ( say, list is empty) • Prove hypothesis is preserved for one recursive / / inductive step ( f(ls) = 1 + f(ls->next) ) • It is proved (by Induction) that the hypothesis is true for this function definition.
More examples • Is length the only thing we can define recursively? • No. For instance, • Addition / insertion is built into the definition. • More complicated examples? How about sorting?
Insert AN-1 into sorted list Insertion Sort – Top Down Design Sort list: A0 to AN-1 Sort list: A0 to AN-2 Sort list: A0 to AN-3 …
Insertion Sorting - Algorithm • Recursive version of insertion sorting: • void isort(Element ls[], int size) • { • if (size>1) { • isort(ls, size-1); • insert(ls[size-1], ls, size-1); • } • }
Insertion Sorting - Correctness • insert is order-preserving: • If ls is an ordered list (of size n-1) then insert(e, ls, n-1) returns an ordered list (of size n). • Inductive argument: • Basis: Empty list (size==0) is sorted. • Hypothesis: isort(ls, n-1) returns a sorted list • Step: insert(e, isort(ls, n-1), n) returns a sorted list by hypothesis and by the “order-preserving” property of insert.
Insertion Sorting - Complexity • Let time taken by isort(ls,N) be Tisort(N). • Then Tisort(N) can be broken into (for N>0): • Time taken by isort(ls, N-1): Tisort(N-1) • Time taken by insert(e, ls, N-1): N-1 • Thus Tisort(N) = Tisort(N-1) + N-1 • This is called a recurrence relation or recurrence equation. We solve it by simple substitution: Tisort(N) = Tisort(N-1) + N-1 = Tisort(N-2) + N-2 + N-1 = … = Tisort(0) + 1 + 2 + … + N-1 =O(N2) = because Tisort(0) = 1
Merge Sorting – Top Down Design Sort list: A[0] to A[N-1] M Sort list: A[0] to A[N/2] Sort list: A[N/2+1] to A[N-1] M Sort list: A[0] to A[N/4 ] Sort A[N/4 +1] to A[N/2] M …
mid = (st+end)/2; msort(ls, st, mid); msort(ls, mid+1, end); mergeIn(ls, st, mid+1, end); Merge Sorting – Algorithm void msort(Element ls[], int st, int end) { if (st >= end) return; else { } }
Merge Sorting - Correctness • merge is order-preserving: • If l1,l2 are ordered lists. then merge(l1,l2) returns an ordered list • Inductive argument: • Basis: Empty list & Singleton list are sorted. • Hypothesis: msort(l1,s1,e1) returns a sorted list for (e1-s1+1)<=n/2 • Step: merge(l1, l2) where l1 and l2 are the results of msort returns a sorted list by hypothesis and by the “order-preserving” property of merge.
Merge Sorting – Complexity • Tmsort(N) = 2*Tmsort(N/2) + N ; when N > 0 • Tmsort(N) = 2*Tmsort(N/2) + N = 4*T(N/4) + 2*N/2 + N = 8*T(N/8) + 4*N/4 + 2*N /2 + N = 2k * T(N/2k) + k*N = N * T(1) + k*N (when N is close to 2k) = (k+1)*N) (since T(1) = 1) = O(N*logN) (since k = log2N)
recursion …only on lists? • No! It can be used on any inductive structure. • Consider natural numbers: • 0 is a natural number • n+1 is a natural number if n is a natural number • Consider any operation on natural numbers: • Factorial of 0 is 1. • Factorial of n+1 is n+1 times Factorial of n. • This leads to natural definition of factorial: • int fact(int n) { return (n==0) ? 1 : n*fact(n-1); }
The #define Preprocessor Directive: Macros • #undef • Undefines a symbolic constant or macro • If a symbolic constant or macro has been undefined it can later be redefined
Conditional Compilation • Conditional compilation • Control preprocessor directives and compilation • Cast expressions, sizeof, enumeration constants cannot be evaluated in preprocessor directives • Structure similar to if #if !defined( NULL ) #define NULL 0 #endif • Determines if symbolic constant NULL has been defined • If NULL is defined, defined( NULL ) evaluates to 1 • If NULL is not defined, this function defines NULL to be 0 • Every #if must end with #endif • #ifdef short for #if defined( name ) • #ifndef short for #if !defined( name )
Conditional Compilation • Other statements • #elif– equivalent of else if in an if structure • #else– equivalent of else in an if structure • "Comment out" code • Cannot use /* ... */ • Use #if 0 code commented out #endif • To enable code, change 0 to 1
Conditional Compilation • Debugging #define DEBUG 1 #ifdef DEBUG cerr << "Variable x = " << x << endl;#endif • Defining DEBUG to 1 enables code • After code corrected, remove #define statement • Debugging statements are now ignored
Assertions • assert macro • Header <assert.h> • Tests value of an expression • If 0 (false) prints error message and calls abort • Example: assert( x <= 10 ); • If NDEBUG is defined • All subsequent assert statements ignored #define NDEBUG