lecture 9: technique. exercise create a method called duplicates that returns a boolean value. the...

52
CSCI1227 Computer Programming and Problem Solving Lecture 9: Technique

Upload: claude-mccoy

Post on 18-Jan-2016

220 views

Category:

Documents


0 download

TRANSCRIPT

Slide 1

CSCI1227Computer Programming andProblem Solving Lecture 9: Technique ExerciseCreate a method called duplicates that returns a boolean value. The method takes, as its argument, an array of strings and returns true if the array contains any duplicates. The method returns false otherwise.Solutionpublic static boolean duplicates(String[] s) { for (int i = 0; i < s.length() 1; i++) { for (j = i + 1; j < s.length(); j++) { if (s[i].equals(s[j])) return (true); } } return (false);}

Solutionimport java.util.Arrays;

public static boolean duplicates(String[] s) { Arrays.sort(s); for (int i = 0; i < s.length() 1; i++) { if (s[i].equals(s[i+1])) return (true); } return (false);}

Subtypes (Ch. 8)We can extend types (e.g., classes) by adding stuff to them to create subtypesThe subtype/subclass has everything the supertype/superclass (its parent) has, but it has extra methods and data

Two important conceptsImplementation Inheritance: A subclass inherits and can use all the methods and data fields of its parentInclusion Polymorphism: An instance of a subclass can be used anywhere an instance of the parent (superclass) can be used

Interfaces (Ch. 8)We can name an interface and save it in a Java fileOther classes can implement the interfaceProvides programmers with knowledge of the methods that the class will provideMakes ADTs more formalpublic interface printable { ... }public class photograph implements printable { ... }Binary Search (11.2)Requires sorted dataCheck the middle itemif (key > middle) know it is in upper half so discard lower halfif (key < middle) know it is in lower half so discard upperif (key == middle) well, we found it, we're doneRepeat on the half we have left until we find it or there is nothing left!If we do this to completion, we will take at most log2(data.length) comparisons at mostBinary Search Implementedpublic static int bsearch (int[] data, int val) { int lo = 0, hi = data.length 1, mid;

while (lo data[mid]) { lo = mid + 1; } else { return (mid); } } return (-1) ;} // end bsearch()Recursion (Ch. 11)Recursion is an advanced technique that can be used to make programs simplerSimply put, recursion is when a method that calls itselfRecursion can be used, mostly in place of complex loops, for problems in that can be broken down into Identical sub-problems for which the data is simpler/smallerBinary search is a good example

Not tested OK not to know thisthe bonus on the final will use recursionExamplesString equality:-- first characters the same-- rest of the strings are the sameSumming an array-- add the first value to the sum of the rest of the arrayPalindrome-- the first character == the last character -- the rest of the string is a palindromePrinciplesThere must be a way to create a smaller identical sub-problemThere must be some way to solve the part we removed and combine its solution with that of the subproblemThere must be some sort of base-case, a most simple situation where there are no more subproblems and the process can stopExamplepublic boolean palindrome (String s) { if (s.length() < 2) return true; else return ( (s.charAt(0) == s.charAt(s.length()-1)) && palindrome (s.substring(1,s.length()-1)) );}General ApproachIdentify the Base Case: When we can stop recursingFor the other cases:SimplySolve sub-problemsCombine solutions to sub-problems

Note: Recursive solutions can be very short and elegant, but can also (sometimes) be very inefficientExample 2public boolean reverse (String s) { if (s.length() < 2) return s; else return ( s.charAt(s.length()-1)) + reverse (s.substring(0, s.length()-1)) );}

Idea: Add the last character to the front of the rest of the string when its reversedBinary Search Recursivepublic int bsearch (int key, int[] data, int lo, int hi){ int mid = lo + (hi - lo) / 2; if (lo > hi) return (-1); else if (key < data[mid]) return (bsearch(key, data, lo, mid-1)); else if (key > data[mid]) return (bsearch(key, data, mid+1, hi)); return (mid); // were equal

} // end bsearch()ExerciseWe have seen the Fibonacci numbers before. They are:1, 1, 2, 3, 5, 8, 13, 21, ...By definition, fib(1) = 1fib(2) = 1fib(n) = fib(n-1) + fib(n-2)Write a recursive method to find the ith Fibonacci number.public static int fib(int i) {...}Solutionpublic static int fib(int i) { // base cases, i = 1 or 2 if (i < 3) return (1); // recursive case return (fib(i-1) + fib(i-2));}

Note: This is one of the instances where recursion is very elegant but sadly is also very inefficient Inefficiency fib(5)= fib(4) + fib(3)= (fib(3) + fib(2)) + (fib(2) + fib(1))= ((fib(2) + fib(1)) + fib(2)) + (fib(2) + fib(1))= 1 + 1 + 1 + 1 + 1= 5

