By the end of this module, for simple programs with real
numbers, you will be able to:
Apply loops/variables to evaluating sequences and sums.
Demonstrate plotting of functions.
Demonstrate straight-line fit for some data.
Work with strings to compute some statistics.
8.0 Audio:
8.0 Some math history via programming - example 1
In this and the next few sections,
we'll use simple for-loops to compute some
of the most famous constants in mathematics, and explore
a few mathematical ideas (no background needed).
One key idea in mathematics is the notion of a limit:
Let x1, x2, ..., xn, ...
be a sequence of real numbers.
For example:
x1 = 1
x2 = 3
x3 = 5
x4 = 7
... (and so on)
A limit explores the idea of what the sequences
tends towards.
The above example grows to ∞ (infinity).
Example: suppose xn = 1/n.
⇒ Then, xn gets closer to 0
as n → ∞.
Let's explain:
First, let's enter values of n to calculate (with a
calculator) a few values:
public class TestSequence {
public static void main (String[] argv)
{
// Remember: 3/4 = 0 because it'll be integer division.
// We need to make this 3.0 / 4.0.
double x = 3.0 / 4.0;
for (int n=2; n<=20; n++) {
System.out.println (x);
// The next term is 3/4 times the previous.
x = (3.0/4.0) * x;
}
}
}
8.1 Exercise:
Write up the above program and verify that successive
powers of 3/4 are getting smaller.
Now try another number that's less than 1, like 0.95
and examine its successive powers.
Next, let's look at a number that's larger than 1, like 4/3.
Since (4/3)2 = (4/3) × (4/3), one
could say that the result will be larger than 4/3.
Thus, (4/3)n should get larger.
8.2 Exercise:
Change the code above to verify.
Now try another number that's bigger than 1, like 1.05,
and examine its successive powers.
We can generalize this:
If x is any number (like 3/4), we're interested
in what happens with successive powers xn.
Note: for simplicity, we'll stick with positive numbers and not deal with
powers of negative numbers.
By the same reasoning as above, if x is smaller
than 1, then xn gets closer and closer to 0.
Similarly, if x is larger
than 1, then xn grows unboundedly, towards
infinity (but not beyond).
If x=1, of course, the result is boring:
xn=1.
To summarize for any number x:
If x < 1, the limit is 0.
If x > 1, the limit is ∞.
If x = 1, the limit is 1.
Observe that the limits are rather stark: only three limits.
A small tweak will make this more interesting:
Suppose x itself is changing from one iteration to
another.
We'll use the notation xn to denote
the value of x at the n-th iteration.
For example: at the n-th iteration x could be
the n-th odd number:
x1 = 1
x2 = 3
x3 = 5
x4 = 7
... (and so on)
To print the successive powers, we could use code like:
for (int n=1; n<=20; n++) {
// Compute the n-th odd number:
double x = 2*n-1;
// Use a for-loop to compute xn
double power = 1;
for (int j=1; j<=n; j++) {
power = power * x;
}
System.out.println ("n-th power of xn=" + power);
}
8.3 Exercise:
Write up the above code in
OddSequence.java.
Clearly, the sequence grows very fast towards infinity.
The fact that xn is itself increasing
(successive odd numbers) and that we're computing higher powers,
combines for a "double acceleration" of sorts.
You also see how extremely large numbers are printed using
the so-called floating-point notation (with "E").
So, now let's take a decreasing sequence and
subject that to higher powers to see if they "offset" each other.
As the decreasing sequence, we'll pick
xn = (1 + 1/n).
Here, even though successive x's are decreasing, we are
"powering" them to higher power.
So: does this new sequence decrease to zero or grow to infinity?
Now let's compute successively higher powers:
for (int n=1; n<=20; n++) {
// Compute the n-th term:
double x = 1.0 + 1.0/n;
// Use a for-loop to compute xn
double power = 1;
for (int j=1; j<=n; j++) {
power = power * x;
}
System.out.println ("n-th power of xn=" + power);
}
8.4 Exercise:
Edit, compile and execute the program, writing
your code in
StrangeSequence.java.
Try much larger values of n.
What does the limit of the sequence appear to be?
The limit of the above sequence is the
celebrated and important mathematical constant e:
About e:
e is approximately 2.718...
It is just as important and practical as its better known
cousin, π.
e turns up all over the place in math and engineering.
e, like π, is transcendental.
It's unexpectedly interesting that such a simple combination
of a decreasing sequence and higher powers results in
this very important constant.
8.1 Some math history via programming - example 2
Now that we've mentioned π,
let's look at a sequence that computes it.
First, recall: π = ratio of any circle's
circumference to it's diameter.
Just knowing this doesn't give us a practical way to compute
π
We could place strings along a circle and its diameter.
Then straighten out the first string and estimate the ratio.
But this would be quite "low res".
In fact, a great deal of effort in earlier civilizations
went into estimating π.
The first successful effort is attributed to Archimedes
in 250 BC
who used ... (wait for it) ... a sequence! (Much like we did
above).
In fact, he used two different sequences. About which more
later.
His approach withstood centuries of attempts to improve,
until the arrival of calculus.
We'll use the so-called Gregory-Leibniz series from
the post-calculus era:
π = 4 (1 - 1/3 + 1/5 - 1/7 + 1/9 + ....)
Let's do this in steps. First, suppose we had to
compute
1 + 1/3 + 1/5 + 1/7 + 1/9 + ....
Some pseudocode:
sum = 0
for i=1 to k {
sum = sum + 1.0/(2*i-1)
}
Next, how do we alternate the sign? Here's one way:
sum = 0
sign = 1
for i=1 to k {
sum = sum + sign/(2*i-1)
sign = -sign;
}
Now let's put this all into code to print out
the first n terms:
8.5 Exercise:
Edit, compile and execute the program. Try larger
values of n to see how quickly it converges
to π.
When k=4 in
the outerloop, trace through the execution inside the outerloop
in (module8.pdf).
There are now more sophisticated sequences that compute
π to very high accuracy.
The current world record (which does not appear to have
any practical use) is about a trillion digits of accuracy.
8.6 Video:
8.2 Some math history via programming - example 3
Let's look at a third famous constant: Φ, the golden ratio.
Recall the definition of the Fibonacci sequence:
Xn = Xn-1 + Xn-2.
The starting values:
X0=0, X1 = 1.
We wrote a program to print out successive Fibonacci
numbers:
Consider the sequence
Yn = Xn / Xn-1.
That is, the ratio of successive Fibonacci terms.
8.7 Exercise:
In
GoldenRatio.java
modify the above program to compute this ratio. What
does it appear to converge to?
8.3 Some math history via programming - example 4
Zeno's paradox:
Zeno was a Greek philosopher famous for creating
several apparent paradoxes.
His most famous one: the hare and the tortoise
Suppose a hare and tortoise are separated by 1 unit of distance.
Suppose hare is twice as fast as tortoise.
In the time the hare covers 1 unit, the tortoise has
moved foward 1/2 unit.
In the time taken to cover this 1/2 unit, the tortoise
has moved forward 1/4 unit ... etc.
Zeno claimed that by the time the hare catches up,
the tortoise will have traveled:
1/2 + 1/4 + 1/8 + ....
An infinite sum
Zeno's paradox: the hare will never catch up.
Let's resolve this by writing a program to
compute
1/2 + 1/4 + 1/8 + ....
Such a sum is often called a series:
It can be written as a sequence. Define
Xn =
1/2 + 1/4 + 1/8 + .... + 1/2n Then, we are interested in
limn→∞ Xn
Let's write a program to compute this for any n:
Note: in the program above, xn is a variable name.
⇒ We could just as easily have called it anything else.
8.8 Exercise:
What does the series appear to converge to?
Write your code in Zeno.java.
8.9 Audio:
Thus, we see that even though a series appears to add up an
infinite number of numbers, the resulting sum can be finite.
And a small number too.
This sort of strangeness with respect to numbers befuddled
the Greeks and others to follow, until rigorous mathematics
in later centuries built a firm theoretical and axiomatic foundation.
We were able to use
WordTool.
to generate random nouns, verbs and such.
We also learned to obtain the length of a string.
Let's estimate the average length of a verb and compare
that to the same metric for nouns.
Generally, we're accustomed to seeing big nouns like:
"establishment" and "temperance".
Whereas popular verbs can be small, like "read" and "write".
We will try and quantify this by getting the average of N
random nouns and verbs.
8.10 Exercise:
Write code in WordAnalysis.java
with a for-loop that calls
WordTool.getRandomVerb()
and assigns to a
String
variable. Then, extract its length and accumulate into a
variable called
total.
Continue from there to compute and print the average
verb length. Use 1000 such verbs. Then repeat
for nouns.
You will need to download into the same
directory:
WordTool.java
and
wordsWithPOSAndPron.txt.
What is the difference between the average lengths?
Are other parts of speech shorter or longer on average?
8.11 Audio:
8.5 Functions and plotting data - example 1
Let's start by plotting a simple function:
Suppose we want to plot the familiar function
f(x) = sin(x) in
the range [0,10].
Let's start by picking 20 points to plot.
We'll divide the interval [0,10]
into 20 so that the x values (along the x-axis)
are
// 20 points along the x-axis
int N = 20;
double xSpacing = (10.0 - 0.0) / N;
for (double x=0; x<=10; x+=xSpacing) {
// Compute f at the x-value of x
double f = Math.sin(x);
// Plot the point x, f(x)
DrawTool.drawPoint (x, f);
}
8.12 Exercise:
Download DrawTool.java
and FunctionExample.java
Then, compile and execute FunctionExample.java.
Edit the file FunctionExample.java to increase
to 100 the number of points (N) at which the function is
computed - this should produce a smoother curve.
8.6 Functions and plotting data - example 2
Next, let's work with some real data
Consider the following data:
x
f(x)
8.33
1666.67
22.22
3666.67
23.61
4833.33
30.55
5000
36.81
5166.67
47.22
8000
69.44
11333.33
105.56
19666.67
Let's write code to display this data:
public class FunctionData {
public static void main (String[] argv)
{
DrawTool.display ();
DrawTool.setXYRange (0, 110, 0, 20000);
double x = 8.33;
double f = 1666.67;
DrawTool.drawPoint (x,f);
x = 22.2; f = 3666.67;
DrawTool.drawPoint (x,f);
x = 23.61; f = 4833.33;
DrawTool.drawPoint (x,f);
x = 30.55; f = 5000;
DrawTool.drawPoint (x,f);
x = 36.81; f = 5166.67;
DrawTool.drawPoint (x,f);
x = 47.22; f = 8000;
DrawTool.drawPoint (x,f);
x = 69.44; f = 11333.33;
DrawTool.drawPoint (x,f);
x = 105.56; f = 19666.67;
DrawTool.drawPoint (x,f);
}
}
Notice that we had to adjust the default x
and y range.
8.13 Exercise:
Edit, compile and execute FunctionData.java. Then,
try to estimate the slope of a line that would approximately
"fit" the points. Use the DrawTool.drawLine method
to draw a line with your estimated slope. For example, if
your estimated slope is m, you would add this to your
program:
DrawTool.drawLine (0,0, 110, m*110);
8.14 Exercise:
The data is from a world-changing paper published in 1929.
Use a websearch to discover what the data is and why
it changed the world.
8.15 Audio:
8.7 Java's Math package
Let's introduce some programming terminology first:
A programming language is a collection of reserved
words like
public,
class,
int,
double,
and "grammar" like rules for writing code using both
the reserved words, punctuation (like periods, brackets),
and your own identifiers>
Most languages also come with pre-packaged programs to make
it easy to write complex programs.
Why? Think about this:
Most people need to be able to read and write to files.
Many programs need to use math functions.
Yet others need to draw, create buttons and menus and such.
If a language provided nothing beyond the language,
programmers would need to write code repeatedly for all these
"basic" needs.
So, every language also features a library of
useful programs that programmers can use.
Some languages like C have small libraries. Others, like
Java, have enormous libraries.
Often, there are third-party libraries that can be bought
because they happen to do something very useful in some
application niche.
We will later learn how to use libraries in detail.
In fact, we've already used the methods inside
System
(like
println).
Sometimes people will use the term API
interchangeably with library. This is
an acronym. The original is a mouthful (Application
Programmer Interface), probably created by someone with
too much time on their hands.
That said, let's look at one very useful part of Java's library,
with math functions. Here's an example:
public class MathExamples {
public static void main (String[] argv)
{
// Compute the square root of a number:
double x = 2;
double y = Math.sqrt (2);
System.out.println (y);
// The package has many trig functions, e.g.
double angle = 3.14159;
double sinOfAngle = Math.sin (angle);
System.out.println (sinOfAngle);
// Power, exponent, logarithm etc, for example:
double fourthPowerOf3 = Math.pow (3.0, 4.0);
System.out.println (fourthPowerOf3);
// Generate random numbers between 0 and 1.
double randomValue = Math.random ();
System.out.println (randomValue);
}
}
What's important to know:
Libraries are NOT meant to be memorized.
Something like the Java library is too large for any one
person to know completely.
When you need something, you browse the library to look for it.
It's often useful to learn by looking at examples of
using something from the library.
8.16 Exercise:
Do a web search for "Java API". The follow the link to merely
"check out" the API and get a feel for how impossibly large it is.
One the lower-left menu, look for the Math collection and
casually browse through the various functions.
If you were not able to find the link,
use this one.
About the Java library:
The link above took you to the standard documentation.
It will take us a while to understand how to read it, and
to read only those parts that are useful to the task at hand.
Some parts of the library need a lot more expertise to
understand how to use. There are entire books written
for some aspects of the library.
8.8 Using animation for visualization
We will explore the use of visualization for
problems in motion:
Consider an object (say, car) that's moving at velocity v
meters/second.
Then, the distance traveled after t units of time is:
d = vt meters.
Let's not worry about the physics but think "physics tells
us how to predict motion; we'll merely use that via their formulas".
Similarly, consider a ball that's dropped from a height
of 100 meters.
The height after t seconds, it turns out, is
predicted by the formula: h = 100 - ½gt2.
(Here, g = 9.8).
Let's visualize both using our drawing tool:
public class MotionExample {
public static void main (String[] argv)
{
DrawTool.display ();
DrawTool.setXYRange (0,100, 0,100);
DrawTool.startAnimationMode ();
double v = 10;
for (double t=0; t<=10; t+=0.1) {
double d = v*t;
double h = 100 - 0.5*9.8*t*t;
DrawTool.writeTopValue (t);
DrawTool.setPointColor ("blue");
DrawTool.drawPoint (d, 0);
DrawTool.setPointColor ("red");
DrawTool.drawPoint (70, h);
DrawTool.animationPause (100);
}
DrawTool.endAnimationMode ();
}
}
8.17 Exercise:
Edit, compile and execute the above program. Make
sure DrawTool.java is in the same directory.
Guess at the velocity v needed to create a collision
between the two objects by changing v
in the program. At what time would this occur?
8.9 Archimedes and π
Now that we can draw, let's go back in time to draw what
Archimedes (circa 250BC) drew in his quest to compute π
8.18 Exercise:
Download Archimedes.java,
compile and run.
You already have DrawTool
in your directory.
What you see is:
A hexagon inscribed in a circle (that is, inside the circle but as large as possible).
Some other details like axes, coordinates, and lines from
the center of the circle to the hexagon's corners.
Archimedes' idea:
Suppose we could compute the perimeter of the hexagon:
The perimeter could serve as an approximation
of the circle's circumference.
Then divide this by the radius (which we already have fixed)
to get an approximation of π
For the hexagon it turns out to be 3.0.
Archimedes reasoned that if he could calculate the perimeter
of any polygon inscribed, he could get better approximations.
For example, a 10-sided polygon:
Turns out: this gives π = 3.09
A 20-sided polygon would produce an even better approximation.
Turns out: this gives π = 3.12 (not bad)
But once you know how to do this, you can calculate
for much larger polygons such as this 100-sided one, which
is nearly indistinguishable from the circle to the naked eye:
Turns out: this gives π = 3.14107 (pretty good)
Now let's examine some of the details (a loop, really):
We will not worry about the trigonometry. In fact, it's likely
that Archimedes did not either since others had solved problems
about triangles before.
So, let's start with drawing a circle:
// Set DrawTool up so that the circle's
// center is at the origin:
DrawTool.display ();
DrawTool.setXYRange (-10, 10, -10, 10);
DrawTool.drawMiddleAxes (true);
// Radius:
double r = 8;
// Draw the circle
DrawTool.drawCircle (0, 0, r);
8.19 Exercise:
Download ArchimedesSteps.java,
compile and run. Then examine the code.
Note:
We have commented out almost everything except the
drawing of the circle (because we'll do the rest in steps).
DrawTool
expects the center's coordinates (0,0) and the radius.
Next, we'll let N be the number of sides of the polygon.
In a loop that runs N times, we'll draw the triangles
and compute the length.
...
// Interior angle of each triangle.
double angle = 2*Math.PI / N;
// Initialize the variable that will accumulate the lengths
// of edges that comprise the perimeter.
double perimeter = 0;
for (int i=0; i<N; i++) {
// One corner of the triangle on the circumference:
double x1 = r * Math.cos (i*angle);
double y1 = r * Math.sin (i*angle);
// The second corner:
double x2 = r * Math.cos ((i+1)*angle);
double y2 = r * Math.sin ((i+1)*angle);
// Distance between the two:
double dist = Math.sqrt ((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
// Accumulate:
perimeter = perimeter + dist;
// Draw the i-th triangle:
DrawTool.drawLine (0,0, x1, y1);
DrawTool.drawLine (0,0, x2, y2);
DrawTool.drawLine (x1,y1, x2, y2);
}
// The estimate of π is the perimeter divided by the diameter.
double piEstimate = perimeter / (2*r);
System.out.println ("pi=" + piEstimate);
Note: Ignore the trigonometry for the moment and just pay attention to:
We have a loop that runs N times.
In each iteration we compute the coordinates of the i-th triangle.
One of the edges of the i-th triangle (the one with endpoints
on the circle) is part of the perimeter.
We add its length to the perimeter (the distance between the
end points).
Later, we divide the perimeter by the diameter, and we're done.
8.20 Exercise:
Uncomment the main loop in
ArchimedesSteps.java
to see the rest.
As an exercise (not required) you can work through
the trigonometry to explain the calculation of the coordinates.
Lastly, we would be remiss if we did not point out a faster way
of achieving the same result:
Since each polygon edge is of the same length, do we really
need to calculate the length of each and every one of them?
No. We could compute the length of one of them and multiply
by N.
Here's the code:
// Radius:
double r = 8;
// Number of triangles to inscribe:
int N = 100;
// Interior angle of each:
double angle = 2*Math.PI / N;
// The corner of the triangle touching the x-axis:
double x1 = r;
double y1 = 0;
// The second corner:
double x2 = r * Math.cos (angle);
double y2 = r * Math.sin (angle);
// Distance between the two:
double dist = Math.sqrt ((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
// Multiply by N:
double perimeter = N * dist;
// The estimate of is the perimeter divided by the diameter:
double piEstimate = perimeter / (2*r);
System.out.println ("pi=" + piEstimate);
This is probably how Archimedes did it.
Why, then, did we use a for-loop?
⇒ We needed it to draw the different triangles.
The coordinates of the different triangles are different.
Each needed calculation, which is why the loop was necessary.
8.21 Exercise:
Download ArchimedesFast.java,
compile and run. Increase N for higher accuracy.
In fact, Archimedes did one more thing:
He used a second sequence with polygons that circumscribe
the circle, like this one:
Both sequences converge to the circumference.
He used the average of the two for some fairly accurate
estimates.
Lastly, we'll point out that this last section was mostly an
exercise in reading and seeing how concepts learned earlier
can be applied. And to pay our respects to Archimedes, of course.