Let's look at some code that does image rotation:
import java.awt.*; import java.awt.image.*; public class ImageRotationReflection { static ImageTool imTool = new ImageTool (); public static void main (String[] argv) { Image image = imTool.readImageFile ("device.jpg"); imTool.showImage (image, "original"); Image rotatedImage = rotateRight (image); } static Image rotateRight (Image image) { // Extract pixels and dimensions. int[][][] pixels = imTool.imageToPixels (image); int numRows = pixels.length; int numCols = pixels[0].length; // Storage for new image. int[][][] rotatedPixels = new int [numCols][numRows][4]; for (int i=0; i < numRows; i++) { for (int j=0; j < numCols; j++) { // Copy from pixels[i][j] to rotatedPixels[j][end-i] for (int k=0; k < 4; k++) { rotatedPixels[j][numRows-1-i][k] = pixels[i][j][k]; } } } Image rotatedImage = imTool.pixelsToImage (rotatedPixels); return rotatedImage; } }
Exercise 1: The line in the innermost loop
rotatedPixels[j][numRows-1-i][k] = pixels[i][j][k];is what makes it happen. Think through this and explain why it works. Download the above program, along with device.jpg and execute. Can you identify the device in the image?
Exercise 2: Reflection about the diagonal is similar to rotation except that the first row becomes the first column, the second row becomes the second column etc. How would you modify the above code to implement reflection? Download ImageReflection.java and implement your code for reflection.
Let's look at a simple example with chars:
n o h t y p s m i a r y c c l l e k s a h r u b y v m e e h h a l l m p c j n i c e r e e k b i pand a word such as "ruby" which we have to find inside the array:
n o h t y p s m i a r y c c l l e k s a h r u b y v m e e h h a l l m p c j n i c e r e e k b i p
n o h t y p s m i a r y c c l l e k s a h r u b y v m e e h h a l l m p c j n i c e r e e k b i p
public class WordSearch { public static void main (String[] argv) { char[][] puzzle = { {'n', 'o', 'h', 't', 'y', 'p', 's'}, {'m', 'i', 'a', 'r', 'y', 'c', 'c'}, {'l', 'l', 'e', 'k', 's', 'a', 'h'}, {'r', 'u', 'b', 'y', 'v', 'm', 'e'}, {'e', 'h', 'h', 'a', 'l', 'l', 'm'}, {'p', 'c', 'j', 'n', 'i', 'c', 'e'}, {'r', 'e', 'e', 'k', 'b', 'i', 'p'} }; String result = findWord (puzzle, "ruby"); System.out.println (result); } static String findWord (char[][] puzzle, String word) { // First convert the String into a char array. char[] letters = word.toCharArray (); // Now try every possible starting point in the puzzle array. for (int i=0; i < puzzle.length; i++) { for (int j=0; j < puzzle[i].length; j++) { // Use (i,j) as the starting point. boolean found = true; // Try to find the given word's letters. for (int k=0; k < letters.length; k++) { if ( (j+k >= puzzle[i].length) || (letters[k] != puzzle[i][j+k]) ) { // Not a match. found = false; break; } } // If we went the whole length of the word, we found it. if (found) { return "String " + word + " found in row=" + i + " col=" +j; } } } return "String " + word + " not found"; } }Note:
1. for each possible start location [i,j] of a word in the array 2. Walk along the array horizontally and check if word occurs; 3. if it occurs 4. return location; 5. endif 6. endfor 7. return "not found";
for (int k=0; k < letters.length; k++) { if ( (j+k >= puzzle[i].length) || ( ... ) ) { // ... Not a match ... } }
Exercise 3: Clearly, ruby and java are two programming languages in the puzzle. There are seven more - can you see them?
There are two points of control flow worth examining in the above program:
for (int k=0; k < letters.length; k++) { if ( ... ) { // Not a match. found = false; break; } }
// Now try every possible starting point in the puzzle array. for (int i=0; i < puzzle.length; i++) { for (int j=0; j < puzzle[i].length; j++) { // Use (i,j) as the starting point. boolean found = true; // Try to find the given word's letters. for (int k=0; k < letters.length; k++) { // ... } // If we went the whole length of the word, we found it. if (found) { return "String " + word + " found in row=" + i + " col=" +j; } } }
public class WordSearch2 { public static void main (String[] argv) { char[][] puzzle = { {'n', 'o', 'h', 't', 'y', 'p', 's'}, {'m', 'i', 'a', 'r', 'y', 'c', 'c'}, {'l', 'l', 'e', 'k', 's', 'a', 'h'}, {'r', 'u', 'b', 'y', 'v', 'm', 'e'}, {'e', 'h', 'h', 'a', 'l', 'l', 'm'}, {'p', 'c', 'j', 'n', 'i', 'c', 'e'}, {'r', 'e', 'e', 'k', 'b', 'i', 'p'} }; // Now findWord() doesn't return anything. findWord (puzzle, "ruby"); } static void findWord (char[][] puzzle, String word) { char[] letters = word.toCharArray (); // We'll need two "flag" variables. boolean over = false; boolean found = false; // Note the second condition in each for-loop: for (int i=0; (i < puzzle.length) && (!over); i++) { for (int j=0; (j < puzzle[i].length) && (!over); j++) { // Use (i,j) as the starting point. found = true; // Note the additional condition: for (int k=0; (k < letters.length) && (found); k++) { if ( (j+k >= puzzle[i].length) || (letters[k] != puzzle[i][j+k]) ) { // Not a match. found = false; } } // If we went the whole length of the word, we found it. if (found) { System.out.println ("String " + word + " found in row=" + i + " col=" +j); // Mark the end of the double-for here. over = true; } } } // We print outside the double-for. if (! found) { System.out.println ("String " + word + " not found"); } } }Note:
It is possible, with a little thinking, to use a single flag for both purposes:
static void findWord (char[][] puzzle, String word) { char[] letters = word.toCharArray (); // Use a single flag variable. boolean found = false; for (int i=0; (i < puzzle.length) && (!found); i++) { for (int j=0; (j < puzzle[i].length) && (!found); j++) { // Use (i,j) as the starting point. found = true; // Try to find the given word's letters. for (int k=0; (k < letters.length) && (found); k++) { if ( (j+k >= puzzle[i].length) || (letters[k] != puzzle[i][j+k]) ) { // Not a match. found = false; } } // If we went the whole length of the word, we found it. if (found) { System.out.println ("String " + word + " found in row=" + i + " col=" +j); } } } if (! found) { System.out.println ("String " + word + " not found"); } }
Sometimes it's hard to read code with complicated for-loop conditions. An alternative is to use an additional if-statement:
static void findWord (char[][] puzzle, String word) { char[] letters = word.toCharArray (); boolean found = false; for (int i=0; i < puzzle.length; i++) { for (int j=0; j < puzzle[i].length; j++) { if (! found) { // ... same as before ... } // end-if } } if (! found) { System.out.println ("String " + word + " not found"); } }
For comparison, let's look at how to implement the outer loops using while:
static void findWord (char[][] puzzle, String word) { char[] letters = word.toCharArray (); boolean found = false; int i = 0; while ( (i < puzzle.length) && (! found) ) { int j = 0; while ( (j < puzzle[i].length) && (! found) ) { // ... same as before ... // Must increment j j ++; } // Must rememeber to increment i. i ++; } if (! found) { System.out.println ("String " + word + " not found"); } }The while-loops are a little more difficult to read, and one must be careful to increment (otherwise, we'd have an infinite loop).
Next, suppose we had to write the method findWord to return the actual location where the word is found:
class Coord { int i, j; }
Coord c = findWord (puzzle, "ruby"); if (c == null) { System.out.println ("Not found"); } else { System.out.println ("Found in row " + c.i + " column " + c.j); }
class Coord { int i, j; } public class WordSearch6 { public static void main (String[] argv) { char[][] puzzle = { {'n', 'o', 'h', 't', 'y', 'p', 's'}, {'m', 'i', 'a', 'r', 'y', 'c', 'c'}, {'l', 'l', 'e', 'k', 's', 'a', 'h'}, {'r', 'u', 'b', 'y', 'v', 'm', 'e'}, {'e', 'h', 'h', 'a', 'l', 'l', 'm'}, {'p', 'c', 'j', 'n', 'i', 'c', 'e'}, {'r', 'e', 'e', 'k', 'b', 'i', 'p'} }; Coord c = findWord (puzzle, "ruby"); if (c == null) { System.out.println ("Not found"); } else { System.out.println ("Found in row " + c.i + " column " + c.j); } } static Coord findWord (char[][] puzzle, String word) { // First convert the String into a char array. char[] letters = word.toCharArray (); // Now try every possible starting point in the puzzle array. for (int i=0; i < puzzle.length; i++) { for (int j=0; j < puzzle[i].length; j++) { // Use (i,j) as the starting point. boolean found = true; // Try to find the given word's letters. for (int k=0; k < letters.length; k++) { if ( (j+k >= puzzle[i].length) || (letters[k] != puzzle[i][j+k]) ) { // Not a match. found = false; break; } } // If we went the whole length of the word, we found it. if (found) { // Make an instance of the Coord class, set it's values and return it. Coord c = new Coord (); c.i = i; c.j = j; return c; } } } return null; } }
As an alternative to creating our own class, we could use the Point class in Java's library:
Point p = new Point (); p.x = 5; p.y = 6;
import java.awt.*; public class WordSearch7 { public static void main (String[] argv) { // ... puzzle array ... Point p = findWord (puzzle, "ruby"); if (p == null) { System.out.println ("Not found"); } else { System.out.println ("Found in row " + p.x + " column " + p.y); } } static Point findWord (char[][] puzzle, String word) { // ... similar to WordSearch6.java, except for using Point // instead of Coord ... } }
What is a matrix?
1.0 2.45 2.1 0.76 1.3 7.72And this one has 5 rows and 2 columns
1 2 4 5 7 8 10 11 13 14
| 1 2 | | 3 0 | | 4 5 | | 1 1 | A = | 7 8 | B = | 0 2.2 | | 10 11 | | 0 1 | | 13 14 | | 0.1 0 |
| 1 2 | | 3 0 | | 4 2 | | 4 5 | | 1 1 | | 5 6 | A + B = | 7 8 | + | 0 2.2 | = | 7 10.2 | | 10 11 | | 0 1 | | 10 12 | | 13 14 | | 0.1 0 | | 13.1 14 |
public class Matrices { public static void main (String[] argv) { // Make two samples matrices. double[][] A = { {1, 3, 5}, {7, 9, 11}, {13, 15, 17}, }; double[][] B = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, }; // Addition. double[][] C = add (A, B); System.out.println ("Sum C = A + B: "); print (C); // Multiplication. double[][] D = multiply (A, B); System.out.println ("Product D = A * B: "); print (D); } static void print (double[][] X) { for (int i=0; i < X.length; i++) { for (int j=0; j < X[i].length; j++) { System.out.printf (" %4.1f", X[i][j]); } System.out.println (); } } static double[][] add (double[][] X, double[][] Y) { // See if the matrices are compatible. if ( (X.length != Y.length) || (X[0].length != Y[0].length) ) { System.out.println ("Incompatible matrices"); System.exit(0); } // Create space for the result. double[][] Z = new double [X.length][X[0].length]; // Simple element-by-element addition. for (int i=0; i < X.length; i++) { for (int j=0; j < X[i].length; j++) { Z[i][j] = X[i][j] + Y[i][j]; } } return Z; } static double[][] multiply (double[][] X, double[][] Y) { // Check that we can multiply. if ( (X.length != Y[0].length) && (X[0].length != Y.length) ) { System.out.println ("Incompatible matrices"); System.exit(0); } // Space for result. double[][] Z = new double [X.length][Y[0].length]; // For (i,j)-th element of Z, multiply i-row of X by j-th row of Y. for (int i=0; i < Z.length; i++) { for (int j=0; j < Z[i].length; j++) { // This is the row x col result. double sum = 0; for (int k=0; k < X[0].length; k++) { sum += X[i][k] * Y[k][j]; } Z[i][j] = sum; } } return Z; } }
Exercise 3: Just as we define powers of a single number such as 23 = 8, we can define powers of a matrix A:
| 1 0 0 | I = | 0 1 0 | | 0 0 1 |
We'll examine how to work with 2D arrays of Integer's using a simple application:
0.4, 0.6, 1.1, 1.3, 1.4, 1.5, 1.6, 2.2, 3.1, 3.3
first interval: [0, 1] second: [1, 2] third: [2, 3] fourth: [3, 4]
histogram[0] = 2; // 2 points lie in [0, 1] histogram[1] = 5; // 5 points lie in [1, 2] histogram[2] = 1; // 1 point lies in [2, 3] histogram[3] = 2; // 2 points lie in [3, 4]
import java.util.*; import java.awt.*; // A class to hold an (x,y) pair where both are double's. class Pointd { double x, y; } public class Histogram2D { public static void main (String[] argv) { // Number of points to generate. int numSamples = 1000; Pointd[] points = new Pointd [1000]; // Generate random points from a Gaussian distribution // where both x and y are in the range [0,4]. for (int n=0; n < numSamples; n++) { points[n] = nextGaussian (); } // Compute and print the histogram. Integer[][] gaussianHist = makeHistogram (points); print (gaussianHist); // Do the same for points generated from a uniform distribution. for (int n=0; n < numSamples; n++) { points[n] = nextUniform (); } Integer[][] uniform = makeHistogram (points); print (uniform); } static void print (Integer[][] H) { System.out.println ("Histogram "); for (int i=0; i < H.length; i++) { for (int j=0; j < H[i].length; j++) { // Note: use of printf for formatting. System.out.printf (" %3d", H[i][j]); } System.out.println (); } } static Integer[][] makeHistogram (Pointd[] points) { // Size of histogram. int size = 10; // Make the space for it, using instances of java.lang.Integer. Integer[][] hist = new Integer [size][size]; // Initialize: must create an instance. for (int i=0; i < size; i++) { for (int j=0; j < size; j++) { hist[i][j] = new Integer (0); } } // Size of each cell. double interval = 4.0 / size; // Now count. for (int n=0; n < points.length; n++) { int i = (int) (points[n].x / interval); int j = (int) (points[n].y / interval); // Note: increment operator works for Integer's. hist[i][j] ++; } return hist; } // Class Random is in java.util. Needed below static Random rand = new Random (); static Pointd nextGaussian () { // ... We don't need to understand how this works... } static Pointd nextUniform () { // ... We don't need to understand how this works... } }Note:
int[][] hist = new int [size][size];creates the space needed, we need one more step with objects like Integer's:
// This creates a 2D array of pointers, each initialized to null. Integer[][] hist = new Integer [size][size]; // Each such pointer must be made to point to an Integer instance. for (int i=0; i < size; i++) { for (int j=0; j < size; j++) { hist[i][j] = new Integer (0); } }Here:
Integer[][] hist = new Integer [size][size];merely creates space for size*size pointers.
hist[i][j] ++;
Finally, since we've discussed Integer's, let's look at an example that shows how Java lets you mix Integer's and int's:
public class IntegerExamples { public static void main (String[] argv) { // Instance of Integer: Integer I = new Integer (5); // Ordinary int: int i = 6; // Integer + int, with result stored in an int. int n = I + i; // The same with the result stored in an Integer. Integer N = I + i; // Comparison operator across types. if (N == n) { System.out.println ("N=" + N + " n=" + n); } // See how the add method is declared. Integer S = add (i, I); System.out.println (I + "+" + i + "=" + S); } static int add (int a, int b) { return (a + b); } }