LMU ☀️ CMSI 2130
ALGORITHMS
HOMEWORK #2 PARTIAL ANSWERS
  1. Here’s the Java version first:

    Bozosorter.java
    import java.util.Random;
    import java.util.Arrays;
    
    /**
     * A little utility class with a bozosort method for integer arrays. A main
     * method is also thrown in that prints a little table showing the average
     * number of swaps required to sort arrays of various sizes.
     */
    public class Bozosorter {
    
        private static boolean isSorted(int[] a) {
            for (int i = 1; i < a.length; i++) {
                if (a[i - 1] > a[i]) {
                    return false;
                }
            }
            return true;
        }
    
        /**
         * Bozosorts a given array using the given random number generator, returning
         * the number of swaps required to sort.
         */
        public static int sort(int[] a, Random random) {
            int swaps = 0;
            while (!isSorted(a)) {
                int i = random.nextInt(a.length);
                int j = random.nextInt(a.length);
                if (i != j) {
                    int x = a[i];
                    int y = a[j];
                    a[i] = y;
                    a[j] = x;
                    swaps++;
                }
            }
            return swaps;
        }
    
        public static void main(String[] args) {
            Random random = new Random();
    
            int iterations = 50;
            System.out.println("Size    Avg # of swaps");
            for (int size = 1; size <= 10; size++) {
                int swaps = 0;
                for (int k = 0; k < iterations; k++) {
                    swaps += sort(random.ints(size).toArray(), random);
                }
                System.out.printf("%2d%20.2f\n", size, swaps / (double) iterations);
            }
        }
    }
    

    Here is one particular output (your results will differ):

    $ java edu.lmu.cs.sorting.Bozosorter
    Size    Avg # of swaps
     1                0.00
     2                0.46
     3                3.94
     4               22.48
     5              114.64
     6              804.12
     7             6239.96
     8            49741.50
     9           404976.52
    10          3959040.06
    

    I also wrote this in Python. But it gets realy slow once the size gets to be 9, unless you use PyPy.

    bozosort-experiments.py
    from random import randint, shuffle
    
    def bozosort(a):
        n = len(a) - 1
        swaps = 0
        while not all(a[i] <= a[i+1] for i in range(n)):
            i, j = randint(0, n), randint(0, n)
            a[i], a[j] = a[j], a[i]
            swaps += 1
        return swaps
    
    def main():
        NUM_TRIALS, NUM_ITERATIONS = 10, 20
    
        print('Size    Avg # of swaps')
        for size in range(1, NUM_TRIALS):
            swaps = 0
            for _ in range(1, NUM_ITERATIONS):
                swaps += bozosort([randint(0,1000) for i in range(size)])
            print(f'{size:2d}{swaps/NUM_ITERATIONS:20.2f}')
    
    main()
    
  2. Since the problem asked you to treat characters as code points, it’s reasonable to infer we’re moving into the realm of encrypting real data, and not just the boring set of characters A-Z. Code points are integers, so we implement both our messages and keys as arrays of integers, not strings or arrays of characters. Here is my Java solution:

    AutoKeyVigenere.java
    /**
     * An auto-key Vigenere cipher. During encryption, the input text is used for
     * the key stream after the initial key is used up. During decryption, we get
     * the key characters from the plaintext we are recovering.
     */
    public class AutoKeyVigenere {
    
        public static int[] encipher(int[] message, int[] key) {
            return encipher(message, key, 1);
        }
    
        public static int[] decipher(int[] cipher, int[] key) {
            return encipher(cipher, key, -1);
        }
    
        private static int[] encipher(int[] input, int[] key, int multiplier) {
            if (key.length == 0) {
                throw new IllegalArgumentException("Key cannot be empty");
            }
            int[] output = new int[input.length];
            for (int i = 0, n = input.length; i < n; i++) {
                int block = i < key.length ? key[i] : multiplier == 1 ? input[i - key.length] : output[i - key.length];
                output[i] = block * multiplier + input[i];
            }
            return output;
        }
    }
    

    And a unit test for it:

    AutoKeyVigenereTest.java
    import java.util.Arrays;
    import org.junit.Assert;
    import org.junit.Test;
    
    public class AutoKeyVigenereTest {
    
        @Test
        public void testRoundTrip() {
            int[][] messages = { { 34634634, 423423, 23423525, 4574574, 47467, 0, 45, 423523677, 97938746 },
                    { 4, 3, 7, 6, 5, 8, 9, 66, 54, 32, 1132, 87, 898, 87, 787, 878789, 9, 89, 8, 89, 89, 1 }, { 7 }, {} };
            int[][] keys = { { 546363434, 3545, 204311244 }, { 0xffffffff, 0xffffff33 } };
            for (int[] message : messages) {
                for (int[] key : keys) {
                    int[] cipher = AutoKeyVigenere.encipher(message, key);
                    int[] recovered = AutoKeyVigenere.decipher(cipher, key);
                    Assert.assertTrue(Arrays.equals(message, recovered));
                }
            }
        }
    
        @Test(expected = IllegalArgumentException.class)
        public void testEmptyKey() {
            AutoKeyVigenere.encipher(new int[] { 3453, 4234235, 123 }, new int[] {});
        }
    }
    

    Now if you are not yet using Eclipse or NetBeans or IntelliJ or other IDE yet (C’mon people it’s time), then you can run this on the command line like this (assuming you downloaded JUnit):

    $ javac -cp ~/lib/junit-4.10.jar:. crypto/AutoKeyVigenere.java crypto/AutoKeyVigenereTest.java
    $ java -cp ~/lib/junit-4.10.jar:. org.junit.runner.JUnitCore edu.lmu.cs.crypto.AutoKeyVigenereTest
    JUnit version 4.10
    ..
    Time: 0.003
    
    OK (2 tests)
    

    The Python version will appear shortly.

  3. THE PROGRESS OF OUR ARMS, UPON WHICH ALL ELSE CHIEFLY DEPENDS, IS AS WELL KNOWN TO THE PUBLIC AS TO MYSELF, AND IT IS, I TRUST, REASONABLY SATISFACTORY AND ENCOURAGING TO ALL. WITH HIGH HOPE FOR THE FUTURE, NO PREDICTION IN REGARD TO IT IS VENTURED.
  4. COMPUTER SCIENCE IS NO MORE ABOUT COMPUTERS THAN ASTRONOMY IS ABOUT TELESCOPESS
  5. Here is a Julia script that solves this problem:

    rsaexample.jl
    p = BigInt(23847623789462398745236743254827634647)
    q = BigInt(80147623789462398745236743254827634711)
    N = p * q
    t = (p-1)*(q-1)
    e = 7 # Techincally we should iterate to find this....
    @assert(gcd(e, t) == 1)
    d = invmod(e,t)
    println("Public key is ($N, $e)")
    println("Private key is ($N, $d)")
    

    The output is

    Public key is (1911330379750465988511865475607817924950038631764482538080744390093883432017,
    7)
    Private key is (1911330379750465988511865475607817924950038631764482538080744390093883432017,
    546094394214418853860532993030805121384583824053016497311505972452636617903)
    
  6. Given $N=729880581317$ and $e=5$. First factor $N=pq$ to get $p=822893$ and $q=886969$. So $(p-1)(q-1) = 729878871456$. Therefore $d = \mathrm{invmod}(5,729878871456) = 583903097165$. The private key is $(N,d) = (729880581317,583903097165)$
  7. (1.45)
    1. Digital signatures can be used for authentication, provided the keys stay secret.
    2. You are given $s = m^d \bmod N$, $(N,e)$, and $m$. Verification is simply a matter of checking whether $s^e \bmod N = m$. Or more intuitively, you got a signature made by applying someone’s private key to a message, so undo that by applying the public key.
    3. Cool, RSA by hand! We make make it super simple. I’ll sign my name "ray" with encoding {a:1, r:2, y:3} and RSA with $p=5$, $q=11$, $e=3$. So $N=pq=55$ and $d=\mathrm{invmod}(3,40)=27$. Now I’ll sign $(2,1,3)$ as follows: $(2^{27}\bmod 55, 1^{27}\bmod 55, 1^{27}\bmod 55) = (18,1,42)$. Now, let’s check: $(18^{3}\bmod 55, 1^{3}\bmod 55, 42^{3}\bmod 55) = (2,1,3)$. All good.
    4. There was a typo in this problem. The public key should have been (391,17). Anyway, getting $d$ is a quick Julia computation away:
      julia> factor(391)
      Dict{Int64,Int64} with 2 entries:
        23 => 1
        17 => 1
      
      julia> invmod(17, 22*16)
      145
      
      So the answer is 145.
  8. (1.46)
    1. Eve can just ask Bob to sign the messages that Alice encrypted with Bob’s public key. Bob’s signing of the message is exactly applying his secret key to it, which decrypts the message for Eve. Stupid Bob.
    2. Eve knows $(N,e)$. She intercepts $m^e \bmod N$ from Alice, then does this:
      • Picks a value $x$ coprime to $N$.
      • Computes $x^e \bmod N$.
      • Multiplies that with the ciphertext: $(x^e \bmod N)(m^e \bmod N) = (xm)^e \bmod N$.
      • Gets Bob to sign that: $((xm)^e \bmod N)^d \bmod N = (xm)^{ed} \bmod N = (xm) \bmod N$.
      • Computes $y = \mathrm{invmod}(x,N)$
      • Multiplies that with the result of the previous step: $(y \bmod N)((xm) \bmod N) = (yxm) \bmod N = m \bmod N = m$.
      Stupid Bob.
  9. (2.12) Both the Master Theorem ($T(n) = 2T(\frac{n}{2}) + 1$) and inspection will tell you this thing is linear. Where $n$ is a power of two, it prints that line exactly $n-1$ times.
  10. (2.23) Heh, we did this on the first day of class, remember? Here are the solutions we covered on that day:

    majority.py
    def majority_naive(a):
        for x in a:
            if a.count(x) > len(a) // 2:
                return x
        return None
    
    def majority_linearithmic(a):
        if len(a) == 0:
            return None
        if len(a) == 1:
            return a[0]
        half = len(a) // 2
        left = majority_linearithmic(a[0:half])
        right = majority_linearithmic(a[half:])
        if left == right:
            return left
        if a.count(left) > half:
            return left
        if a.count(right) > half:
            return right
        return None
    
    from collections import Counter
    def majority_with_dictionary(a):
        counts = Counter()
        for x in a:
            counts[x] += 1
        for value, count in counts.items():
            if count > len(a) // 2:
                return value
        return None
    
    def majority_linear(a):
        count = 0
        for x in a:
            if count == 0:
                candidate = x;
            if x == candidate:
                count += 1
            else:
                count -= 1
        if a and a.count(candidate) > len(a) // 2:
            return candidate
        return None
    
    tests = [
        ([], None),
        ([1], 1),
        ([4,3], None),
        ([4,4], 4),
        ([4,4,1], 4),
        ([4,0,4], 4),
        ([4,1,8], None),
        ([3,3,3,3,3,0,0,0,0,0], None),
        ([3,3,3,3,3,0,0,0,3,0], 3),
        ([1,1,1,3,3,2,2,3,3,3,2,3,3], 3),
        ([1,1,1,1,3,2,2,3,3,3,2,3,3], None),
    ]
    
    # Tests
    for fun in (majority_naive, majority_linearithmic, majority_with_dictionary, majority_linear):
        for a, expected in tests:
            if fun(a) != expected:
                print(f'Ooops, {fun.__name__}({a}) != {expected}')