lecture 5 intro_method iterate recursion

Download Report

Transcript lecture 5 intro_method iterate recursion

Sound Check
Some
Final Words
on Methods
RECALL:
Three major
paradigms for
programming
Java Methods
There exists in Java a single construct, the method, for
both procedures and functions:
• when a procedure is called for, specify
the return type “void” before method name
public void printHelloWorld( )
{
Quick
Review
System.out.println(“Hello World!”);
} // of printHelloWorld
• Note: All methods must have parentheses
for parameters . . . even if no parameters!
Java Methods
Single construct for both procedures and functions:
• when a function is called for, specify the
appropriate return type before method name
public float average (float fNum1, float fNum2, float fNum3)
{
float fReturnVal;
fReturnVal =
(fNum1 + fNum2 + fNum3)/ 3;
return (fReturnVal);
} // of average
Quick
Review
Method Signatures
“The signature of a method consists of the name of the
method and the number and types of formal parameters
to the method. A class may not declare two methods
with the same signature, or a compile time error occurs.”
--Java Language Specification s.8.4.2
Method overloading occurs when identically named methods have
different signatures.
public int getCube(int iNum){
return iNum*iNum*iNum;
}
public int getCube(float fNum){
return (int)(fNum*fNum*fNum);
}
public int getCube(double dNum){
return (int) (dNum*dNum*dNum);
}
Quick
Review
Parameters
• Unlike Pseudocode, Java only has “in”
parameters.
• Thus no need to declare in, in/out or out
‘cause they’re all IN
• Parameters do need to be declared just
like variables:
public void demo(int i, float x)
• More about parameters when we discuss
objects
Where do Methods Go?
Methods (and variables) are contained in Classes, as either
class or instance members. More on creating classes and
objects shortly . . .
class zippy {
int pin;
public String yow() {
// some code
}
}
Quick
Review
Questions?
Iteration
Java Basics: Iteration Constructs
• In Pseudocode, we had a single iteration
construct, flexible enough to be used in all iteration
contexts.
Actually (if you were paying attention last
semester) we talked about three kinds of
loops:
Sentinal Loops
Test-Last Loop
N-and-a-half Loops
Java Basics: Iteration Constructs
• In Pseudocode, we had a single iteration
construct, flexible enough to be used in all iteration
contexts.
• Java, like most programming languages, does not
provide a single flexible construct.
• Instead, Java offers three special case loop
constructs, each good for a particular context.
• Do not get accustomed to only one of them and
try to use it for all situations.
• To use them wisely, you should consider not only
how they work, but also when each is best used…
Java Iteration Constructs: “While Loops”
When repeating steps, people naturally want to
follow the pattern:
get a value, then process that value
The while loop construct calls for the unnatural
pattern:
obtain the first loop control value before
entering the loop itself;
then, within the loop body,
first do the process steps,
then do the get next steps
Unnatural?
while(You haven’t gotten to my
street)
{
Keep going straight
}
turn right
Java Iteration Constructs: “While Loops”
Pseudocode:
Java example:
<get first value>
loop
exitif NOT(condition)
<process value>
<get next value>
endloop
<get first value>
while (condition)
{
<process value>
<get next value>
}
Pseudocode flavor: Sentinal
Java Iteration Constructs: “Do While Loops”
Pseudocode:
loop
statement 1
...
statement N
exitif (NOT(condition))
endloop
Pseudocode flavor: Test Last
Java example:
do
{
statement 1;
...
statement N;
} while (condition);
Java Iteration Constructs: “For Loops”
Java syntax:
for (<initialization>; <continue if>;<increment>)
Pseudocode:
i isoftype Num
i <- 0
loop
exitif (i =>10)
<some statements>
i <- i + 1
endloop
Java example:
int i;
for (i=0; i<10; i++)
{
<some statements>
}
Secret!
A for Loop can be exactly written as a
while loop
i = 0;
while (i < 10)
{
System.out.println(i);
i++;
}
for(i = 0; i < 10; i++)
{
System.out.println(i);
}
This will help you understand the
sequence of operations of a for loop
Java Iteration Constructs: “For Loops”
Common Problems with For Loops include:
for (i=0; i<N; i++);
{…}
--Spins on ending semicolon; code in
braces executed once!
for (int i=0; i<N; i++){…}
--variable declared inside for loop signature; caution:
--the variable may be needed outside the for loop
structure
Potential Confusion
public class Forex
{
static int i = 42;
public static void main(String args[])
{
for(int i=0; i < 5; i++)
{
System.out.println(i);
}
System.out.println(i);
}
}
Output
0
1
2
3
4
42
Java Iteration Constructs: When to Use
--The term “control variable” refers
to the variable whose value is tested
to determine if the loop should
continue for another iteration or halt.
--For example, variable thisVar,
below:
while (thisVar < = SOME_CONSTANT)
--To determine which loop construct
is appropriate for a given situation,
ask yourself “where does the control
variable’s value come from?”
ASK:
Is it simply a count
of the number of
iterations?
Is it a value that the
loop itself must
compute?
Is it a value that
already exists
somewhere, and the
loop only obtains it
from elsewhere?
Java Iteration Constructs: When to Use
The for loop: used when the control variable is a simple
count of the number of iterations,
e.g.: “create a loop that reads and processes
the next 100 numbers.”
The while loop: used when the control variable has a value
that already exists and is simply obtained by the loop.
e.g.: “create a loop that reads in numbers and
processes them until it reads in a 100.”
The do-while loop: used when the control variable’s value
must be calculated by the loop itself.
e.g.: “create a loop that reads in numbers
until their sum is greater than 100.”
Java Iteration Constructs: Review
Which loop construct would you use if...
You need to perform a series of steps
exactly N times?
You need to traverse a linked list of unknown
size, and stop when you find a certain value?
You need to perform a series of steps at least
once, and continue performing these steps for
an an unknown number of times
Iteration Reiteration
–Three kinds of iteration
– for (...;...;...) {...} loops
– Fixed number of iterations
– while (...) {...} loops
– Iteration condition evaluated in advance
– do {...} while (...) loops
– Iteration condition evaluated in the loop
Questions?
Arrays of Primitives
• The Idea:
Same concepts you know from Pseudocode
A few differences in implementation
• Java array declaration:
<elemType>[ ] <arrID> = new <elemType>[<size>];
e.g.: to declare an array of ten ints for storing numerical grades...
int[ ] iGradeArray = new int[10];
• Array declaration error:
using parentheses, not brackets, e.g.,
int[ ] iGradeArray = new int(10);
Details
• The odd looking syntax is because
arrays (even arrays of primitives) are
objects.
• We'll explain in detail once we get to
objects...
int[ ] iGradeArray = new int[10];
int iGradeArray[ ] = new int[10];
Arrays
Example:
• declare iGradeArray of 10 ints
• initialize all 10 values to 0
int[ ] iGradeArray = new int[10];
int i;
// when declaring and manipulating arrays,
// you may use the single-letter IDers
// i, j, k for indices due to convention
// (everybody knows what it is)
for (i=0; i < iGradeArray.length; i++)
iGradeArray[i] = 0;
} // for loop
{
Great idea!
if you change
the array size,
you need only
change the
instantiation.
Notes:
•Arrays know their own length
• length is a field, not a method
• Arrays are statically sized: you cannot change the length after declaration.
• All arrays are objects, thus you must declare a reference, and instantiate it, and initialize it
More Notes:
• Array indices begin at 0, not at 1
• So, length is one greater than iMAX_INDEX
• Thus, an error if you do:
Arrays
int[ ] iGradeArray = new int[10];
int i;
for (i=1; i <= iGradeArray.length; i++)
{
iGradeArray[i] = 0;
} // for loop
• Code above attempts to access elements 1..10
• But... you have indices 0..9
• So: it misses the 1st element (which is at index 0)
it tries to go past 10th element (which is at index 9)
Questions?
Sample Quiz Problem
Given an array of ints, return the index of the largest
element. Use the code below.
public int getLargestIndex(int[ ]
/* YOUR CODE GOES HERE */
}
iArray){
Sample Quiz Problem
Given an array of ints, return the index of the largest
element. Use the code below.
public int getLargestIndex(int[ ] iArray) {
int iLargest = -9999;
int iCounter;
for (iCounter = 0; iCounter < iArray.length;
iCounter++)
{
if (iLargest < iArray[iCounter])
iLargest = iArray[iCounter];
}
return iLargest;
}// getLargestIndex
Does this work?
Sample Quiz Problem
Given an array of ints, return the index of the largest
element. Use the code below.
public int getLargestIndex(int[ ] iArray) {
int iLargest = -9999;
int iCounter;
for (iCounter = 0; iCounter < iArray.length;
iCounter++)
{
if (iLargest < iArray[iCounter])
iLargest = iArray[iCounter];
}
return iLargest;
}// getLargestIndex
What if all the values
are less than -9999
Sample Quiz Problem
Given an array of ints, return the index of the largest
element. Use the code below.
public int getLargestIndex(int[ ] iArray) {
int iLargest = iArray[0];
int iCounter;
for (iCounter = 0; iCounter < iArray.length;
iCounter++)
{
if (iLargest < iArray[iCounter])
iLargest = iArray[iCounter];
}
return iLargest;
}// getLargestIndex
How about this?
Sample Quiz Problem
Given an array of ints, return the index of the largest
element. Use the code below.
public int getLargestIndex(int[ ] iArray) {
int iLargest = iArray[0];
int iCounter;
for (iCounter = 1; iCounter < iArray.length;
iCounter++)
{
if (iLargest < iArray[iCounter])
iLargest = iArray[iCounter];
}
return iLargest;
}// getLargestIndex
Why not start out
iCounter at 1, and not
0 like most for loops?
Sample Quiz Problem
Given an array of ints, return the index of the largest
element. Use the code below.
public int getLargestIndex(int[ ] iArray) {
int iLargest = iArray[0];
int iCounter;
for (iCounter = 1; iCounter < iArray.length;
iCounter++)
{
if (iLargest < iArray[iCounter])
iLargest = iArray[iCounter];
}
return iLargest;
}// getLargestIndex
Now it should be perfect!
Sample Quiz Problem
Given an array of ints, return the index of the largest
element. Use the code below.
public int getLargestIndex(int[ ] iArray) {
int iLargest = iArray[0];
int iCounter;
for (iCounter = 1; iCounter < iArray.length;
iCounter++)
{
if (iLargest < iArray[iCounter])
iLargest = iArray[iCounter];
}
return iLargest;
}// getLargestIndex
Test taking is about
READING THE DIRECTIONS
Sample Quiz Problem
What’s the solution? This is a good problem to work
out on your own. We don’t want the largest VALUE, we
instead want the INDEX of the largest value.
Lessons Learned
Read the question carefully.
Don’t fall into habits (e.g., all for loops must
start with the counter at 0).
Don’t make assumptions about input data (e.g.,
-99999 is NOT a good initial value).
Trace your code; watch for simple >, < errors.
Questions?
The Road Ahead
Today
Recursion
Classes and Objects
Constructors
Object References
Parameters
A Question
How many people here don’t like recursion?
Why not?
A Promise: By the end of this lecture, you will say:
“Recursion Rocks My World!”
Recursion
Let’s say you place two rabbits in a hutch.
What’s going to happen?
Two Months Later...
original rabbits
new rabbits
If it takes two months for rabbits to reach maturity, in
two months you’ll have one productive pair and one
(brand new) non-productive pair. (This assumes all
rabbits live up to their reputation.)
The rabbits keep at it.
1 month old
0 months old
The next month, you get another pair . . .
Suppose
What if:
1
The rabbits always had two offspring, always
male and female;
2
Rabbits always reached maturity in two months;
3
No rabbit dies.
How many pairs of rabbits do you have in a year?
Hare Raising Story
Start
KEY
= one m/f pair
End Month 1
End Month 2
End Month 3
End Month 4
End Month 5
Pairs of Rabbits
Month
1
2
Productive
0
1
Non-Productive
1
0
Total
1
1
3
4
5
6
1
2
3
5
1
1
2
3
7
8
9
8
13
21
5
8
13
13
21
34
10
11
12
34
55
89
21
34
55
55
89
144
See a
pattern
yet?
2
3
5
8
Let’s Take Another Example
Instead of rabbits, let’s use geometry.
Draw a square of size 1.
Rotating 90 degrees, add to it a square of size 1.
Rotating 90 degrees again, add a square of size 2.
Again, rotate and add a square of size 3, and so on.
Keep this up for the sequence we noted in the table:
1, 1, 2, 3, 5, 8, 13, 21, . . . ,
What do you see?
1
1
2
3
5
8
13
21
13
21
2
3
8
1 1
5
Does this look familiar?
It’s not just about rabbits.
The Truth
is Out There
See the pattern?
Month
Productive
Non-Productive
Total
1
0
1
1
2
1
0
1
3
1
1
2
4
2
1
3
5
3
2
5
6
5
3
8
7
8
5
13
8
13
8
21
9
21
13
34
10
34
21
55
11
55
34
89
12
89
55
144
It’s the Fibonacci
Sequence.
We used brute force to find the progression:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... ,
It turns out this pattern is repeated in many places: sea shells, sun
flowers, pine cones, the stock market, bee hives, etc.
Writing the Formula
Given:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... ,
Can we write this as a formula for any number, n?
This guy could:
n
Fib(n) =
1+
5
2
Jacques Binet
(1786-1856)
-
n
1-
5
2
But let’s be honest.
We’re not as smart as him.
But that’s OK. We can code.
What If You Can’t Find the
Formula?
Suppose you didn’t know:
n
Fib(n) =
1+
5
2
-
n
1-
Which one
would your
rather code
and debug?
5
2
You could take Math 3012 or you could
instead manage with:
Fib(n) = Fib(n-1) + Fib(n-2),
Fib(0) = 0;
Fib(1) = 1;
For some
problems,
there might
not exist a
formula, and
recursion is
your only
option.
(The value at any given place is the sum of the two prior values.)
Recursive Fibonacci
We have our general rule:
Fib(n) = Fib(n-1) + Fib(n-2),
Fib(0) = 0;
Fib(1) = 1;
We can say a few things about it:
1
It’s defined recursively (duh).
2
It has a terminal condition (AHA!)
3
It can be determined and calculated (addition).
Coding Fibonacci
public class FibTest {
public static int fib (int num) {
What do we know to
start with? We know that
we need a method that
return the Fibonacci
value for a number at a
given position.
}// FibTest
This suggests a method
that gives and gets an int
Coding Fibonacci
public class FibTest {
public static int fib (int num) {
if (num == 0)
return 0;
What’s the FIRST thing we do
with a recursive method?
We plan on how it will terminate!
We know one special case
for the Fibonacci sequence:
}// FibTest
F(0) = 0
Coding Fibonacci
public class FibTest {
public static int fib (int num) {
if (num == 0)
return 0;
else if (num == 1)
return 1;
We also know a
second special case
that could terminate
our recursion:
F(1) = 1.
}// FibTest
Coding Fibonacci
public class FibTest {
public static int fib (int num) {
if (num == 0)
return 0;
else if (num == 1)
return 1;
else
return fib(num-1) + fib(num-2);
}
The last part of our
formula is merely:
}// FibTest
F(n) = F(n-1) + F(n-2)
Coding Fibonacci
public class FibTest {
public static int fib (int num) {
if (num == 0)
return 0;
else if (num == 1)
return 1;
else
return fib(num-1) + fib(num-2);
}
Is this safe? What if someone
passed in 0 to our method?
What happens?
}// FibTest
What if they passed in 1?
Coding Fibonacci
public class FibTest {
public static int fib (int num) {
if (num == 0)
return 0;
else if (num == 1)
return 1;
else
return fib(num-1) + fib(num-2);
}
public static void main(String[] args) {
for (int i=0; i < 10; i++)
System.out.println (fib(i));
}
}// FibTest
It is our responsibility
to write a main to test
this method.
Coding Fibonacci
public class FibTest {
public static int fib (int num) {
if (num == 0)
return 0;
else if (num == 1)
return 1;
else
return fib(num-1) + fib(num-2);
}
public static void main(String[] args) {
for (int i=0; i < 10; i++)
System.out.println (fib(i));
}
Are we done?
}// FibTest
What about negative
numbers? More work is needed
Recursion Review
So far, we’ve seen that for recursive behavior:
1) Recursion exists in all of nature.
2) It’s easier than memorizing a formula. Not every
problem has a formula, but every problem can be
expressed as a series of small, repeated steps.
3) Each step in a recursive process should be small,
calculable, etc.
4) You absolutely need a terminating condition.
Honesty in Computer Science
1. To make life easy the typical examples given for
recursion are factorial and the Fibonacci numbers.
2. Truth is the Fibonacci is a horror when calculated
using “normal” recursion and there’s not really any
big advantage for factorial.
3. So why all the fuss about recursion?
4. Recursion is absolutely great when used to write
algorithms for recursively defined data structures
like binary trees. Much easier than iteration!
5. Recursion is excellent for any divide & conquer
algorithm like...
One More Example
Suppose we wanted to create a method that solve:
Pow(x, y) = xy
In other words, the method returned the value of one
number raised to the power of another:
public static double pow
(double value, int exponent);
Planning the Method
Unlike the Fibonacci example, our mathematical
formula is not the complete answer.
Pow(x, y) = xy
We’re missing some termination conditions.
But we know:
x1 = x;
x0 = 1;
So we could use these as our terminating condition.
Attempt #1
public static double pow(double value, int exponent){
if (exponent == 0)
return 1D;
Always, always start with
some sort of terminating
condition. We know any number
raised to the zero power is one.
Attempt #1
public static double pow(double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
... and any number raised to the
power of one is itself.
Attempt #1
public static double pow(double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else
return value * pow (value, exponent--);
}
For all other values, we can
return the number times the
recursive call, using our exponent
as a counter. Thus, we calculate:
26 = 2*2*2*2*2*2
Attempt #1
public static double pow(double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else
return value * pow (value, exponent--);
}
When we run this, however, bad
things happen. The program
crashes, having caused a stack
overflow. How can we solve this?
Attempt #1
public static double pow(double value, int exponent){
if (bDEBUG) System.out.println
(“Entering with: “ + value +
“, and exp: “ + exponent);
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else {
return value * pow (value, exponent--);
}
}
Attempt #1
public static double pow(double value, int exponent){
if (bDEBUG) System.out.println
(“Entering with: “ + value +
“, and exp: “ + exponent);
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else {
return value * pow (value, exponent--);
}
}
Our debug statement tells us that the
exponent is never being decreased.
Evidently, the “exponent--” line is
not being evaluated before the recursive
call takes place. As it turns out, the
post-decrement operator -- is the problem.
Attempt #1
public static double pow(double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else {
exponent = exponent - 1;
return value * pow (value, exponent);
}
}
We decide that typing one extra
line takes less time than debugging
such a subtle error. Things are
working now.
“Do I Have to Use Recursion?”
public static double pow(double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else {
exponent = exponent - 1;
return value * pow (value, exponent);
}
}
How many would have preferred to do this with a
“for loop” structure or some other iterative
solution?
How many think we can make our recursive method
even faster than iteration?
Nota Bene
Our power function works through brute force
recursion.
28 = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2
But we can rewrite this brute force solution into two
equal halves:
28 = 24 * 24
and
24 = 22 * 22
and
22 = 21 * 21
and
anything to the power 1 is itself!
And here's the cool part...
28 = 24 * 24
Since these are the same we don't
have to calculate them both!
AHA!
So only THREE multiplication operations have to
take place:
28 = 24 * 24
24 = 22 * 22
22 = 21 * 21
So the trick is knowing that 28 can be solved by dividing
the problem in half and using the result twice!
"But wait," I hear you say!
You picked an even power of 2. What
about our friends the odd numbers?
Okay we can do odds like this:
2odd = 2 * 2 (odd-1)
"But wait," I hear you say!
You picked a power of 2. That's a no
brainer!
Okay how about 221
221 = 2 * 220 (The odd number trick)
220 = 210 * 210
210 = 25 * 25
25 = 2 * 24
24 = 22 * 22
22 = 21 * 21
"But wait," I hear you say!
You picked a power of 2. That's a no
brainer!
Okay how about 221
221 = 2 * 220 (The odd number trick)
220 = 210 * 210
210 = 25 * 25
25 = 2 * 24
That's 6 multiplications
instead of 20 and it gets
24 = 22 * 22
more dramatic as the
2
1
1
2 =2 *2
exponent increases
The Recursive Insight
If the exponent is even, we can divide and conquer so it
can be solved in halves.
If the exponent is odd, we can subtract one,
remembering to multiply the end result one last time.
We begin to develop a formula:
Pow(x, e) = 1, where e == 0
Pow(x, e) = x, where e == 1
Pow(x, e) = Pow(x, e/2) * Pow(x,e/2), where e is even
Pow(x, e) = x * Pow(x, e-1), where e > 1, and is odd
Solution #2
public static double pow (double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
We have the same base
termination conditions
as before, right?
}
Solution #2
public static double pow (double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else if (exponent % 2 == 0) {
This little gem determines if
a number is odd or even.
}
Solution #2
public static double pow (double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else if (exponent % 2 == 0) {
exponent = exponent / 2;
We next divide the
exponent in half.
}
Solution #2
public static double pow (double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else if (exponent % 2 == 0) {
exponent = exponent / 2;
double half = pow (value, exponent);
We recurse to find that
half of the brute force
multiplication.
}
Solution #2
public static double pow (double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else if (exponent % 2 == 0) {
exponent = exponent / 2;
double half = pow (value, exponent);
return half * half;
And return the two
halves of the equation
multiplied by themselves
}
Solution #2
public static double pow (double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else if (exponent % 2 == 0) {
exponent = exponent / 2;
double half = pow (value, exponent);
return half * half;
}
else {
exponent = exponent - 1;
}
If the exponent is odd,
we have to reduce it
by one . . .
Solution #2
public static double pow (double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else if (exponent % 2 == 0) {
exponent = exponent / 2;
int half = pow (value, exponent);
return half * half;
}
else {
exponent = exponent - 1;
double oneless = pow (value, exponent);
}
And now the exponent is
even, so we can just
recurse to solve that portion
of the equation.
Solution #2
public static double pow (double value, int exponent){
if (exponent == 0)
return 1D;
else if (exponent == 1)
return value;
else if (exponent % 2 == 0) {
exponent = exponent / 2;
int half = pow (value, exponent);
return half * half;
}
else {
exponent = exponent - 1;
double oneless = pow (value, exponent);
return oneless * value;
}
We remember to multiply the value
}
returned by the original value, since
we reduced the exponent by one.
Recursion vs. Iteration:
Those of you who voted for an iterative solution are
likely going to produce:
O(N)
In a Dickensian world, you would be fired for this.
While those of you who stuck it out with recursion are
now looking at:
O(log2n)
For that, you deserve a raise.
Questions?