Concrete Abstractions: Chapter 5

Exercise 5.6 Write a general purpose procedure, that when given two integers, low and high, and a procedure for computing a function f , will compute f(text{low}) + f(text{low} + 1) + f (text{low} + 2) + ... + f (text{high}). Show how it can be used to sum the squares of the integers from 5 to 10 and to sum the square roots of the integers from 10 to 100.

Solution: I wrote a iterative version of it because you have to directly apply the function to each number, i.e. it isn’t commutative.

(define add-integers 
  (lambda (f low high)
    (add-integers-iter f low high 0)))

(define add-integers-iter
  (lambda (f low high end)
    (if (= low high)
        (+ end (f low))
        (add-integers-iter f (+ low 1) high (+ end (f low))))))

; sum of squares from 5 to 10
(add-integers (lambda (x) (* x x)) 5 10)

; sum of square roots from 10 to 100
(add-integers (lambda (x) (sqrt x)) 10 100)

Exercise 5.11 Write a procedure make-verifier, which takes f and m as its two arguments and returns a procedure capable of checking a number. The argument f is itself a procedure, of course. Here is a particularly simple example of a verifier being made and used:

(define check-isbn (make-verifier * 11)) (check-isbn 0262010771)

The value #t is the “true” value; it indicates that the number is a valid ISBN.
As we just saw, for ISBN numbers the divisor is 11 and the function is simply f(i, d_i ) = i * di. Other kinds of numbers use slightly more complicated functions, but you will still be able to use make-verifier to make a verifier much more easily than if you had to start from scratch.

Solution:

