CMSI 282
Homework #2
Partial Answers
  1. Here's a Bozosort analyzer
    package edu.lmu.cs.sorting;
    
    import java.util.Random;
    
    /**
     * 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 {
    
        /**
         * Returns whether the given array is sorted.
         */
        private static boolean isSorted(int[] a) {
            for (int i = 1; i < a.length; i++) {
                if (a[i-1] > a[i]) {
                    return false;
                }
            }
            return true;
        }
    
        /**
         * Does a bozosort, 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;
        }
    
        /**
         * Returns a randomly filled in integer array for a given size.
         */
        private static int[] randomArray(int size, Random random) {
            int[] a = new int[size];
            for (int i = 0; i < size; i++) {
                a[i] = random.nextInt();
            }
            return a;
        }
    
        public static void main(String[] args) {
            Random random = new Random();
    
            // Try 50 sorts at each size, recording average # of swaps
            int iterations = 50;
            System.out.println("Size    Avg # of swaps");
            System.out.println("----------------------");
            for (int trialSize = 1; trialSize < 15; trialSize++) {
                int totalSwaps = 0;
                for (int k = 0; k < iterations; k++) {
                    totalSwaps += sort(randomArray(trialSize, random), random);
                }
                System.out.printf("%2d%20.2f\n", trialSize,
                        totalSwaps / (double)iterations);
            }
        }
    }
    

    Here is one particular output:

    Size    Avg # of swaps
    ----------------------
     1                0.00
     2                0.48
     3                4.20
     4               21.90
     5              126.86
     6              828.60
     7             5532.56
     8            49353.32
     9           473626.84
    10          3389858.32
    

    Another run gave these averages

    Size    Avg # of swaps
    ----------------------
     1                0.00
     2                0.58
     3                5.06
     4               24.10
     5              107.74
     6              818.54
     7             5133.62
     8            39475.00
     9           292153.18
    10          4204461.32
    
  2. My PriorityQueue class
    package edu.lmu.cs.collections;
    
    import java.util.ArrayList;
    import java.util.NoSuchElementException;
    
    /**
     * A heap-based priority queue implementation.
     */
    public class PriorityQueue<E extends Comparable<E>> {
        private ArrayList<E> heap = new ArrayList<E>();
    
        /**
         * Adds an item to the queue.
         */
        public void add(E item) {
            heap.add(item);
            siftUp(size() - 1);
        }
    
        /**
         * Remove the highest priority item from the queue.
         *
         * @throws NoSuchElementException if the queue is empty
         */
        public E remove() {
            if (isEmpty()) {
                throw new NoSuchElementException();
            }
    
            E item = heap.get(0);
            if (size() == 1) {
                heap.remove(0);
            } else {
                heap.set(0, heap.remove(size() - 1));
                siftDown(0);
            }
    
            return item;
        }
    
        /**
         * Returns the highest priority element without removing it.
         *
         * @throws NoSuchElementException if the queue is empty
         */
        public E peek() {
            if (isEmpty()) {
                throw new NoSuchElementException();
            }
            return heap.get(0);
        }
    
        /**
         * Returns the number of items in the queue.
         */
        public int size() {
            return heap.size();
        }
    
        /**
         * Returns whether the queue is empty.
         */
        public boolean isEmpty() {
            return heap.isEmpty();
        }
    
        // Sifts the item at position j up as far as it can go.
        private void siftUp(int j) {
            int i = (j - 1) / 2;
            if (i < 0) return;
            E parent = heap.get(i);
            E child = heap.get(j);
            if (parent.compareTo(child) <= 0) return;
            heap.set(i, child);
            heap.set(j, parent);
            siftUp(i);
        }
    
        // Sifts the item at position i down as far as it can go.
        private void siftDown(int i) {
            int j = i * 2 + 1;
            if (j >= size()) return;
    
            E parent = heap.get(i);
            E child = heap.get(j);
    
            if (j + 1 < size()) {
                E rightChild = heap.get(j + 1);
                if (rightChild.compareTo(child) < 0) {
                    child = rightChild;
                    j++;
                }
            }
    
            if (parent.compareTo(child) <= 0) return;
            heap.set(i, child);
            heap.set(j, parent);
            siftDown(j);
        }
    }
    

    And a test

    package edu.lmu.cs.collections;
    
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertFalse;
    import static org.junit.Assert.assertTrue;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.NoSuchElementException;
    
    import org.junit.Test;
    
    /**
     * PriorityQueue unit tests.
     */
    public class PriorityQueueTest {
    
        @Test
        public void testConstructor() {
            assertTrue(new PriorityQueue<Integer>().isEmpty());
            assertEquals(new PriorityQueue<String>().size(), 0);
        }
    
        @Test
        public void testSizeOne() {
            PriorityQueue<Integer> q = new PriorityQueue<Integer>();
            assertTrue(q.isEmpty());
            q.add(1000);
            assertTrue(q.size() == 1);
            assertTrue(!q.isEmpty());
            assertTrue(q.peek() == 1000);
            q.remove();
            assertTrue(q.isEmpty());
        }
    
        @Test
        public void testBiggerQueues() {
            PriorityQueue<Integer> q = new PriorityQueue<Integer>();
    
            // Add 0..99, scrambled
            List<Integer> a = new ArrayList<Integer>();
            for (int i = 0; i < 100; i++) a.add(i);
            Collections.shuffle(a);
            for (int x: a) q.add(x);
            assertFalse(q.isEmpty());
            assertEquals(q.size(), 100);
            assertEquals((int)q.peek(), 0);
    
            // Insert 100
            q.add(100);
            assertFalse(q.peek() == 100);
            assertEquals(q.size(), 101);
    
            // Insert and remove a highest priority element
            q.add(-1);
            assertEquals((int)q.peek(), -1);
            assertEquals((int)q.remove(), -1);
    
            // Remove 0..99
            for (int i = 0; i < 100; i++) {
                assertEquals((int)q.remove(), i);
            }
    
            // Remove 100
            assertFalse(q.isEmpty());
            assertTrue(q.remove() == 100);
            assertTrue(q.isEmpty());
            assertTrue(q.size() == 0);
        }
    
        /*
         * Cannot peek into an empty queue.
         */
        @Test(expected=NoSuchElementException.class)
        public void testPeekException() {
            new PriorityQueue<String>().peek();
        }
    
        /*
         * Cannot remove from an empty queue.
         */
        @Test(expected=NoSuchElementException.class)
        public void testRemoveException() {
            new PriorityQueue<String>().remove();
        }
    }
    
  3. The primality tester:

    And a unit test for it:

  4. The Autokey Vigenere cipher:
    package edu.lmu.cs.crypto;
    
    /**
     * 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 {
    
        /**
         * Encrypt the given plaintext with the given key.
         */
        public static String encipher(String plaintext, String key) {
            return encipher(plaintext, key, 1);
        }
    
        /**
         * Decrypt the given ciphertext with the given key.
         */
        public static String decipher(String ciphertext, String key) {
            return encipher(ciphertext, key, -1);
        }
    
        private static String encipher(String text, String key, int multiplier) {
            if ("".equals(key)) {
                throw new IllegalArgumentException("Key cannot be empty");
            }
            StringBuilder builder = new StringBuilder();
            int keyLength = key.length();
            for (int i = 0, n = text.length(); i < n; i++) {
                char k = i < keyLength ? key.charAt(i) :
                    multiplier == 1 ? text.charAt(i - keyLength) :
                    builder.charAt(i - keyLength);
                builder.append((char)(k * multiplier + text.charAt(i)));
            }
            return builder.toString();
        }
    }
    

    And a unit test for it:

    package edu.lmu.cs.crypto;
    
    import org.junit.Assert;
    import org.junit.Test;
    
    public class AutoKeyVigenereTest {
    
        @Test
        public void testRoundTrip() {
            String[] messages = {
                "Attack @T DaWN!",
                "",
                "fjh94h8\u3032\u412b2r98h923h",
                "%"
            };
            String[] keys = {
                "......11111111111111111111111111111..",
                "\uffff,\uff73"
            };
            for (String message: messages) {
                for (String key: keys) {
                    String cipherText = AutoKeyVigenere.encipher(message, key);
                    String recoveredText = AutoKeyVigenere.decipher(cipherText, key);
                    Assert.assertEquals(message, recoveredText);
                }
            }
        }
    
        @Test(expected=IllegalArgumentException.class)
        public void testEmptyKey() {
            AutoKeyVigenere.encipher("hello", "");
        }
    }
    
  5. LETUS CHANG EOURT RADIT IONAL ATTIT UDETO THECO NSTRU CTION OFPRO GRAMS INSTE ADOFI MAGIN INGTH ATOUR MAINT ASKIS TOINS TRUCT ACOMP UTERW HATTO DOLET USCON CENTR ATERA THERO NEXPL AININ GTOHU MANBE INGSW HATWE WANTA COMPU TERTO DO
  6. COMPU TERSC IENCE ISNOM OREAB OUTCO MPUTE RSTHA NASTR ONOMY ISABO UTTEL ESCOP ESS
  7. 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 = 5.modInverse(729878871456) = 583903097165. The private key is (N,d) = (729880581317,583903097165)