Consider letting the less experienced member of the group do the typing if it seems like there's a mismatch in experience or comfort levels. That will help make sure they don't get left behind. Worst case, flip a coin to see who does the typing. Or have Java generate a random boolean value. (Type "(new java.util.Random()).nextBoolean()
" in the codepad without the double quotes.)
At the conclusion of the lab, make arrangements for the typer to share a copy of the code with the other member(s) of the group if desired. (E.g. email it, or put it on a shared Google drive, etc.) My solutions to the lab will get posted as well.
Searches
and SearchTester
classes. Our recursive searching methods from class are in Searches
, and there's some code to help test out those search methods in the SearchTester
class.
Searches
class and find the binarySearch
method. Take a moment to review the code and figure out how it works: Look at the parameters and think about how they're used. Find the base case and think about when it applies and what it's returning. Consider the recursive case(s). Why are there two recursive calls? Might they both execute, or just one or the other? If the latter, how are we deciding which to use?
[10,12,14,16,18,20,22,24,26,28,30]
. Do not actually run it yet. Instead, use a piece of scratch paper or an online tool to help keep track of the recursive calls as you simulate its execution. Make sure you write down how left and right are changing as we make each recursive call, and think about how we "unwind" all of those recursive calls after hitting the base case.
binarySearch
that displays the values of left and right. If you're feeling ambitious, you could also put print statements above each of the return statements, making it clear which return statement is about to be executed. (It might make sense to print out left and right for these as well, so you can more easily see which recursive call is about to return.)
Once you've got print statements in your binarySearch
method, run the main method in SearchTester
. It will build that same array and search for 22 so you can see the output. Compare the actual behavior to your predictions, and ask for help if you have any questions before proceeding.
Searches
class, and methods for accessing that count. Your next task is to modify binarySearch
so it counts the number of comparisons performed during a search. Make sure you increment the counter for every comparison between data values! (Don't bother counting how many comparisons get done as part of the test at the top of the loop — we're only interested in how many times we inspect data in the array.) You can ignore the commented-out lines for now, but later it might be interesting to add them back and see how that changes things. Verify that your counter is getting updated as expected before proceeding.
binarySearch
via the codepad, you'll see that there's a bit of variation in the number of comparisons for searches on a given array, depending upon which values we search for, but the variation is small enough we can ignore it for now. As an "extra" at the end of the lab, you could get a more precise estimate by running a handful of searches and averaging the number of comparisons required across the set of searches. Below, notice that sometimes it takes 3 comparisons and sometimes 4:
> int[] nums = {2,5,6,8,9,11,15,17,18,20,21,23}; > Searches s = new Searches(); > s.getCount() 0 (int) > s.binarySearch(nums, 23, 0, 12) true (boolean) > s.getCount() 4 (int) > s.clearCount(); > s.binarySearch(nums, 8, 0, 12) true (boolean) > s.getCount() 3 (int) > s.clearCount(); > s.binarySearch(nums, 200, 0, 12) false (boolean) > s.getCount() 4 (int)
Finish defining the measureScaling
method in the SearchTester
class. At the moment, it sets up a search over an array of size 100 and then prints the results. (It's printing the array size and the number of comparisons separated by a tab character, so that later it'll be easy to copy our results and paste them into a Google sheet.) Put a loop around that search code so that the problem size, n, is being increased each time through the loop. The number of comparisons changes pretty quickly at first, and then gets more stable. In my code I did searches up to size 1000 with a step size of 2, and then switched to a second loop that did searches from 2000 to 1,000,000 with a step size of 1000, but you're welcome to take other approaches. Try to measure up to some pretty large array sizes though.
Searches
class and find the findPosition
method. Notice that the signature differs from binarySearch
. For one thing, it takes a list of integers rather than an array. (This makes it more like your assignment, where you'll be working with lists instead of arrays.) It also returns an int
instead of a boolean value. The plan here is that we'll return the index of the "final box" we end up on after doing a binary search, but that we won't actually check the contents of that box. This makes the method potentially more useful than binarySearch
— we could still use it to narrow down the search to a single box and then do the final check outside of findPosition
, but we could also use the information it returns to insert at that position.
binarySearch
into the body of findPosition
as a starting point, then edit it so that all of the array references are instead using the appropriate list operations. Then think about how to modify the code so that it returns the index of the "final box" when the search is complete. Test your code before proceeding. In the interactions below, for example, I'm verifying that findPostion
returns the proper index for each of the values in the array. But it returns useful information for values that aren't in the array as well. For example, when I asked where the 9 was in [10,12,14], it reported that the 9 would be at position 0 in the array if it were there.
> import java.util.ArrayList; > ArrayListnumsList = new ArrayList (); > numsList.add(10); > numsList.add(12); > numsList.add(14); > numsList.toString() "[10, 12, 14]" (String) > Searches s = new Searches(); > s.findPosition(numsList, 10, 0, numsList.size()) 0 (int) > s.findPosition(numsList, 12, 0, numsList.size()) 1 (int) > s.findPosition(numsList, 14, 0, numsList.size()) 2 (int) > s.findPosition(numsList, 9, 0, numsList.size()) 0 (int) > s.findPosition(numsList, 13, 0, numsList.size()) 1 (int) > s.findPosition(numsList, 27, 0, numsList.size()) 2 (int)
insert
method. It takes an ordered list of values, and a new value to be inserted, and inserts the value such that the list remains ordered. You can call findPosition
to see where a value would be if it were present. If it's not already at that position, you can pretty much just do an add
at that position to insert the item. It's not quite that simple though. Notice in the interactions above that findPosition
returned a 0 for 9. If we inserted 9 at position 0 the other values would shift down and we'd have an ordered array. It returned a 1 for 13 though. What would the list look like if we inserted 13 at position 1? (Hint: It wouldn't be ordered.) The findPosition
result gets us close to the right insertion point, but you still need to do a bit more work to figure out exactly where to insert. You can uncomment the block of code at the bottom of the main method to test your insert method once it's complete.
binarySearch
that check whether the item at position mid
is the value we're looking for — this check has the potential to stop the search early, but requires an additional comparison. Fix up your counting code so that it accurately counts comparisons for the revised code, then re-run your tests and see how much things change. We should need fewer iterations of the loop, but each iteration does more comparisons per iteration. Do we end up doing fewer comparisons in total with the new version?
binarySearch
requires varies a bit depending on which item in the array we search for. That wouldn't be the case if the array size was a power of two. Try some tests in the codepad with an array size of eight, for example, and ponder why that makes a difference. You could also write a new version of measureScaling
that only tests array lengths that are powers of two. (There's a Math.pow
method that might come in handy. Math.pow(2,5)
is 32, for example.)