CS62 - Spring 2010 - Lecture 9

  • How would you use your OnDiskSort class that you're developing?
       - It's not always that case that you actually write a program
       - Sometimes you'll just write a class or a set of classes that provides some functionality to someone else
       - You will still need to test it yourself, likely by writing your own main method, though
       - public OnDiskSort(int maxSize, File workingDirectory, Sorter<String> sorter)
       - public void sort(Iterator<String> dataReader, File outputFile)

  • A few follow-up items from last time
       - only need a single forward slash '/' in filenames
          - if you're on windows, need 2, to '\\' to avoid confusion with special characters
       - how does an Iterator know when the data structure it's modifying has been changed?
          - http://java.sun.com/j2se/1.5.0/docs/api/java/util/ConcurrentModificationException.html
          - data structure dependent
             - For any list (ArrayList, Vector, LinkedList, etc), there is a protected field called modCount that is the number of times this list has been structurally modified (http://java.sun.com/j2se/1.5.0/docs/api/java/util/AbstractList.html)

  • number representation in Java
       - As we've mentioned before, numbers are stored in binary
       - For example, the first few binary numbers are:
          0 = 0
          1 = 1
          2 = 10
          3 = 11
          4 = 100
          5 = 101
          6 = 110
          7 = 111
          8 = 1000
       - To calculate the value of a binary number:
          - digit/position i in the binary number represents a value of 2^i
             - this is similar to decimal number representation where digit/position i represents a value of 10^i
          - sum up the value of all of the digits where there is a 1
             - 101 = 2^0 + 2^2 = 5
             - 11011 = 2^0 + 2^1 + 2^3 + 2^4 = 1 + 2 + 8 + 16 = 27
             - 101010 = 2^1 + 2^3 + 2^5 = 2 + 8 + 32 = 42
          - In Java, there is another version of Integer.parseInt where we can pass a second parameter that is the "base" of the number we're trying to interpret. For binary, the base is 2.
             - System.out.println(Integer.parseInt("101010", 2));
                - 42
       - ints in Java are stored with 32 bits and longs with 64 bit
       - for now, we won't talk about how negative numbers are represented, but the basic idea is to use one of the bits to represent the sign and then the remaining bits for the number portion (it's a bit more complicated than this... Java uses a twos-complement representation for those who took cs52)

  • bitwise operators in Java
       - What is an operator?
          - operators are special built-in functions that perform operate on built-in data types (int, double, long, String)
           - e.g. mathematical operators: +, -, *, /, %
       - There are also operators that perform operations on Java numbers (specifically integers) at the bit level

  • bit shifting operators
       - << (left shift)
          - n << p shift the number n, p bits to the left, filling in the lower order bits with zeros
          - examples
             - 2 << 1 = 10 << 1 = 100 = 4
             - 5 << 1 = 101 << 1 = 1010 = 10
             - 6 << 3 = 110 << 4 = 110000 = 48
          - shifting the bits up one is equivalent to doubling the number
          - so, shifting the bits up p positions is equivalent to doubling the number p times, or multiplying by 2^p      - >> (right shift)
       - >> (right shift)
          - n >> p shift the number n, p bits to the right, throwing away the lower order bits
          - examples
             - 2 >> 1 = 10 >> 1 = 1
             - 5 >> 1 = 101 >> = 10 = 2
             - 13 >> 2 = 1101 >> 2 = 11 = 3
             - 14 >> 2 = 1110 >> 2 = 11 = 3
          - shifting the bits down one is equivalent to dividing the number by 2 and rounding down
          - so, shifting the bits down by p positions, is equivalent to divising by 2 p times, or dividing by 2^p and rounding down
       - be careful about overflow errors because of shifting too far to the left or right. Remember, an int is only 32 bits (and really 31 with a sign bit).

  • bitwise logical operators
       - we've seen && and || for booleans, there are equivalent operators on the bit level
       - show truth tables for "AND" and "OR"
       - the '&' and '|' operator perform bitwise "AND" and "OR" between two numbers
          - 14 & 3 = 1110 & 11 = 10 = 2
          - 14 | 3 = 1110 | 11 = 11 = 3
          - 12 & 5 = 1100 & 101 = 100 = 4
          - 12 | 5 = 1100 | 101 = 101 = 5
       - and, if you're curious '^' is the bitwise "XOR" operator

  • uses of bitwise operators
       - how can we get, say the lower 4 bits of a number?
          - '&' the number with 15 = 1111: we'll keep the lower 4 bits, but throw away everything else
          - we could also mod (%) by 16
       - how can we get the nth bit from a number (if we start counting bit positions at 0)?
          - num >> n & 1: shift the number n places to the right then select the lower order bit
       - how can we tell if a number is odd or even (using bit operators)?

  • why use bitwise operators?
       - there are some situations where it will make our life easier to use them
       - bitwise operators are, in general, faster than doing the equivalent operations in other ways (for example, with mathematical operators)

  • ternary operator
       - while we're talking about operators, one more interesting operator is the ternary operator
       - say we have two variables "first" and "second" and we want to assign the value of the larger of the two to a third variable "largest". How would we do it?
          - one way:
             if( first > second ){
                largest = first;
             }else{
                largest = second;
             }
          - another way:
             largest = first > second ? first : second;
          - the ternary operator takes three parts:
             <condition> ? <expression1> : <expression2>
             - if the condition is true, the value of the ternary expression is <expression1>
             - if the condition is fals, the value of the ternary expression is <expression2>
             - you can simplify many statements using this

  • two towers problem (see section 8.7 from the book for another view of this problem)
       - someone gives you a bunch of square blocks with areas a_1, a_2, a_3, ..., a_n and asks you to build two towers using all of these blocks such that the height of the two towers are as close as possible
       - how can we solve this problem?
          - what is the overall combined height of the blocks?
             - h = \sum_i=1^n sqrt(a_i)
          - can we rephrase the problem with respect to just one tower?
             - we'd like to build a tower that is as close as possible to h/2 using whatever blocks we'd like
             - the remaining blocks would then also be close to h/2 as well
          - unfortunately, one of the best approaches is a brute force approach
             - enumerate all possible subsets of the blocks
             - for each subset, calculate how close it is to h/2
             - return the subset that is closest to h/2
          - How many subsets are there for n items?
             - 2^n
          - Can we leverage our knowledge of binary numbers to help us here?
             - let's say we have just 3 elements that we want to enumerate, what are the subsets?
                - 000: {}
                - 001: {1}
                - 010: {2}
                - 011: {1 2}
                - 100: {3}
                - 101: {1 3}
                - 110: {2 3}
                - 111: {1 2 3}
             - Given this, if we have n items, how might we enumerate all 2^n items?