Consider the following simple problem:
As a first attempt to solve this problem, consider
(source file):
Here are some timings for different array sizes (in milliseconds):
How could we improve this program?
So, it is faster. Can we improve it further?
In-Class Exercise 1.1:
(Solution)
In what other ways can the original Java program be improved?
In-Class Exercise 1.2:
(Solution)
Why are algorithms 2 and 3 faster?
static void algorithm1 (double[] A)
{
// 1. Set the initial maximum to the difference between the first two:
double max = Math.abs (A[0] - A[1]);
// 2. Record the actual values:
double x = A[0], y = A[1];
// 3. Now compare each pair of numbers:
for (int i=0; i < A.length-1; i++){
for (int j=i+1; j < A.length; j++) {
// 3.1 For each such pair, see if it's differenceis larger
// than the current maximum.
double diff = Math.abs (A[i] - A[j]);
if (diff > max) {
// 3.1.1 If so, record the new maximum and the values
// that achieved it.
max = diff;
x = A[i];
y = A[j];
}
} // inner-for
} // outer-for
// 4. Output:
System.out.println ("Algorithm 1: the numbers: " + x + "," + y
+ " difference=" + max);
}
Array size execution time
1000 84
2000 258
3000 554
4000 985
5000 1551
6000 2228
7000 3031
8000 3962
9000 5020
10000 6247
Write it in a faster language, e.g. C.
(source file):
Here are some timing comparisons (in milliseconds):
Array size Java (Algorithm1) C
7000 3031 2670
8000 3962 3490
9000 5020 4430
10000 6247 5490 Now let's consider some algorithmic improvements:
static void algorithm2 (double[] A)
{
// 1. Sort the array:
Arrays.sort (A);
// 2. The smallest is first, the largest last:
double diff = A[A.length-1] - A[0];
// 3. Output:
System.out.println ("Algorithm 2: the numbers: " + A[0] + "," + A[A.length-1]
+ " difference=" + diff);
}
Now compare this with Algorithm1 (in Java) and the algorithm in C:
Array size Algorithm1 (Java)
Algorithm1 (C) Algorithm2 (Java)
7000 3031 2670 47
8000 3962 3490 49
9000 5020 4430 49
10000 6247 5490 56
static void algorithm3 (double[] A)
{
// 1. Initialize min and max:
double min = A[0];
double max = A[0];
// 2. Scan through array finding the latest min and max:
for (int i=1; i < A.length; i++){
if (A[i] < min)
min = A[i];
else if (A[i] > max)
max = A[i];
}
// 3. Output:
double diff = max - min;
System.out.println ("Algorithm 3: the numbers: " + min + "," + max
+ " difference=" + diff);
}
Consider the largest distance problem:
In-Class Exercise 1.3:
(Solution)
In this exercise you will write code to find the distance
between the two furthest points among a set of points:
In-Class Exercise 1.4:
(Solution)
Take your code for Exercise 3, and count the number of times "distance"
is computed. For n points, how many times
is distance computed?
Now consider this algorithm:
If the center point is at a "right turn", discard that point.
For example, the two farthest points of the two edges crossing at P0
are Q0 and Q1. Then all antipodal
points of P0 are those points from Q0 to Q1 in the convex hull, including
Q0 and Q1.
Now, let's take a first look at the code:
Next, consider this idea:
Note:
Summary:
% java LargestDistance 50
public class Pointd {
public double x, y;
}
to hold the x and y coordinates of a point.
Let's take this one step at a time:
In other words, a polygon is a curve ending on itself
that is formed by a sequence of straight-line segments. A simple
polygon does not cross itself.
public class AntiPodalAlgorithm {
public static double findLargestDistance (Pointd[] points) {
// 1. Compute the convex hull:
Hull grahamHull = new Hull(points);
// 2.1 Extract the hull points:
Pointd[] hullPoints = grahamHull.getHull();
// 2.2 Extract the number of points in the hull (hullSize):
int hullSize = grahamHull.getSize();
// variables to be used.
double largest=0.0;
int q0, q1, q2, i=0;
// 3.0 Find the farthest point to size s0 of the hull
// incident at points[hullSize-1] and points[0]:
while (Geometry.area(hullPoints[hullSize-1], hullPoints[0], hullPoints[i])
< Geometry.area(hullPoints[hullSize-1], hullPoints[0], hullPoints[(i+1)%hullSize]))
i++;
q0 = i;
// 3. Find farthest points to other sizes of the hull.
// Compute largest distance:
for (int j=0; j < hullSize-1; j++) {
// 3.1 Find farthest points:
while (Geometry.area(hullPoints[j], hullPoints[j+1], hullPoints[i % hullSize])
< Geometry.area(hullPoints[j], hullPoints[j+1], hullPoints[(i+1) % hullSize]))
i++;
q2 = i;
q1 = i;
// 3.2 Now the antipodal points of hullPoints[j] are between q0 and q1, inclusive.
// Compute the largest distance between antipodal pairs containing hullPoints[j]
while (!(q2 < q0)) {
largest = Math.max(largest,
Geometry.distance(hullPoints[j], hullPoints[q2 % hullSize]));
q2--;
}
q0 = q1;
}
return largest;
}
}
import java.util.*;
public class Hull {
// Maintain hull points internally:
private Pointd[] hull;
private int hullSize;
// Constructor that takes the point set:
public Hull (Pointd[] points) {
Pointd[] vertices;
int i, n;
// record the number of points in the point set:
n = points.length;
// 0. Make a copy of the vertices because we'll need to sort:
vertices = new Pointd[n];
for (i=0; i < n; i++)
vertices[i] = new Pointd();
System.arraycopy (points, 0, vertices, 0, n);
// 1. Find the leftmost, lowest point (it is on the hull):
int low = findLowest(vertices);
// 2.0 Put the low in the first position:
swap (vertices, 0, low);
// 2.1 Sort vertices based on their polar angles in counterclockwise
// order around the leftmost lowest point.
HullSortComparator comp = new HullSortComparator(vertices[0]);
Arrays.sort(vertices, 1, vertices.length-1, comp);
// 3. Remove collinear points:
n = removeCollinearPoints(vertices);
// 4. Compute the Hull based on Graham's Scan:
hull = grahamScan(vertices, n);
}
private Pointd[] grahamScan(Pointd[] points, int numOfPoints) {
// 1. Create a stack and initialize with the first three points:
HullStack hstack = new HullStack (numOfPoints);
hstack.pushNew(points[0]);
hstack.pushNew(points[1]);
hstack.pushNew(points[2]);
// 2. Start scanning points:
for (int i=3; i < numOfPoints; i++) {
// 2.1 isHull check whether the top two points in the stack and the point
// this is scanning form a right turn or not.
// if right turn, then pop the top element.
while (!hstack.isHull(points[i]))
hstack.popTop();
// 2.2 Push the point that is just scaned into the stack.
hstack.pushNew(points[i]);
}
// 3. Return all points still on the stack:
hullSize = hstack.getSize();
return hstack.hullArray();
}
// Compute the lowest point (the point with smallest y-coordinate). Choose
// the leftmost one in case of a tie.
private int findLowest (Pointd[] points)
{
// 1. Scan through points:
// 1.1 If y-value is lower, the point is lower. If the y-values
// are the same, check that the x value is further to the right:
// 2. Return lowest point found:
}
// Compute the lowest point (the point with smallest y-coordinate). Choose
// the leftmost one in case of a tie.
private int findLowest (Pointd[] points) {
// Not shown
}
// Swap two points.
private void swap (Pointd[] points, int i, int j) {
// Not shown
}
}
For example, we compute the number of hull points for the
following data sizes (randomly generated):
# of points # hull points
10 6
100 13
1000 18
10000 25
public class HullAlgorithm {
public static double findLargestDistance(Pointd[] points) {
double diffX, diffY, dist, largest = 0.0;
// 1. Compute the convext hull:
Hull grahamHull = new Hull(points);
// 2.1 Extract the hull points:
Pointd [] hull = grahamHull.getHull();
// 2.2 Extract the hull size:
int size = grahamHull.getSize();
// 3. Compute largest distance between hull points:
for (int i=0; i < size; i++) {
for (int j=i+1; j < size; j++) {
diffX = hull[i].x - hull[j].x;
diffY = hull[i].y - hull[j].y;
dist = Math.sqrt(diffX*diffX + diffY*diffY);
// largest = Math.max(largest, dist);
if (largest<dist)
largest = dist;
}
}
return largest;
}
}
What this course is about:
Key principles:
Algorithms in the context of the rest of computer science:
In addition, you will:
Example: sorting
These data structures tend to be used again and again.
(So, it's not enough to use a package).
What is an algorithm?
e.g., finding the convex hull is part of finding the largest distance.
(Analyzing a problem is often harder).
What is pseudocode?
1. Scan through all possible pairs of numbers in the array.
2. For each such pair, compute the absolute difference.
3. Record the largest such difference.
Input: an array of numbers, A
1. Set the initial maximum to the difference between the first two
in the array.
2. Loop through all possible pairs of numbers:
2.1 For each such pair, see if it's difference is larger
than the current maximum.
2.1.1 If so, record the new maximum and the values
that achieved it.
3. Output the maximum found.
Algorithm1 (A)
Input: A, an array of numbers.
// Set the initial maximum to the difference between the first two:
1. max = Absolute-Difference (A[0], A[1]);
// Record the actual values:
2. x = A[0], y = A[1];
// Now compare each pair of numbers:
3. for i=0 to length-1:
4. for j=i+1 to length
// For each such pair, see if it's difference is larger
// than the current maximum.
5. diff = Absolute-Difference (A[i], A[j]);
6. if (diff > max)
// If so, record the new maximum and the values
// that achieved it.
7. max = diff;
8. x = A[i]; y = A[j];
9. end-if
10. end-for
11. end-for
Output: min, max, diff
Note:
(they are usually implied or left to the reader)
Algorithms are analysed using the so-called "order notation" or "asymptotic notation":
Consider the following example:
First, it's preferable to use math symbols:
Nonetheless, it is possible to simplify further by observing that
n(n-2)/2 = n2/2 -2n/2.
Observe: the term that dominates is
n2/2.
Accordingly, for purposes of approximate analysis:
for (int i=0; i < A.length-1; i++){
for (int j=i+1; j < A.length; j++) {
double diff = Math.abs (A[i] - A[j]);
if (diff > max)
max = diff;
}
}
Let's analyse this piece of code:
=> The algorithm requires O(n2) steps.
Let f(n) and g(n) be two functions defined on the reals:
In-Class Exercise 1.5:
(Solution)
Find the c and N in the first definition above to show
that f(n)=6n2+18 is O(n2).
In-Class Exercise 1.6:
(Solution)
Implement and analyze the Selection Sort algorithm:
Consider comparing two algorithms A and B on the sorting problem:
Typical worst-case analysis:
Theoretical average-case analysis:
Experimental average-case analysis:
A comprehensive analysis of a problem:
How to compare them?
(Depends on processor, system architecture, OS).
(e.g., sorting cannot be solved faster than O(n log(n))
A quick math review:
In-Class Exercise 1.7:
(Solution)
Use a calculator and plot the following functions in the range
(0,100) by "joining the dots" of function values computed
at 0, 10, 20, ..., 100:
In-Class Exercise 1.8:
(Solution)
Use the definition of logarithms and the exponentiation rules to
show that: loga(xy) = loga(x) + loga(y).
e.g., f(x) = x2 implies f(3) = 32 = 9.
Notes:
2? = 256?
28 = 256
Thus, log2(256) = 8.
log2256 times.
How long does this procedure take?
How many times can you recursively break up the data into two pieces?
Ans: log(n) times.
Thus, the procedure takes about log(n) steps (Eg. Binary search).
Use the System.currentTimeMillis() method:
long startTime = System.currentTimeMillis();
// ... algorithm runs here ...
double timeTaken = System.currentTimeMillis() - startTime;
A brief mention of some "famous" algorithms and data structures:
In-Class Exercise 1.9:
Do a web search on "top ten algorithms".
(Solution)