Recursion

Recursive Definitions

     functions on natural numbers may be defined recursively

     value of function for n defined in terms of values for numbers < n

     “base” case defined for n = 0

     fibonacci numbers

   f(0) = f(1) = 1

   f(n) = f(n-1) + f(n-2) for n >= 2

     0! = 1,  n! = n* (n-1)! for n >= 1

Recursive Definitions

     other objects like sets and lists may also be defined recursively

     list of integers

   is either empty ( base case)

   is an ordered pair (i,l) where i is an integer and l is a list of integers ( i is the first element and l is the remaining list)

     all lists of integers can be constructed using this rule starting from the empty list

Recursive Computations

     computations on recursively defined objects  expressed using recursive subprograms

     a subprogram is recursive if it directly or indirectly calls itself (with different arguments)

     result of subprogram depends on the result of subprogram for other values (usually smaller) of its actual arguments

     “base case” needs to be defined when subprogram computes result directly without calling itself

List Insertion

     use recursive definition of list to define list insertion

     insert integer i in a list l, two possibilities

     i is the first element in new list, i.e. new list is the ordered pair (i,l)

     i is not the first element, l is not empty and is an ordered pair (j, l’)

     new list is (j,l”) where l” is obtained from l’ by inserting i

Recursive List Insertion

recursive subroutine insert(list,i)

! inserts integer i in list if not already present

! note that target of list may be changed

! type element should be made public to user

integer, intent(in) :: i

type(element), pointer :: list, temp

logical :: empty, first

empty = .not. associated(list)

if (.not. empty)  first = list%value > i

if (empty .or. first) then

temp => list

allocate(list)

list%value = i

list%next => temp

elseif( list%value < i) then

call insert(list%next,i) ! recursive call

end if

end subroutine insert

 

List Reversal

     reverse the order of elements in the list

     only pointer manipulations should be done

     elements should not be copied

     can be expressed more easily recursively

     recursive programs are often shorter, clearer, easier to correct and more elegant

     may be inefficient compared to non-recursive programs

     should not be used if efficiency is the main criterion

List Reversal

recursive subroutine reverse(list)

type(element), pointer :: list, temp

if (associated(list)) then

if (associated(list%next)) then

temp => list%next

call reverse(list%next)

temp%next => list

temp => list

list => list%next

nullify(temp%next)

endif

endif

end subroutine reverse

Sorting Lists

     same idea used for sorting a list of integers

     remove the first element, recursively sort the remaining list and insert the first element in the sorted list

     same as insertion sort

     get a faster sorting algorithm if we break the list into two lists of half the size, sort each half and combine sorted lists into one

     this algorithm is known as merge-sort and is one of the fastest sorting algorithms

Merge Sorting Lists

recursive subroutine merge_sort(list)

type(element), pointer :: list, temp, list1, list2

integer :: i, j

temp => list

i = 0

do

   if (.not. associated(temp)) exit

   temp => temp%next

   i = i+1

end do

if ( i <= 1) return

i = i/2

temp => list

do j = 1, i-1

temp => temp%next

end do

list1 => list

list2 => temp%next

nullify(temp%next)

call merge_sort(list1)

call merge_sort(list2)

call merge(list1,list2,list)

end subroutine merge_sort

Merging Sorted Lists

subroutine merge(list1, list2, list)

type(element), pointer :: list, list1, list2, temp

if (list1%value < list2%value) then

list => list1

list1 => list1%next

else

list => list2

list2 => list2%next

end if

temp => list

do

   if ((.not. associated(list1)) .or. (.not.associated(list2))) & exit

   if (list1%value < list2%value) then

      temp%next => list1

      temp => temp%next

      list1 => list1%next

   else

      temp%next => list2

      list2 => list2%next

      temp => temp%next

   end if

end do

if (.not. associated(list1)) then

temp%next => list2

else

temp%next => list1

end if

end subroutine merge

! time required for merge sort is proportional to nlog2n

! rather than n2 as merging and splitting require time

! proportional to n

 

Quick Sort

     quick sort is another sorting algorithm which when implemented properly gives the fastest known sorting algorithm for random inputs

     useful for sorting arrays rather than lists

     main idea – pick one element, put it in its correct position, all elements < than it to its left and others to its right

     recursively sort left and right halves

     expressed easily using recursion

Quick Sort

recursive subroutine quick_sort(a)

implicit none

integer, dimension(:), intent(inout) :: a

integer :: i,n

n = size(a)

if ( n > 1) then

call partition(a,i)

call quick_sort(a(:i-1))

call quick_sort(a(i+1:))

end if

contains

Partition

subroutine partition(a,j)

integer, dimension(:), intent(inout) :: a

integer,intent(out) :: j

integer :: i,temp

i = 1 ; j = size(a)

do

do

if ( i > j ) exit

if (a(i) > a(1)) exit

i = i+1

end do               

do

if ((j < i) .or. (a(j) <= a(1)))  exit

j = j-1

end do

if ( i >= j) exit

temp = a(i)

a(i) = a(j)

a(j) = temp

end do

temp = a(j) ; a(j) = a(1) ; a(1) = temp

end subroutine partition

end subroutine quick_sort