CS503: First Lecture, Fall 2008

Download Report

Transcript CS503: First Lecture, Fall 2008

CS305/503, Spring 2009
Basic Java, OOP, and Arrays.
Michael Barnathan
Review Questions
• What is one difference between a program and an
algorithm?
• What is the asymptotic complexity of each of the
following?
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
f(n) = 10n2 - 15n - 2
f(n) = (5n4 + 2n2 - 10n + 2) / 3n
f(n) = 1 + 2 + 3 + … + n
f(n) = 12 * sqrt(n+5)
f(n) = 5n + log(n4)
f(n) = 5n + log(n)4
f(n) = 9n3 + O(n6)
f(n) = n + Ω(n3)
f(n) = 9n3 + Θ(n6)
f(n) = f(n-1) + f(n-2), f(1) = f(2) = 1.
Review Questions
• Let a be an array of n numbers. What is the
complexity of the following algorithms?
1. System.out.println(a[10]);
2. for (int i = 0; i < n; i++)
System.out.println(a[i]);
3. for (int i = 0; i < n; i++)
for (int j = 0; j < i; j++)
System.out.println(a[i] * a[j]);
4. for (int i = 0; i < n; i++)
System.out.println(a[i]);
for (int j = 0; j < n; j++)
System.out.println(a[i]);
Review Questions
• Finally, what is the complexity of the following
compositions of asymptotics?
1.
2.
3.
4.
2 * O(n)
O(n)2
2)
O(n
2
log(O(n3))
Important things to remember
• Nesting two linear functions results in a quadratic function.
– i = 1, j = 1..n, i = 2, j=1..n, …, i = n, j = 1..n
– Total number of iterations is n2.
• Taking two linear functions in sequence results in another
linear function:
– i = 1..n, j = 1..n
– Total number of iterations is 2n.
• More generally, nesting multiplies functions (adds
exponents):
– O(n3) * O(n4) = O(n7)
• While sequences add functions (no change to exponent):
– O(n3) + O(n4) = O(n4)
Here’s what we’ll be learning:
• Java Review:
– Constants (using final).
– Member variables of a class.
– Methods and constructors (similar in C++).
• Lack of a destructor, garbage collection.
– Static variables (using static, also in C++).
– “Public”, “private”, and “package” access and the concept of least
privilege (also in C++).
– Encapsulation.
• Theory:
– Unsorted Arrays.
• “CRUD” operations: Create/read/update/delete.
• Linear searching.
• Fixed vs. variable size arrays.
– “Growing”/“Doubling” strategy.
Back to the Guessing Game
• Here’s the code for the game itself:
import java.util.Scanner;
class GuessingGame {
public static void main(String[] args) {
final int maxnum = 100;
//Highest number we're going to guess to.
//I'm thinking of a number...
int number = (int) ((Math.random() * maxnum) + 1);
System.out.println("I'm thinking of a number between 1 and " + maxnum);
Scanner readnums = new Scanner(System.in);
int guess = 0;
while (guess != number) {
System.out.print("What do you think it is? ");
guess = readnums.nextInt();
if (guess < number)
System.out.println("Nope, higher!");
else if (guess > number)
System.out.println("Nope, lower!");
else
System.out.println("You got it!");
}
}
}
Analysis:
• Let’s play the guessing game. We’ll run through
the program line-by-line as we go.
• Not knowing the number, what strategy would
you use to minimize the number of guesses you
would have to make?
– The computer executes an algorithm to verify your
guesses…
– And you can execute an algorithm to make them.
• In the worst case, how many guesses must you
make by following your strategy?
The other side of the game…
• You just manually executed an algorithm to try
and guess the number.
• Now let’s try to write a program that will do it in
Java. I’ve “encapsulated” the verification part of
the guessing game into the following class:
public class Guesser {
public static final int MAXNUM = 100;
private final int secret;
//Generate the secret number in the constructor.
public Guesser() { secret = (int) (Math.random() * MAXNUM + 1); }
//Returns < if the number is less than the guess, > if greater, = if equal.
//This uses the “ternary operator” – which is just a concise way to write if/else statements.
public char guessNumber(int guess) { return (secret > guess) ? ‘>’ : ((secret < guess) ? ‘<’ : ‘=’); }
}
This has some new things that we need to know before writing anything.
Member variables.
public class Guesser {
public static final int MAXNUM = 100;
private final int secret;
…
}
• Standard local variables are defined inside of a function and “go out of
scope” when the block of code they’re defined in ends.
• These are member variables of class Guesser, which means that they
belong to the class, not just a function inside of the class.
– They will stay in scope as long as their Guesser object does.
– Makes sense: they’re declared outside of any functions.
• They can be public, private, or protected. This determines which classes
can access them for both reading and modification.
Final Variables
•
•
•
You declare constants using the final keyword in Java.
Both local and member variables can be final.
final variables may be set only once.
– Once they have a value assigned, it is an error to assign another value to them.
•
They are like const variables in C, with one important difference:
– They do not need to be declared and initialized in the same statement.
•
The following is valid in Java:
– final int secret;
– secret = 50;
•
But the following is not valid in C:
– const int secret;
– secret = 50;
•
//Nope, you need to set it above.
The following is not valid in either language:
– final int secret = 50;
– secret = 51;
•
//Declaration.
//Initialization.
Why is this an important difference?
– Constructors.
//This is fine.
//No; it must always be 50 now.
Methods
• A method is a function inside of a class.
• All functions in Java are methods.
– You can’t define functions outside of classes, like you could in
C/C++.
• Like variables, methods can be public, private, or protected.
Like variables, this controls which classes can invoke the
methods.
• We’ve already written one: main.
• We’ve already invoked several:
– Scanner.nextInt()
– Math.random()
• Like member variables, you use the “.” operator to call
methods in Java.
Constructors
• A constructor is a special method that is called when an object is created.
– They are usually used to initialize an object’s member data.
• Constructors always have the same name as their class and never return
anything.
– Not even void.
• They can take arguments. A constructor without arguments is called a
default constructor.
• Example constructors:
class Guesser {
private final int secret;
Guesser() { secret = (int) (Math.random() * MAXNUM + 1); }
Guesser(int s) { secret = s; }
//Default constructor.
}
• This is why you must be able to set final variables later in Java – they are
often set inside of constructors.
• If you don’t specify a constructor, Java will give your class an empty one by
default.
– If you specify any constructor, even one with arguments, Java won’t give you a
default one anymore. You must implement it yourself if you want it.
Destructors
• In C++ and some other object-oriented languages, you can define a
destructor.
– This is a function that executes when an object is about to be destroyed (e.g.,
it goes out of scope or is manually deleted using the delete operator).
• Java does not permit destructors.
– This is because Java is a garbage collected language:
• In C/C++, if you allocated memory with malloc or new, you were responsible for cleaning
it up with free / delete when you were finished.
• Failure to do this may result in a memory leak.
• Java automatically keeps track of unused objects and deletes them on its own… but does
so on its own schedule.
– Because the time at which an object is cleaned up is unpredictable, it is
impossible to accurately guess the program state at this time.
– Even something as innocuous as outputting a message might fail.
• Your program may be finished executing and the standard output stream may be closed.
• If you need this, you must manually invoke a function to clean up.
• However, because Java is garbage collected, you will seldom need to do
this. Java takes care of the cleanup for you.
Access: public, private, package.
• Both data and methods have access levels.
• 4 levels: private, package, protected, public.
• Private:
• Only methods within the same class can access a private member variable or method.
• Package:
• Only methods within the same package (group of classes) can access these variables.
•
We’ll discuss packages a bit later.
• This is the default level; i.e., what you get when you leave out a “private”, “public”, or
“protected” keyword in a declaration.
• Protected:
• Only members within the same package or a class that inherits from the same class have
access.
•
More on inheritance next week.
• Public:
• Everyone can access a public member or method.
• Concept of least privilege: don’t allow any more access than you need to.
• Note to C++ programmers: Java doesn’t have “friend” classes.
Encapsulation
• Classes should stand on their own as much as
possible.
• They do this by hiding details of their
implementation (using private) and providing
access to the class through public methods.
– This restricts the actions the caller can take on
objects of your class to the methods you provide.
– This is a good thing because you gain more control
over how your code is called and can test for such
things as invalid or malicious input.
Static Members
• Both variables and methods can be static.
• Normal member variables belong to an object.
– 1 object, 1 instance of the member.
– Two “Employee” objects would have different names and salaries, for
example.
• Static members belong to the class itself.
– 1 instance no matter how many objects.
• You can invoke static members using the name of the class, without
creating an object:
– For example: Math.random() is static.
– So is main!
• Because static methods are not associated with a particular object,
they can only access other static members on their own.
– But you can declare objects of your class and access their members.
Back to the Game
public class Guesser {
public static final int MAXNUM = 100;
private final int secret;
This class is encapsulated. There is no way for you, the
caller, to find out the secret other than to guess it.
//Generate the secret number in the constructor.
public Guesser() { secret = (int) (Math.random() * MAXNUM + 1); }
//Returns < if the number is less than the guess, > if greater, = if equal.
//This uses the “ternary operator” – which is just a concise way to write if/else statements.
public char guessNumber(int guess) { return (secret > guess) ? ‘>’ : ((secret < guess) ? ‘<’ : ‘=’); }
}
We already know the algorithm: find the midway point, guess, change the
boundaries of the acceptable answers depending on whether the guess was
low or high. Can you think of some code that would do it?
Analysis
• When you used this strategy manually, it took
logarithmic time. Let’s take a look at the code
to see whether this is still true.
Arrays
•
•
•
Reminder: Data structures are models that make certain types of access to your
data easier.
Among the simplest of these models is the array.
Arrays are contiguous homogenously typed addressable collections of data.
– Contiguous: Element 2 immediately succeeds Element 1 in memory.
– Homogenous: Every element is of the same data type.
– Addressable: You can access an individual item in an array based on its index.
•
RAM is actually just one very big array.
– Memory addresses are indices into the array.
•
Both C and Java have built-in support for arrays.
– You’ve seen at least one: String[] args in main.
– Arrays in C are just pointers. Java gives you much more.
•
When you declare an array in Java, the object gets the size.
– Example:
•
Employee[] e = new Employee[10];
Accessing an array is simple:
– System.out.println(e[0]);
– The index runs from 0 to size-1.
•
The most fundamental difference between arrays in C and Java:
– You can get the size:
System.out.println(e.length);
//Prints 10.
An Array
Data
10
21
44
-13
7
26
28
14
Index
0
1
2
3
4
5
6
7
Contiguous: adjacent elements are adjacent in memory.
Homogenous: all elements are “int”s.
Size: 8.
A[0] = 10
A[4] = 7
A[8] will throw an ArrayIndexOutOfBounds exception in Java.
Random Access
• CRUD operations: Let’s start with reading.
• Reading an array is easy: just use arr[index].
– This is a constant-time (O(1)) operation, no matter what
the index is.
– This is possible due to the contiguous and homogenous
properties of the array.
• All elements are the same type.
• All elements are adjacent in memory.
• All Java needs to do is offset sizeof(type) * index from the start of
the array in memory – quick lookup.
– This is known as “random access”.
• Because looking up a random element always takes O(1).
• And this is why RAM is called “random access memory”.
Insertion on Unsorted Arrays
• Since we don’t care about the order, we can
insert at the end.
– If we wanted to insert in the middle, we’d need to
shift everything after it down – O(n).
• This is simple: keep a counter of how full the
array is and increment it as we go.
• Let’s call it arrcount and initialize it to 0.
• We can just say: arr[arrcount++] = newelem;
• Since arr[arrcount] is O(1), so is insertion.
Updating
• arr[index] = newval;
• Simple, easy, also O(1) due to random access.
Deletion
• Two interpretations:
– The book’s: You need to shift everything beyond
the target element down.
– Mine: If the order really doesn’t matter, you can
just swap with the last element and decrement
the counter you’re using to keep track of the size.
– “Shift everything down” is linear – you may have
to move up to n-1 items.
– The swap method is constant-time.
Unsorted arrays seem pretty good.
•
•
•
•
•
•
Insertion at end:
Insertion in the middle:
Accessing any element:
Updating an element:
Deletion (shift):
Deletion (swap):
O(1).
O(N).
O(1).
O(1).
O(N).
O(1).
• Why use anything else?
– Using only arrays worked pretty well in VB.
– We could end the course now!
Back to reality…
• There are some drawbacks, however:
– If you don’t know how large the array is, you’ll have to do
one of three things (all of which trade off):
• Underestimate and tell the user he’s out of space (flexibility).
• Overestimate and waste space (space).
• Grow the array dynamically as it gets larger (time + complexity).
– Searching for an element in the array is linear.
• Access and Search are generally the most
frequent/important actions a program will take.
– Unless you’re doing a lot of writing; e.g., logging data.
– These are the ones we want to optimize, then.
Growing the Array
•
•
•
•
Idea: when the array runs out of space, make it bigger.
Problem: you can’t resize arrays.
Solution: create a new, larger array and copy.
Double the current size is usually a good choice.
– The overall number of doubles you’ll need to do for n insertions
is O(log n).
• The algorithm:
– Create a new array of size n*2: O(1).
– Copy the data to the new array: O(n).
– Refer the variable to the new array: O(1).
• There is a very useful Java class that will do this for you.
– Look up the Vector class in the Java API.
– We’ll cover these so-called “container classes” later on.
Growing the Array
A=
B=
A
Data
10
21
44
-13
Index
0
1
2
3
Data
10
21
44
-13
7
26
28
14
Index
0
1
2
3
4
5
6
7
Linear Search
• What’s the problem with searching an
unsorted array?
Worst case: Find 15.
Found it?
Data
10
21
44
-13
7
26
28
14
Index
0
1
2
3
4
5
6
7
• We have to search every element: O(n).
No.
Linear search: The Algorithm.
• You should be able to figure this one out.
• It’s very intuitive.
Object[] arr;
for (int i = 0; i < arr.length; i++) {
if (arr[i].equals(target))
return i;
}
return -1;
//“Sentinel” value; not found.
Binary Search: A Better Way
• If you know the array is sorted, it’s possible to
search it in O(log n) time.
• This works using the same principle as the
guessing game:
– Eliminate half of the array on each try.
• There are some tradeoffs to sorting the array.
– See what I mean? Tradeoffs are everywhere!
• We’ll discuss this more next week.
Fin.
•
•
This is really the end. To re-post slide #14:
The lesson:
– The principles of Java owe an intellectual heritage to C++. Much of “progress” is successive
copying with some changes. Your existing knowledge will be useful to you when learning new
techniques.
•
Next class: Searching, basic sorting, sorted arrays, and intro. to recursion.
•
Assignment 1: Design an automatic guessing game.
•
•
•
•
•
•
•
Due Thursday, 12/29.
Rather than having the user guess the number, have the program try to guess it according to
the strategy we discussed.
Keep track of the number of guesses required. Run the program several times and report the
average and worst number of guesses. Does this agree with our worst-case assumption of
log(n)? (The base-2 log of 100 is 6.64. There’s no such thing as .64 of a guess, so round to 7).
Try varying the maximum number the program will choose up to. How do the average and
worst number of guesses increase with the input? Do they increase logarithmically?
Remember, in CS, logarithms are base 2. To do base 2 logs on a calculator, use log(x) / log(2).
You may use the Guesser class that I provided.
Contact me with any questions.