Problem: fib(3) is calculated more than oncefib(9) = fib(8) + fib(7) = fib(7) + fib(6) + fib(6) + fib(5) = fib(6) + fib(5) + fib(5) + fib(4) + fib(5) + fib(4) + fib(4) + fib(3)

Some, not all, recursion is inefficient

TESTINGThe LAST LINE OF DEFENSE!Relying on testing is dangerous and leads to low-quality productsBe Proactive NOT ReactiveGood design and planning prevents faults and is far better than relying on testing# of Faults # LOCFaults are often clustered: Where there is one, there is usually anotherFixing faults frequently introduces new faults, or reveals previously undetectable faultsFaults frequently NOT fixed because the actual problem has not been identifiedApproachesThere are lots of testing methodologiesWe are going to look at twoCode Coverage focuses on control flowData Driven focuses on data valuesBoth can (and should) be usedNeither is 100% effectiveCode CoverageYour test data should be such that every line of code is executed at least once in the test processEvery method is calledEvery branch of every if statement is usedEvery loop executes at least onceMore difficult than it sounds for complex programs with things nested many timesOnly works when you can examine the code, won't work for library codeData DrivenYour test suite should contain data thatMatches an average/typical useIs an extremee.g., maximum int, minimum int, largest array, empty arrayIs a critical value that could cause different behavioure.g., 0 is special in a lot of math Difficult to find these values, requires a lot of insight and experienceExamplepublic static boolean compare(String s1, String s2) {}

Data to tests1 is "" or null, s2 is "" or null, and all combos of thiss1 = s2, s1 s2s1 is a prefix, suffix, substring of s2 (and same for s2 with respect to s1)Strings are 1 character, 10 chars (average), thousands of characters (long)

Now, create data that does this for EVERY method in the program ... as you should see, testing is very time consuming and difficultSystem TestingFunctional TestingDoes the software do what its supposed?Try to minimise the number of tests while still testing the software fullyOptimal (complete) testing is unfeasible!Too many pathsToo many different input combinations (including bad data)Often uses half of software development time and budgetWriting the program is half the job, ensuring its right is the other halfNon-Functional Testing(Performance)Stress max users, requests, devicesVolume max dataConfiguration all hardware combinationsCompatibility with other systemsRegression with system being replacedSecurity is it safeTiming performance, efficiency, response speedEnvironmental use site (heat, humidity, vibration)Quality MTF, MTBFRecovery equipment failure, data lossMaintenance diagnostic tools and procedures workDocumentation sufficient/correct documentation existsHuman factors usabilityRegression TestingTests applied when a working version is changedVerifies it functions in the same manner as the previous versionShould be used in iterative/incremental developmentWhen we add code:Regression test does it do what it used to do?Functional testing does it do the new stuff right?Performance testing is it fast enough, secure, and usable?Acceptance TestingConvince the customer it worksGenerally involves the requirements team Benchmark Test: Typical Cases representative of usePilot Test: Use on an experimental basis Alpha Testing: Testing of installed software by development organisation (e.g., programmers or professional testers)Beta Testing: Testing of installed software by client organisation (e.g., users)Parallel Testing: New system run in parallel with existing system and results comparedFault ReportsAdmin tester, test #, site, equipment, dateLocation where did it occurTiming any details possible, sequencingSymptom how you knew it was a faultEnd-result failure caused, consequencesMechanism how to make it happen againCause type of error that caused itSeverity with respect to performance and application domainCost to fix and its impact on application domainNot all of these can be clearly answeredComponents often overlap

REPRODUCIBILITY is the key!Fault (Bug) TrackersAlso known as issue trackersLarge number existGenerally an interface to a DB systemWide variety of featuresMay be integrated with other systems (RCS, IDE)

http://en.wikipedia.org/wiki/Comparison_of_issue-tracking_systemsThoughtsTesting cannot remove all faultsSafety, reliability, and security are NOT the same thingAssume users will make every mistake possible (and then some)Review, review, and then review againShort-term costs should not overshadow long-term risks and costsExerciseThe median of a data set is the middle value when the values are sorted. If there is an even number, it is the average of the two middle values. Write a method called median() that finds the median of an array of doubles. Solutionpublic static double median(double[] data) { int len = data.length, half = len / 2; double[] d = new double[len]; for (int i = 0; i < len; i++) { d[i] = data[i]; } Arrays.sort(d); if ((len % 2) == 0) { return ((d[half-1]+d[half])/2.0); } else { return (d[half]); }}Cutting a Problem in HalfThe concept of binary search is based on the idea of cutting a problem in half Each time we check a value, we can get rid of half the remaining dataThe same principle can be used in other placeOnce place where it can be used is to solve almost any math equation using a technique called bisection

BisectionNeed a range, (lo..hi), that we know the answer must be inThus: lo < answer < hiThen we solve the equation just like binary searchTest midpoint of the rangeIf too low, then raise lo to midIf too high, then lower hi to midOtherwise we found the answer

