Week 5 - Monday. What did we talk about last time? Linked list implementations Stacks Queues.

Post on 12-Jan-2016

235 views 0 download

Transcript of Week 5 - Monday. What did we talk about last time? Linked list implementations Stacks Queues.

CS221Week 5 - Monday

Last time

What did we talk about last time? Linked list implementations

Stacks Queues

Questions?

Infix to Postfix Converter

Project 2

Assignment 2 Post-Mortem

Project 1 loops

For some reason, people like to have crazy loops:

For this (relatively simple) case, it's not terrible, but the more complex it gets, the harder it is to think about indexes that jump around

Even so, what do i and j mean in this case?

for( int i = 0; i < height; ++i )for( int j = 0; j < width*3; j += 3 ) {

data[i][j] = 255 – data[i][j]);data[i][j + 1] = 255 – data[i][j + 1];data[i][j + 2] = 255 – data[i][j + 2];

}

Project 1 loops continued I like to keep my for loop indexes simple,

because loops are hard to thinking about Written this way, my indexes are rows and

columns:for( int row = 0; row < height; ++row )

for( int column = 0; column < width; ++column ) {data[row][3*column] =

255 – data[row][3*column]);data[row][3*column + 1] =

255 – [row][3*column + 1];data[row][3*column + 2] =

255 – [row][3*column + 2];}

Project 1 loops continued

Or even better:

I don't object to i and j, but a loop index should mean something

for( int row = 0; row < height; ++row )for( int column = 0; column < width; ++column )

for( int color = 0; color < 3; ++color )data[row][3*column + color] = 255 – data[row][3*column + color]);

Recursion

What is Recursion?

Defining something in terms of itself

To be useful, the definition must be based on progressively simpler definitions of the thing being defined

Bottom Up

It is possible to define something recursively from the bottom up

We start with a simple pattern and repeat the pattern, using a copy of the pattern for each part of the starting pattern

Top Down

Explicitly: n! = (n)(n – 1)(n – 2) … (2)(1)Recursively: n! = (n)(n – 1)! 6! = 6 ∙ 5!

5! = 5 ∙ 4!▪ 4! = 4 ∙ 3!▪ 3! = 3 ∙ 2!

2! = 2 ∙ 1! 1! = 1

6! = 6 ∙ 5 ∙ 4 ∙ 3 ∙ 2 ∙ 1 = 720

Examples in Acronyms

PHP PHP: Hypertext Processor▪ (PHP: Hypertext Processor): Hypertext

Processor▪ …

XINU XINU Is Not Unix▪ (XINU Is Not Unix) Is Not Unix▪ …

Useful Recursion

Two parts: Base case(s)

Tells recursion when to stop For factorial, n = 1 or n = 0 are

examples of base cases Recursive case(s)

Allows recursion to progress "Leap of faith" For factorial, n > 1 is the recursive case

Solving Problems with Recursion

Approach for Problems

Top down approach Don’t try to solve the whole problem Deal with the next step in the

problem Then make the "leap of faith" Assume that you can solve any

smaller part of the problem

Problem

ProblemProblemProblem

Walking to the Door

Problem: You want to walk to the door

Base case (if you reach the door): You’re done!

Recursive case (if you aren’t there yet): Take a step toward the door

Problem

Implementing Factorial

Base case (n 1): 1! = 0! = 1

Recursive case (n > 1): n! = n(n – 1)!

Code for Factorial

public static long factorial( int n ){if( n <= 1 )

return 1;else

return n*factorial( n – 1 );}

Base Case

RecursiveCase

Count the Zeroes

Given an integer, count the number of zeroes in its representation

Example: 13007804 3 zeroes

Recursion for Zeroes

Base cases (number less than 10): 1 zero if it is 0 No zeroes otherwise

Recursive cases (number greater than or equal to 10): One more zero than the rest of the

number if the last digit is 0 The same number of zeroes as the rest

of the number if the last digit is not 0

Code for Zeroes

public static int zeroes( int n ){if( n == 0 )

return 1;else if( n < 10 )

return 0;else if( n % 10 == 0 )

return 1 + zeroes( n / 10 );else

return zeroes( n / 10 );}

Base Cases Recursive

Cases

Searching in a Sorted Array

Given an array of integers in (ascending) sorted order, find the index of the one you are looking for

Useful problem with practical applications

Recursion makes an efficient solution obvious

Play the High-Low game

Recursion for Binary Search Base cases:

The number in the middle of the range is the one you are looking for. Return its index.

The number isn’t in the range you are looking at. Return -1.

Recursion cases: The number in middle of the range is too

high. Look in the range below it. The number in the middle of the range is too

low. Look in the range above it.

Code for Binary Search

public static int search( int[] array, int n, int start, int end )

{int midpoint = (start + end)/2;if( array[midpoint] == n )

return midpoint;else if( start >= end )

return -1;else if( array[midpoint] < n )

return search( array, n, midpoint + 1, end );

elsereturn search( array, n, start,

midpoint );}

BaseCases

RecursiveCases

Time for Binary Search

Each recursive call splits the range in half In the worst case, we will have to keep

splitting the range in half until we have a single number left

We want to find the number of times that we have to multiply n by ½ before we get 1 n(½)x = 1 n = 2x

x = log2(n)

How Does it Work Inside The Computer?Pay no attention to the man behind the curtain…

All this math is great, but…

How does it actually work inside a computer?

Is there a problem with calling a function inside the same function?

How does the computer keep track of which function is which?

The Stack

As you know, a stack is a FILO data structure used to store and retrieve items in a particular order

