(2) n + 3 is in Ω (n). Choose c = 1 and n0 = 0. Then, we have n + 3 ≥ n which is for all n.
(3) n + 3 is in Θ(n) since we showed it was in O(n) and in Ω(n).
(2) n2 + n is in Ω(n2). Choose c = 1 and n0 = 0. Then we have (n2 + n) > n2 which is true for all n > 0.
(3) n2 + n is in Θ(n2) since we showed it was in O(n2) and in Ω(n2).
(2) 2n is not in Ω (3 n). Since 3n grows faster than 2n for any choice of c, we can always find a value m such that 2n < c * 3n for all n > m. Recall that 2n is 2 * 2 * 2 * ... (n times) and 3n is 3 * 3 * 3 ... (n times). So, we need to choose n such that c * 3 * 3 * 3 ... (n times) is greater than 2 * 2 * ... (n times). Each time we increase the value of n by one, the value of c3n / 2n is multiplied by 1.5. After some number of multiplications, it must be greater than 1.
(3) 2n is not in Θ (3 n) since in (2) we argued it is not in Ω (3 n).
(2) 2n is not in Ω (n n). Since nn grows faster than 2n for any choice of c, we can always find a value m such that 2n < cnn for all n > m. For example, we can choose n = 1/c + 1. Then, cnn = c1/c1/c + 1 = 1/c1/c. Since c must be less than 1/2 (otherwise n = 3 would work), we know 1/c > 2, and 1/c1/c ≥ 21/c.
(3) 2n is not in Θ (n n) since in (2) we argued it is not in Ω (3 n).
(define (xor a b) (if (eq? a b) 0 1))
The running time of baudot-to-string depends on the length of the list passed in as baudot-list. We use n to refer to the number of elements in the input list. The map application evaluates an application of baudot-to-char on every element of the list. Since baudot-to-char has constant running time, and is evaluated once for each element, the total running time for the map expression is in Θ(n). Then, baudot-to-string applies list->string to the result of the map. We don't have the code for list->string since it is built in to DrScheme, but it is reasonable to guess it is in Θ(l) where l is the number of elements in the input list, since it has to look at each element to convert it to a string. In this case, the length of the input list to list->string is the length of the output of map which is the length of the input baudot-list, n. This means the list->string application is in Θ(n) work. So, the total running time for baudot-to-string involves Θ(n) work for the map plus Θ(n) work for the list->string application. n + n = 2n, but within Θ the factor doesn't matter, so the total running time is in Θ(n).(define (string-to-baudot string) (map char-to-baudot (string->list string))) (define (baudot-to-string baudot-list) (list->string (map baudot-to-char baudot-list)))
a. Define a function rotate-wheel that takes one of the wheels as a parameter. It should return the wheel rotated once. For example, (rotate-wheel (list 1 0 0 1 0)) should evaluate to (0 0 1 0 1). Although all the wheels in our simulated Lorenz cipher machine have five bits, your rotate-wheel procedure should work for any length list.
b. Define a function rotate-wheel-by that takes a wheel as the first parameter and a number as the second. The function should return the wheel list rotated by the number passed in. For example, (rotate-wheel-by (list 1 0 0 1 0) 2) should evaluate to (0 1 0 1 0) and (rotate-wheel-by wheel 5) should evaluate to wheel.
(define (rotate-wheel wheel)
(append (cdr wheel) (list (car wheel))))
(define (rotate-wheel-by wheel n)
(if (= n 0) wheel
(rotate-wheel-by (rotate-wheel wheel) (- n 1))))
Another approach is to remember the n-times procedure we used in PS3:
(define (rotate-wheel-by wheel n) ((n-times rotate-wheel n) wheel))
b. Define a function rotate-wheel-list-by that takes a list of wheels and a number as parameters, and evaluates to a list where each of the wheels in the parameter list of wheels has rotated the number parameter times. For example, (rotate-wheel-list-by K-wheels 5) should evaluate to the same list as K-wheels.
c. Describe the running time of your rotate-wheel-list-by procedure using Θ notation. Be sure to explain what all variables you use mean.
Another approach:(define (rotate-wheel-list wheellist) (map rotate-wheel wheellist)) (define (rotate-wheel-list-by wheellist n) ((n-times rotate-wheel-list n) wheellist))
We'll analyze the second definition (although the result would be the same for the first definition). The running time of rotate-wheel-list-by depends on both inputs — the wheels passed in and the number of rotates, n. To avoid confusion we will use w to represent the number of wheels, p to represent the number of positions in each wheel (fixed as 5 in the problem set, but the procedure we defined would work for any length), and r as the number of rotates to do (the value passed in as n.(define (rotate-wheel-list-by wheels n) (map (lambda (wheel) (rotate-wheel-by wheel n) wheels))
Applying the procedure rotate-wheel-list-by evalutes map on the list of wheels. This means the procedure passed as the first parameter to map is evaluated w times.
That map procedure is (lambda (wheel) (rotate-wheel-by wheel n)) so we are evaluating (rotate-wheel-by wheel n) w times. The body of rotate-wheel-by is ((n-times rotate-wheel n) wheel)), so we are evaluating rotate-wheel r times (note that n corresponds to the r variable we introduced) for each wheel. The amount of work done by rotate-wheel depends on the length of the wheel. We defined it as (append (cdr wheel) (list (car wheel))), and append has running time in Θ(l) where l is the number of elements in the first list passed to append (probably, we don't know how DrScheme actually defines it, but do know it has to look at every element in the first list). In this case l is the number of positions in each wheel, p.
So, we are doing Θ(p * r) work for each wheel, and there are w wheels, so the total running time is in Θ(prw).
If you assumed the number of positions and number of wheels is fixed, as it is in the problem set problem, then the only input that varies is the value of r. In this case p and w would be constant, so the total running time is in Θ(r).
(define (wheel-encrypt baudot-letter wheel-list) (map xor baudot-letter (map car wheel-list)))
b. Define a function lorenz that takes four parameters: a string and three integers. The integers represent the starting positions of the three wheels, respectively. The function should call do-lorenz with the string converted to Baudot and the wheels rotated to the correct starting positions. The function should return the ciphertext in the form of a string.
(define (do-lorenz code k-wheels s-wheels m-wheel)
(if (null? code) code
(cons
(wheel-encrypt (wheel-encrypt (car code) k-wheels) s-wheels)
(do-lorenz
(cdr code)
(rotate-wheel-list k-wheels)
(if (= (car m-wheel) 1) (rotate-wheel-list s-wheels) s-wheels)
(rotate-wheel m-wheel)))))
(define (lorenz string n1 n2 n3)
(if (null? string) string
(baudot-to-string
(do-lorenz (string-to-baudot string)
(rotate-wheel-list-by k-wheels n1)
(rotate-wheel-list-by s-wheels n2)
(rotate-wheel-by m-wheels n3)))))
(define (solve-lorenz string position1 position2 position3)
(if (<= position1 5)
(if (<= position2 5)
(if (<= position3 5)
(begin ; need begin to do evaluate both expressions
(printf (lorenz string position1 position2 position3))
(solve-lorenz string position1 position2 (+ 1 position 3)))
(solve-lorenz string position1 (+ 1 position2) 1))
(solve-lorenz string (+ 1 position1) 1 1))))
Here's another way:
(define (solve-lorenz ctext)
(map
(lambda (k-step)
(map
(lambda (s-step)
(map
(lambda (m-step)
(printf (lorenz ctext k-step s-step m-step)))
(intsto 5)))
(intsto 5)))
(intsto 5)))
The encrypted message: AT THE TIME, I HAD NO THOUGHT OR KNOWLEDGE OF
COMPUTERS IN THE MODERN SENSE, AND HAD NEVER HEARD THE TERM USED EXCEPT
TO DESCRIBE SOMEBODY WHO DID CALCULATIONS. - THOMAS FLOWERS.
An interesting book about human computers is, When Computers Were Human by David Alan Grier. The human computers of the day followed algorithms similar to those followed by (non-human) computers, just doing the steps by hand and mind.
Question 9:
a. The actual Lorenz cipher operated similarly to the one in this problem set except instead of only having 5 positions in each wheel, each wheel had many more positions (up to 61). Suppose n is the number of possible positions for each Lorenz cipher wheel, and the procedure used to cryptanalyze the cipher is the same as your code (except extended as necessary to handle the extra wheel positions). Describe how the amount of work needed to break a Lorenz-encrypted message grows as a function of the number of wheel positions, n, using Θ notation. Assume the message length and number of wheels are fixed.
(define (solve-lorenz p ctext)
(map
(lambda (k-step)
(map
(lambda (s-step)
(map
(lambda (m-step)
(printf (lorenz ctext k-step s-step m-step)))
(intsto p)))
(intsto p)))
(intsto p)))
The length of the list passed to the outer map is n since
(intsto p) evaluates to a list of length p which we
have define as n, the number of wheel positions. For each,
element the inner map produces a length of length n, so the
length of the list to the innermost map is n2. The
innermost map evaluates lorenz n times for each
element of that list, so there are n * n2
total evaluations of lorenz which is in
Θ(n3).
The time each lorenz evaluation takes is not constant, though, since it scales with the length of the message (fixed here), and the number of rotations to do (not fixed here - it depends on the number of wheel positions). The average number of rotations will scale as Θ(n) so, the actual work done will scale as Θ(n4) in the number of wheel positions. (This was a tough question — is your assumed lorenz was constant time and got Θ(n3) that is a very good (but not quite correct) answer to this question.)
b. Suppose instead that it was possible to add S and K wheels to the Lorenz cipher, and w represents the number of S and K wheels (for example, for the cipher machine in the problem set, w is 5). Describe how the amount of work needed to break a Lorenz-encrypted message grows as a function of w, using Θ notation. Assume the message length and number of wheel positions are fixed.
Note that this assumes all the wheels in a group must start at the same position (this is not the case for the actual machine), and all the wheel pins are known to the code breakers (also not the case in reality most times). Without those assumptions, the analysis becomes much more complicated, and the running time increases as Θ(pw) where p is the number of possible starting positions for each wheel.
c. If the Nazis learned of Bletchley Park's success in breaking the Lorenz cipher during the war, what changes that could be done without building and redistributing completly new cipher machines, would be most likely to prevent further successful cryptanalysis?