Finding a Cube RootWe want to implement cubeRoot(x)No provided Math function for thisWe can random try different answers, say yif (y * y * y) > x, y is too bigif (y * y * y) < x, y is too smallif (y * y * y) == x, then we know y is the cubeRoot of xWe know y must be between 0 and x (ignore negative roots for this example)So, we apply bisection to get an answerImplementationpublic static double cubeRoot (double x) {

double lo = 0, hi = x, mid, midCubed;

while (true) { mid = (hi + lo) / 2.0; midCubed = mid * mid * mid; if (Math.abs(x-midCubed) < .00001) return (mid); else if (midCubed > x) hi = mid; else // ans < x otherwise lo = mid; } // end while

} // end cubeRoot()Getting CloseWe can almost never get the exact right answerFloating point math is never preciseE.g., (1.0 / 3.0) * 3.0 1.0Thus, we only ever try to get closeif ((desired found) < .00001) ...desired might be smaller than found if ((found - desired) < .00001) ...We generally combine the two testsif (Math.abs(found-desired) < .00001) ...Can use a much smaller tolerance than .00001 if we want more accuracy

Generalising Bisectionpublic static double solve () { // solves: x^2 + 4 = 100 double lo = -1000.0, hi = 1000.0, mid, found;

while (true) { mid = (hi + lo) / 2.0; found = (mid * mid) + 4.0; // Equation we are solving if (Math.abs(found 100.0) < .00001) // .00001 is the tolerance return (mid); else if (found > x) hi = mid; else // found < x otherwise lo = mid; } // end while

} // end solve()

ONLY THE COLOURED STUFF CHANGES!Testing BisectionHow do we test this method?Code coverage ensure every line is executedHARD depends on factors we can't easily predictWhat inputs will cause all branches to be taken?Data driven 0: interesting to multiply1: all roots are always 17, 8, 9: values a known cube, 2.007, .008, .009: values a known cube, .2Returning Multiple ValuesIf we want a method to return more than one value we have several optionsIf they are the same type (e.g., both int) we could use an arraySometimes we can combine them, return them, and then them aparte.g., concatenate two strings with a symbol between them so we know where to break them into two strings laterWe can create a new object that stores the values and returns that (no limitations, always works)Example 1public static String[] getNames1() { String[] names = new String[2]; Scanner s = new Scanner (System.in); System.out.print("First name: "); names[0] = s.next(); System.out.print("Last name: "); names[1] = s.next(); return (names);}Application 1String[] names = getNames1();System.out.printf("%s %s\n", names[0], names[1]);

Requires the user of getNames1() to know that the first name is in index 0 and the last name is in index 1

Example 2public static String getNames2() { Scanner s = new Scanner (System.in); System.out.print("First name: "); String n1 = s.next(); System.out.print("Last name: "); String n2 = s.next(); return (n1 + "&" + n2);}Application 2String names = getNames2();int marker = names.indexOf("&");System.out.printf("%s %s\n", names.substring(0,marker), names.substring(marker+1));

Requires the user of getNames2() to know that the names are separated by the character &

Example 3public class fullname { String first, last;} public static fullname getNames3() { fullname n = new fullname(); Scanner s = new Scanner (System.in); System.out.print("First name: "); n.first = s.next(); System.out.print("Last name: "); n.last = s.next(); return (n);}Application 3fullname n = getNames3();System.out.printf("%s %s\n", n.first, n.last);

Requires the user of getNames3() to know that the details of the class fullname

QueuesA Queue is an ADT that functions like a bank line-up. Where a stack was FILO (first in, last out), a queue is FIFO (first in, first out)Instead of push and pop, the interface methods are enqueue and dequeueWe usually implement a queue using a "circular array"That is, when we hit the back of the array, we wrap around to the front of the array. ExerciseImplement a queue given the following interface:

public interface FIFO { public void enqueue(int val) throws Exception; public int dequeue() throws Exception; public int count(); }

count returns the number of items currently in the queue.Add a constructor that initialises an empty queue to a provided size. (Note, interfaces in Java can't contain constructors). Solutionpublic class queue implements FIFO { private int numItems, head, tail; private int[] data; public queue (int size) { data = new int[size]; head = 0; tail = 0; numItems = 0; } public void enqueue(int val) throws Exception { if (numItems == data.length) throw Exception ("Overflow"); data[tail++] = val; numItems += 1; tail = tail % data.length; } public int dequeue() throws Exception { int v; if (numItems == 0) throw new Exception ("Underflow"); v = data[head++]; head = head % data.length; numItems -= 1; return (v); } public int count() { return (n); } }

To DoRead Chapter 11 on Recursion (optional)Assignment solutions are in the process of being posted onlineStart preparing for the final examProgram, Program, ProgramDo Assignment 9 in the lab today

CHANGE: You may bring as many pages as you want of HANDWRITTEN notes to the final exam! No 2 page limit now exists. Make all the notes you want