(define make-verifier
  (lambda (f m)
    (lambda (n)
      (if (= (remainder 
              (map-f f n) 
              m) 0)
          #t
          #f))))


(define map-f
  (lambda (f n)
    (map-digit-f f n 0 1)))
     
(define map-digit-f
  (lambda (f n end i)
    (if (= n 0)
        end
        (map-digit-f f
                     (quotient n 10)
                     (+ end (f i (remainder n 10)))
                     (+ i 1)))))
    

Exercise 5.12 For UPC codes (the barcodes on grocery items), the divisor is 10, and the function f (i, d_i ) is equal to d_i itself when i is odd, but to 3d_i when i is even. Build a verifier for UPC codes using make-verifier, and test it on some of your groceries. (The UPC number consists of all the digits: the one to the left of the bars, the ones underneath the bars, and the one on the right.) Try making some mistakes, like switching or changing digits. Does your verifier catch them?

Solution: Yeah, the mistakes were caught. By the way you can see here how powerful high-order functions are.

(define UPC-f
  (lambda (i di)
    (if (odd? i)
        di
        (* 3 di))))

(define UPC-verifier
  (make-verifier UPC-f 10))

Exercise 5.13 Credit card numbers also use a divisor of 10 and also use a function that yields d_i itself when i is odd. However, when i is even, the function is a bit fancier: It is 2d_i if $latex d_i < 5$, and $latex 2d_i + 1$ if $latex d_i geq 5.$ Build a verifier for credit card numbers. In the dialog at the beginning of this section, did the order taker really mistype the number, or did the customer read it incorrectly? Solution: The credit card number was correct.

(define CC-f
  (lambda (i di)
    (if (odd? i)
        di
        (if (< di 5)
            (* 2 di)
            (+ (* 2 di) 1)))))

(define CC-verifier
  (make-verifier CC-f 10))

Exercise 5.13 The serial number on U.S. postal money orders is self-verifying with a divisor of 9 and a very simple function: f (i, d_i ) = di, with only one exception, namely, f (1, d_1 ) = -d_1. Build a verifier for these numbers, and find out which of these two money orders is mistyped: 48077469777 or 48077462766.
Actually, both of those money order numbers were mistyped. In one case the error was that a 0 was replaced by a 7, and in the other case two digits were reversed. Can you figure out which kind of error got caught and which didn’t? Does this help explain why the other kinds of numbers use fancier functions?

Solution: The reversal of digits, if the digit isn't the first one, isn't caught by this verification function. This is a reason for using fancier verification functions.

(define USPostal-f
  (lambda (i di)
    (if (= i 1)
        (- di)
        di)))

(define USPostal-verifier
  (make-verifier USPostal-f 9))

Exercise 5.17 Suppose you have a function and you want to find at what integer point in a given range it has the smallest value. For example, looking at the following graph of the function f (x) = x^2 - 2x, you can see that in the range from 0 to 4, this function has the smallest value at 1. We could write a procedure for answering questions like this; it could be used as follows for this example:

(integer-in-range-where-smallest (lambda (x) (- (* x x) (* 2 x)))
0 4)
1

Here is the procedure that does this; fill in the two blanks to complete it:

(define integer-in-range-where-smallest 
  (lambda (f a b)
    (if (= a b) 
        a
        (let ((smallest-place-after-a _________________________))
               (if __________________
                a 
                smallest-place-after-a)))))

Solution: I find it actually pretty hard to do fill in questions. Anyhow, here's the solution:

(define integer-in-range-where-smallest 
  (lambda (f a b)
    (if (= a b) 
        a
        (let ((smallest-place-after-a (integer-in-range-where-smallest f (+ a 1) b)))
               (if (< (f a) (f smallest-place-after-a))
                a 
                smallest-place-after-a)))))

Exercise 5.18 Consider the following definitions:

    (define make-scaled 
     (lambda (scale f)
      (lambda (x) 
       (* scale (f x)))))

    (define add-one 
     (lambda (x) 
      (+ 1 x)))
    (define mystery 
     (make-scaled 3 add-one))

For the following questions, be sure to indicate how you arrived at your answer:
a. What is the value of (mystery 4)?
b. What is the value of the procedural call ((make-scaled 2 (make-scaled 3 add-one)) 4)?

Solution: a. You can see that mystery gets a function from (make-scaled 3 add-one). This produces (lambda(x) (* 3 (add-one x))) which equals (lambda(x) (* 3 (+ 1 x))). Therefore the value is 15.
b. Let's take this call apart. (make-scaled 2 f) produces (lambda(x) (* 2 (f x)). Here f is (make-scaled 3 add-one) which produces the known (lambda(x) (* 3 (+ 1 x))) = 15 for x = 4. If you substitute this into (make-scaled 2 f) it becomes (lambda (x) (* 2 15)) = 30.

Exercise 5.20 Suppose the following have been defined:

    (define f 
     (lambda (m b)
      (lambda (x) (+ (* m x) b)))) 

    (define g (f 3 2))

For each of the following expressions, indicate whether an error would be signaled, the value would be a procedure, or the value would be a number. If an error is signaled, explain briefly the nature of the error. If the value is a procedure, specify how many arguments the procedure expects. If the value is a number, specify which number.
a. f
b. g
c. (* (f 3 2) 7)
d. (g 6)
e. (f 6)
f. ((f 4 7) 5)

Solution:
a. f is a procedure with two arguments.
b. g is a procedure returned by f with one argument.
c. (* (f 3 2) 7) evals to (* (lambda (x) (+ (* 3 x) 2) 7) = (* 23) which throws an error because * needs at least two arguments.
d. Here we got another value: (g 6) = ((f 3 2) 6) = (+ (* 3 6) 2) which equals 20.
e. f needs two arguments. So this throws an error.
f. (+ (* 4 5) 7) which equals 27.

Exercise 5.24 Consider the following procedure:

(define positive-integer-upto-where-smallest 
 (lambda (n f) ; return an integer i such that
  ; 1 <= i <= n and for all integers j
  ; in that same range, f(i) <= f(j) 
  (define loop
   (lambda (where-smallest-so-far next-to-try)
    (if (> next-to-try n)
     where-smallest-so-far
     (loop (if (< (f next-to-try)
                (f where-smallest-so-far))
            next-to-try
            where-smallest-so-far)
      (+ next-to-try 1)))))
  (loop 1 2)))

a. Write a mathematical formula involving n that tells how many times this procedure uses the procedure it is given as its second argument. Justify your answer.
b. Give a simple Theta order of growth for the quantity you determined in part a. Justify your answer.
c. Suppose you were to rewrite this procedure to make it more efficient. What (in terms of n) is the minimum number of times it can invoke f and still always determine the correct answer? Justify your answer. (You are not being asked to actually rewrite the procedure.)

Solution: a. If next > n then loop returns directly smallest-so-far, so there's no call of f. If next <= n then there are two calls in the if header. Therefore there are 2(n-1) function calls of f.
b. We can see that 2(n-1) = 2n - 2. 2n is the biggest term and 2 is just a factor, therefore \Theta(n).
c. We can save (f where-smallest-so-far) by adding it in the parameter list of loop. In the ideal case we just need to calculate (f where-smallest-so-far) one time and (f next-to-try) (n - 1) times. However, in the worst case there's no improvement.

SPOJ: 8060. Cookies Piles

The kids in my son’s kindergarten made Christmas cookies with their teacher, and piled them up in columns. They then arranged the columns so that the tops of the columns, going from shortest to tallest, were in a nice straight ramp. The cookies were all of uniform size. Given that there were A cookies in the shortest pile, that the difference in height between any two adjacent piles was D cookies, and that there were N piles, can you write a program to figure out how many cookies there were in total?

Problem: Sphere Online Judge (SPOJ) – Problem AMR10F

Solution: This is quite a nice and simple problem. The easiest solution is just to write a loop to calculate the amount of cookies. If you think one step further you can write a general formula for calculating this.
Let’s imagine three piles of cookies.

At the rightmost there are A cookies. The middle one got A + D cookies and the leftmost got A + 2D cookies. If we generalize this we get: \text{allCookies} = (A + 2 * D) + (A + 1 * D) + (A + 0 * D) which can be written as \sum_{i=0}^{N-1} (A + iD).
And thus can be simplified:

\sum_{i=0}^{N-1} A + D \sum_{i=0}^{N-1} i = NA + D\frac{(N-1)N}{2}.

T = int(raw_input())

for i in xrange(0, T):
    N, A, D = raw_input().split()
    
    N = int(N)
    A = int(A)
    D = int(D)

    print (N * A) + D * ( (N - 1) * N ) / 2

SPOJ: 1030. Triple Fat Ladies

Pattern Matchers have been designed for various sorts of patterns. Mr. HKP likes to observe patterns in numbers. After completing his extensive research on the squares of numbers, he has moved on to cubes. Now he wants to know all numbers whose cube ends in 888.

Given a number k, help Mr. HKP find the kth number (indexed from 1) whose cube ends in 888.

Problem: Sphere Online Judge (SPOJ) – Problem EIGHTS

Solution: Yeah, an other number problem! And again I just looked for patterns.

for i in xrange(0, 4444):
     if str(i**3)[-3:] == "888":
         print i, i**3

Which gets us:

192 7077888
442 86350888
692 331373888
942 835896888
1192 1693669888
1442 2998442888
1692 4843965888
1942 7323988888
2192 10532261888
2442 14562534888
2692 19508557888
2942 25464080888
3192 32522853888
3442 40778626888
3692 50325149888
3942 61256172888
4192 73665445888
4442 87646718888

EDIT: Thanks to the anonymous person who found a simple connection between the numbers but can’t properly communicate. You can find the k-th number with this simple formula: 192 + 250(k-1)

You should see that each number ends in 2. If you look closer you see that the last two digits are 42 if i is even, and 92 if i is odd. Let’s look at the other digits. We get:

1
4
6
9
11
14
16

If you do some basic arithmetics you see that:

1
4  =  1 + 3
6  =  4 + 2
9  =  6 + 3
11 =  9 + 2
14 = 11 + 3
16 = 14 + 2

You can calculate your first digits either recursively or search for a function. This takes probably some time, so I looked this sequence up. OEIS gives us the formular: a(n) = floor((5n-2)/2) for n > 2

 

from math import floor

def kthnumber(k):
    if k > 2:
        num = "%i" % int((floor( (5 * (k - 1) + 3) / 2.0 )))
    else:
        if k == 1:
            num = "1"
        elif k == 2:
            num = "4"

    if k % 2 == 0:
        num += "42"
    else:
        num += "92"

    return num



t = int(raw_input())
for c in xrange(0, t):
    print kthnumber(int(raw_input()))

Concrete Abstractions: Chapter 3

Let’s call the number of people in the circle n and number the positions from 1 to n. We’ll assume that the killing of every third person starts with killing the person in position number 3. […]
As we saw above, if the person we care about is in position 3, that person is the one killed and hence definitely not a survivor:

(define survives?
  (lambda (position n)
    (if (< n 3)
      #t
      (if (= position 3)
       #f
    ;we still need to write this part))))

Suppose we aren’t interested in the person in position number 3 but rather in some other person—let’s say J. Doe. The person in position number 3 got killed, so now we only have n – 1 people left. Of that smaller group of n – 1, there will still be two survivors, and we still want to know if J. Doe is one of them.

Exercise 3.9 How about the people who were in positions 1 and 2; what position numbers are they in after the renumbering?

Solution: In the example there were 8 persons at the start. After killing #3, the new #1 is #4, the new #2 is #5*, etc. #2 is #(n-1) and #1 is #(n-2).

*: clearly a reference to the Prisoner ;)

Exercise 3.10 Write a procedure for doing the renumbering. It should take two arguments: the old position number and the old number of people (n). (It can assume that the old position number won’t ever be 3, because that person is killed and hence doesn’t get renumbered.) It should return the new position number.

Solution:

(define renumber
  (lambda (position n)
   (if (< position 3)
     (+ position (- n 3))
     (- position 3))))

Exercise 3.11 Finish writing the survives? procedure, and carefully test it with a number of cases that are small enough for you to check by hand but that still cover an interesting range of situations.

Solution:

(define survives?
  (lambda (position n)
    (if (< n 3)
      #t
      (if (= position 3)
        #f
        (survives? (renumber position n) (- n 1))))))

I made a table for testing if the program works correctly. Each line represents a new round, i.e. somebody got killed. The killed person is labeled with X.

1 2 3 4 5 6 7 8
6 7 X 1 2 3 4 5
3 4   5 6 X 1 2
X 1   2 3   4 5
  3   4 X   1 2
  X   1     2 3
              X

Exercise 3.15 Consider the following two procedures:

(define f 
  (lambda (n)
    (if (= n 0)
      0
      (g (- n 1)))))

(define g 
  (lambda (n)
    (if (= n 0)
      1
      (f (- n 1)))))

a. Use the substitution model to evaluate each of (f 1), (f 2), and (f 3).
b. Can you predict (f 4)? (f 5)? In general, which arguments cause f to return 0 and which cause it to return 1? (You need only consider nonnegative integers.)
c. Is the process generated by f iterative or recursive? Explain.

Solution: a. I started to define each function mathematically. f(0) = 0, f(n) = g(n-1) and g(0) = 1, g(n) = f(n-1). Now we can see what’s happening.
f(1) = g(0) = 1
f(2) = g(1) = f(0) = 0
f(3) = g(2) = f(1) = g(0) = 1
b. You see that odd numbers lead to g(0) = 1 and even numbers lead to f(0) = 0.
c. I would say that f’s process iterative because you could stop at any time and just continue later with the saved parameters.

Exercise 3.16 Consider the following two procedures:

(define f (lambda (n)
  (if (= n 0)
  0
  (+ 1 (g (- n 1))))))

(define g (lambda (n)
  (if (= n 0)
  1
  (+ 1 (f (- n 1))))))

a. Use the substitution model to illustrate the evaluation of (f 2), (f 3), and (f 4).
b. Is the process generated by f iterative or recursive? Explain. c. Predict the values of (f 5) and (f 6).

Solution: a. Same trick at last time.
f(0) = 0, f(n) = 1 + g(n-1) and g(0) = 1, g(n) = 1 + f(n - 1)
Therefore: f(2) = 1 + g(1) = 1 + (1 + f(0)) = 1 + 1 + 0 = 2
f(3) = 1 + g(2) = 1 + (1 + f(1)) = 1 + (1 + (1 + g(0))) 1 + (1 + (1 + 1) = 3
f(4) = 1 + g(3) = 1 + (1 + f(2)) = 1 + (1 + (1 + g(1))) = 1 + (1 + (1 + (1 + f(0)))) = 3

b. The process is recursive because you can’t just stop and continue later. This happens because of the (+ 1 f) which have to be saved on the stack.
c. The values of (f 5) and (f 6) are both 6.

Exercise 3.18 We’ve already seen how to raise a number to an integer power, provided that the exponent isn’t negative. We could extend this to allow negative exponents as well by using the following definition:
 b^n = \begin{cases} 1 & \text{ if } x=0 \\ b^{n-1}*b & \text{ if } n>0 \\ b^{n+1}/b & \text{ if } n<0 \end{cases}
a. Using this idea, write a procedure power such that (power b n) raises b to the n power for any integer n.
b. Use the substitution model to show how (power 2 -3) would be evaluated. (You can leave out steps that just determine which branch of a cond or if should be taken.) Does your procedure generate a recursive process or an iterative one?

Solution a.

(define power
  (lambda (b n)
    (cond ((= n 0) 1)
          ((> n 0) (* b (power b (- n 1))))
          ((< n 0) (* (/ 1 b) (power b (+ n 1)))))))

b.

(power 2 -3)
(* (/ 1 2) (power 2 (+ -3 1)))
(* 0.5 (power 2 -2))
(* 0.5 0.5 (power 2 -1))
(* 0.25 0.5 (power 2 0))
(* 0.125 1)
0.125

It’s an recursive one because you can’t determine the final solution without knowing the beginning parameters.

Exercise 3.19 Prove that, for all nonnegative integers n and numbers a, the following procedure computes the value 2^n *a:

(define foo (lambda (n a)
  (if (= n 0)
    a
    (foo (- n 1) (+ a a)))))

Solution Let’s define the function again mathematically. f(0, a) = a, f(n, a) = f(n-1, 2a). The base case is correct:
f(0, a) = 2^0*a = a.
We assume that f(n+1, a) = 2^{n+1}*a = 2^n * 2a which can be written as 2^n * 2a = 2^n * b.
With our assumption it should follow that f(n+1, a) = f(n, b).
If we look at the definition, we see that f(n, a) = f(n-1, 2a) which implies f(n+1, a) = f(n, 2a) = f(n, b).