MTH 225 Lecture-Module #08:
(Review) Some Basic Math (Algebra, Functions, etc.) Used in Computer Science


selected elements of our textbook's Appendix and Chapter 3 (etc.)
exponentiation, logarithm, factoring, factorial, floor, ...
some of these things you should have seen but may have forgotten details (;-)
    if so it's OK, that's why reviewing, so you'll remember
some of these things or the notation for them here may appear new to you....
all of these things are used misc. places in Computer Science

exponentiation

bx a.k.a. 
read "b raised to the power x"
yields b*b*b*···*b where the number of "b"s there is x

e.g. 102 is  which has the value 
e.g. 54 is  which is 
e.g. 28 is 2*2*2*2*2*2*2*2 which is 

Laws of Exponents [p.A7 (in Appendix of our textbook)]:
1.  b(x+y)  =  bx * by
2.  (bx)y  ==  b(x*y)
(there are further ones; refer to your prior studies of math...)

questions to check familiarity with this material:

  • bx + bx =?= b(2*x)
    it is true that left-hand side can be simplified


  • 2x + 2x =?= 2(x+1)
    it is true that left-hand side can be simplified

    check:
    28 + 28 == 


    Computer Scientists need to know powers of 2
    (they arise many places such as binary representation, binary sizes of memory, ...)
    as follows:
     e | 2e
    ---+----
     0 | 
     1 | 
     2 | 
     3 | 
     4 | 
     5 | 
     6 | 
     7 | 
     8 | 
     9 | 
    10 | 
    
    obtain higher powers of 2 from those basic ones plus key convenient observation:

    e.g. obtain 231 as follows:
    decompose 31 into 10s plus whatever's left over:
    231 ==  2()
              ==   by Law (i)
    which is 
    which is 
    thus if you ever see some text involving say 231 - 1, you can relatively easily have a sense of how much that is

    incidentally
    2-1 := 
    2-2 := 
    2-3 := 

    logarithm

    inverse of exponentiation is 

    the function logb(z) returns the power to which you need to raise b to get z
    i.e. logb(z) returns value x such that bx := z

    e.g. log2(32) returns 
    because  works for the value x in the expression 2x := 32

    e.g. log10(1000) returns 
    e.g. log2(1000) returns 

    Laws of Logarithms [pages A7-8 (in Appendix of our textbook)]:
    0.  b(logb(z)) == z
    0.5  logb(bx) == x
    1.  logb(x*y)  ==  logb(x) + logb(y)
    2.  logb(xy) == y*logb(x)
    3.  loga(x) == logb(x)/logb(a)
    (there are further ones; refer to your prior studies of math...
    e.g.  logb(x/y)  ==  logb(x) - logb(y)
    )

    e.g. [0]  2log2(32) := 2 := 

    e.g. [3]  log2(1000) := log10(1000)/log10() := /


    e.g. [1]  consider seeking log5(80000)
    well, note:
    80000 == 8*10*10*10*10
                == 8*()*()*()*()
                ==  (gathering powers)
    then log5(80000)  :=  log5()  :=   by (1)
    which is 

    e.g. [2]  log5(243) := 3*log5(24) := 3* := 


    for calculus and related areas of Mathematics,
    the main base used for logarithms is "e := 2.71828..."
    so they use an abbreviation "ln()" for "loge()"
    similarly Computing Sciences has a main base used for logarithms: 
    so we may use just "log()" for "log2()"
    or in some places "lg()"
    e.g. lg(8) := 
    e.g. lg(32) := 

    factoring numbers

    some numbers factor into smaller ones
    e.g. 15 := 
    e.g. 22 := 
    e.g. 42 :=  := 
    e.g. 18 := 
    e.g. 40 := 
    e.g. 64 := 
    some numbers do not factor into smaller ones, in which case we call them 
    e.g. 
    e.g. 
    e.g. 
    (1 not called "prime" because not significant as a factor for other numbers)

    a number n is called "composite" iff .
    e.g. 18 is composite

    to completely/thoroughly factor a number into all its primes,
    split into parts
    e.g.:
            40
           /  \
          /    \
         4   *  10
        / \    /  \
       2 * 2  2  * 5
    
    or:
    first divide out and save all the factors of the first prime ,
    then divide out and save all the factors of the next prime ,
    then divide out and save all the factors of the next prime ,
    then divide out and save all the factors of the next prime ,
    then divide out and save all the factors of the next prime ,
    and so on
    in our textbook, primes less than 100 are listed at bottom page 210 in Section 3.5:
    2, 3, 5, 7, 11, ...

    can easily check divisibility for first three primes 2,3,5:
    * a number is divisible by 2 iff its last digit is 
    * a number is divisible by 3 iff the sum of its digits is divisible by 3
        can repeat if sum of digits isn't one digit
    * a number is divisible by 5 iff 
    to check divisibility by larger primes, you need to do the division and check whether it comes out even
    DON'T BOTHER CHECKING PRIMES WHOSE SQUARES ARE LARGER THAN NUMBER
        if reach this point, number is prime

    e.g. for 24:

    e.g. for 25:

    e.g. for 26:

    e.g. for 

    factorial

    [page 145 in Section 2.3 of our textbook]

    used for counting numbers of permutations, combinations of items
    e.g. when analyzing sorting

    the "factorial" of an integer n, denoted ,
    is the product of all the integers from n down to 1
    e.g. 2! := 2*1 := 
    e.g. 5! := 5*4*3*2*1 := 

    as a special case, 0! is defined to be 
    why isn't 0! := 0?
    well, though it's true that when summing a number of values,
    if the number of values is zero then we get the result ,
    despite that situation with summing,
    when multiplying a number of values,
    if the number of values is zero then we get the result 
    e.g.:
        23 = 2*2*2 := 
        22 = 2*2 := 
        21 = 2 := 
        20 = _ := 
    as if there's always an implicit "*1" or something
    similarly:
        3! = 3*2*1 := 
        2! = 2*1 := 
        1! = 1 := 
        0! = _ := 
    if the choice were different,
    various properties would break down
    e.g. that 2x+1 = 2*2x :
        23 = 2*22
        22 = 2*21
        21 = 2*20
    and similarly (n+1)! = (n+1)*n! :
        3! = 3*2!
        2! = 2*1!
        1! = 1*0!

    floor, ceiling

    [pages 142-45 in Section 2.3 of our textbook]

    these are Mathematicians' notations for ~~rounding/truncation of floating-point number to integer — down or up

    |_x_| denotes the "floor" of x a.k.a. 
    which is the greatest integer less than or equal to x
    |¯x¯| denotes the "ceiling" of x a.k.a. 
    which is the least integer greater than or equal to x

    e.g.  |_1.414_|  := 
    e.g.  |_1.618_|  := 
    e.g.  |¯1.414¯|  := 
    e.g.  |¯1.618¯|  := 

    e.g. usage: floor "|_x_|" used often in programs using "(int)" to convert  double  to  int

    e.g. application for ceiling "|¯x¯|":
    * pigeonhole principle (presented later)
    * when searching a list using binary search, how many times may compare against the target
        |¯lg(list.length)¯|

    e.g. what may be the number of days between two times given in milliseconds

    well, the number of ms per day is
        1000ms/s * 60s/m * 60m/h * 24h/d := ms/d
    then 
    but if the time-difference in days is ,
    do you say the number of days between the two times is 57 or 58??
    using floor,  |_57.87..._|  := 
    using ceiling,  |¯57.87...¯|  := 

    quotient, remainder/modulus

    [pages 200ff. in Section 3.4 of our textbook]

    basic features of division for integers:

    "division algorithm":
    the issue is, how can you do division
    if you don't already know it
    but have simpler operations such as + and - ?
    divide integer a by positive divisor d by
    repeatedly subtracting d from a (using r for intermediate values)
        while using  q  to count number of subtractions done
    until left with remainder r less than d.
    the results are the final values of:
    * q, which is the quotient — the number of times d can be taken away from a; and
    * r, the remainder
    Note:
    I won't be requiring you to write code like this in this course (MTH 225).
    But I do expect you to be able to understand it,
    considering this course's ~~prerequisite CS 162.
        int[] division_algorithm(int n, int d) {
            if ( d <= 0 )
                throw new &IllegalArgumentException("divisor should be positive");
            int q = 0, r = n;
            // typically, reduce high value, until remainder is less than |d|:
            while ( d <= r ) {
                r -= d;
                q++;
                }
            // alternatively, if value was negative, increase until not negative:
            while ( r < 0 ) {
                r += d;
                q--;
                }
            int[] results = {q, r};
            return  results;
            }
    
    (our textbook presents somewhat different version page 225)

    like CS 251 MIPS computer instruction  div
    yields quotient in one register, remainder in another

    e.g. (3rd grade?) divide 126 by 23:


    note quotient q := |_n/d_|
    e.g. 48/5 :=  ; |_48/5_|  :=  |__|  :=  
    (when doing division of two declared integers,
    Java and other programming languages automatically yield integer truncation like that
    instead of real n/d
    but handling negative values maybe more as you would expect)

    more abstractly (i.e. instead of referring to that primitive algorithm),
    if n is any integer and d is a positive integer
    then dividing n by d yields unique integers q and r
        with r one of the values {0,1,2,..,(d-2),(d-1)}
    such that  n  ==  d*q + r
    we call q the "quotient" and r the "remainder"
    e.g. dividing n=48 by d=5 yields q :=  and r := 
    satisfying  (n=48)  ==  (d=5)*(q=) + (r=)
        and  (r=) being one of the values {0,1,2,...,(d-2=),(d-1=)}
    and there are no other possibilities for q and r satisfying the conditions
    notably that r must be one of the values {0,1,2,..,(d-2),(d-1)}
    don't say dividing n=48 by d=5 yields q := 10 and r := -2

    e.g. dividing n=52 by d=3 yields q :=  and r := 
    e.g. dividing n=64 by d=8 yields q :=  and r := 

    e.g. dividing n = -25 by d = 7 yields q :=  and r := 
    the values for r being considered here are only {0,1,2,...,(d-2=),(d-1=)}
        not considering possible negative values for r
    which forces q :=  and r := 
    satisfying  (n=-25)  ==  (d=7)*(q=) + (r=)
    again note quotient q := |_n/d_|
    e.g. (-25)/7 :=  ; |_(-25)/7_|  :=  |__|  :=  

    e.g. dividing n = -1 by d = 7 yields q :=  and r := 
    again, the values for r being considered here are only {0,1,2,...,(d-2=),(d-1=)}
        not considering possible negative values for r
    which forces q :=  and r := 
    satisfying  (n=-1)  ==  (d=7)*(q=) + (r=)
    again note quotient q := |_n/d_|
    e.g. (-1)/7 :=  ; |_(-1)/7_|  :=  |__|  :=  



    actually we'll focus more on remainder (r) than quotient
    further special notation for it is as follows:
    "n  d" returns the remainder r obtained as above
    here "mod" may be read 
    and d called the "modulus"
        so might use identifier  instead of "d"
    this operation called "modulus operation"
    e.g.  27 mod 5 := 
    e.g.  21 mod 7 := 
    (in Java as well as in C++ and C, can use  for "mod"
    but again whereas here consider only nonnegative remainders,
    in a program  %  can yield negative value if operands are negative
    (probably because such a scheme easier to implement))

    what is the range of values possible for x mod m?
    e.g.  21 mod 7 := ,
            39 mod 7 := ,
            6 mod 7 := ,
            -25 mod 7 := ,
            -2 mod 7 := ,
            71 mod 7 := ,
                                .
                                .
                                .
    the range of values possible for x mod 7 is {}
    generally, the range of values possible for x mod m is {}



    application of mod:
    consider game such as  Minesweeper
    program places mines at apparently randomly chosen locations
        (5,2), (6,3), (0,7), ...
    how obtain those random values 5, 2, 6, 3, 0, 7, ...?
    "linear congruential method" provides such pseudo-random integers x0, x1, x2, x3, ... as follows
    start with seed s say (last few digits of) current time
    e.g. 
    have modulus say 1999,
    multiplier μ say 101,
    increment c say 997
    start x0 := s
    then generate succesive apparently random integers as follows:
        xnext :=  (μ*xprev + c) mod m
    e.g. x1 :=  (101* + 997) mod 1999
                    :=  ( + 997) mod 1999
                    :=   mod 1999
                    :=  
    e.g. x2 :=  (101* + 997) mod 1999
                    :=  ( + 997) mod 1999
                    :=   mod 1999
                    :=  
    e.g. x3 :=  (101* + 997) mod 1999
                    :=  ( + 997) mod 1999
                    :=   mod 1999
                    :=  
    e.g. x4 :=  (101* + 997) mod 1999
                    :=  ( + 997) mod 1999
                    :=   mod 1999
                    :=  
    e.g. x5 :=  (101* + 997) mod 1999
                    :=  ( + 997) mod 1999
                    :=   mod 1999
                    :=  
    thus x0 := , x1 := , x2 := , x3 := , x4 := , x5 := , ...
    system maintains sequence of x-values like that behind the scenes
    so if you request a random number between 0 and a small value k,
        e.g. if you use Java Random.nextInt(k)
    then system gives you  xcurrent mod k  i.e.  x_current % k
    e.g. if k is 10
    mod 10  yields  
    mod 10  yields  
    mod 10  yields  
    mod 10  yields  
    mod 10  yields  
                                            ·
                                            ·
                                            ·
    will appear random
    except sequence will repeat
        ... xm-2, xm-1, xm=x0, xm+1=x1, xm+2=x2, ...
    so called "pseudo"-random
    computer-systems' pseudorandom number generators use very large m to stave this off


    (Copyright © 2009 by Hugh McGuire   — for thoughts about this, see   http://www.cis.gvsu.edu/~mcguire/teaching/copyright_thoughts.html .)