Just like a stack of blocks:

A A

BPush

A

B

CPush

A

BPop

Stack for Functions

In the same way, the local variables for each function are stored on the stack

When a function is called, a copy of that function is pushed onto the stack

When a function returns, that copy of the function pops off the stack

main main

solve

Call

main

solve

factorial

Call

main

solve

Return

Example with Factorial

Each copy of factorial has a value of n stored as a local variable

For 6! :

6*factorial(5)

5*factorial(4)

4*factorial(3)

3*factorial(2)

2*factorial(1)

1

x = factorial(6);factorial(6)

factorial(5)

factorial(4)

factorial(3)

factorial(2)

factorial(1)

720

120

24

6

2

1

Issues of Efficiency

When to use recursion?

Recursion is a great technique One of its strengths is in writing

concise code to solve a problem Some recursive solutions are very

efficient Some are not It pays to be aware of both

Summation

Find the sum of the integers 1 through n

Example: n = 8 sum(8) = 8 + 7 + 6 + 5 + 4 + 3 + 2

+ 1 sum(8) = 36

Recursion for Summing

Base case (n = 1):

Recursive case (n > 1):

1

1

1i

i

1

11

n

i

n

i

ini

Code for Summing

public static int sum( int n ){if( n == 1 )

return 1;else

return n + sum( n – 1 );}

Base Case

RecursiveCase

Why not recursion?

Recursive summing takes linear time (summing n takes n function calls)

Is there another way to find this sum?

Closed form equation

Constant time! Remember the story of young Gauss

n

i

nni

1 2)1(

Fibonacci

The sequence: 1 1 2 3 5 8 13 21 34 55… Studied by Leonardo of Pisa to model the

growth of rabbit populations

Fibonacci Problem

Find the nth term of the Fibonacci sequence

Simple approach of summing two previous terms together

Example: n = 71 1 2 3 5 8 131 2 3 4 5 6 7

Recursion for Fibonacci

Base cases (n = 1 and n = 2): Result = 1

Recursive case (n > 2): Result = fibonacci(n – 1) + fibonacci(n –

2)

Code for Fibonacci

public static int fib( int n ){if( n <= 2 )

return 1;else

return fib(n – 1) + fib(n – 2);

}

Base Case

RecursiveCase

What’s the running time for fib()?

Example: fib(6) fib(6)

fib(4)fib(5)

fib(4) fib(3)

fib(3) fib(2) fib(2) fib(1)

fib(2) fib(1)

fib(3) fib(2)

fib(2) fib(1)

Uh oh.

Exponential Time for fib

For most cases, calling fib() makes calls two more calls to fib(), which each make two more calls to fib(), and so on…

Many values are redundantly computed

The final running time is O(2n/2)

Can we do better?

The recursion is fine from a mathematical perspective

We just need to avoid recomputing lower terms in the sequence

We can use the idea of carrying along both the (n – 1) term and the (n – 2) term in each recursive step

Code for Better Fibonacci

public static int fib2( int a, int b, int n )

{if( n <= 2 )

return b;else

return fib2(b, a + b, n - 1);}

//proxy methodint fib( int n ){return fib2(1, 1, n);

}

Base Case

RecursiveCase

Exponentiation

We want to raise a number x to a power n, like so: xn

We allow x to be real, but n must be an integer greater than or equal to 0

Example: (4.5)13 = 310286355.9971923828125

Recursion for Exponentiation

Base case (n = 0): Result = 1

Recursive case (n > 0): Result = x ∙ x(n – 1)

Code for Exponentiation

public static double power( double x, int n )

{if( n == 0 )

return 1;else

return x * power( x, n – 1);}

Base Case

RecursiveCase

Running time for power

Each call reduces n by 1n total calls What's the running time?

O(n)

Can we do better?

We need to structure the recursion differently

Instead of reducing n by 1 each time, can we reduce it by a lot more?

It’s true that xn = x ∙ x(n – 1)

But, it is also true that xn = x(n/2) ∙ x(n/2)

New Recursion for Exponentiation

Assume that n is a power of 2 Base case (n = 1):

Result = x Recursive case (n > 1):

Result = (x(n/2))2

Code for Better Exponentiation

public static double power2( double x, int n )

{double temp;if( n == 1 )

return x;else{

temp = power2( x, n/2 );return temp * temp;

}}

Base Case

RecursiveCase

Running time for power2

Each call reduces n by half log2(n) total calls Just like binary search Can we expand the algorithm to

even and odd values of n?

Even Newer Recursion for Exponentiation

Base case (n = 1): Result = x

Recursive cases (n > 1): If n is even, result = (x(n/2))2

If n is odd, result = x ∙ (x((n – 1)/2))2

Code for Even Better Exponentiation

public static double power3( double x, int n ){double temp;if( n == 1 )

return x;else if( n % 2 == 0 ){

temp = power3( x, n/2 );return temp * temp;

}else{

temp = power3( x, (n – 1)/2 );return x * temp * temp;

}}

Base Case

RecursiveCases

Running time for power3

Each call reduces n by half (more or less) O(log2 n) total calls Does as well as power2() Better yet, we can use this solution to get

a logarithmic time answer for Fibonacci!

The nth term of the Fibonacci sequence is:

Where

Upcoming

Next time…

Review for Exam 1

Reminders

Study for Exam 1 This Friday, September 25, in class

Start working on Project 2 Sign up for new teams on Canvas

immediately! You can't partner with someone you've

partnered with before I will start docking points if you haven't

picked your teams by Friday