LMU ☀️ CMSI 2130
ALGORITHMS
HOMEWORK #1 PARTIAL ANSWERS

NOTE: Many of the “answers” are only suggestive of how you might solve the problem, and shouldn’t be considered answers that would get you full credit on any assignment.

  1. (Dasgupta 0.1) a.$\Theta$ b.$O$ c.$\Theta$ d.$\Theta$ e.$\Theta$ f.$\Theta$ g.$\Omega$ h.$\Omega$ i.$\Omega$ j.$\Omega$ k.$\Omega$ l.O m.O n.$\Theta$ o.$\Omega$ p.O q.$\Theta$

    Hints: Here are some things that are helpful:

    • $\log(3n) = \log 3 + \log n$, (and note $\log 3$ is just a constant)
    • $\log(n^2) = 2\log n$
    • $5^{\log_2 n} = 5^{(\log_2 5)(\log_5 n)} = 5^{(\log_5 n)(\log_2 5)} = n^{\log_2 5} \approx n^{2.321928}$
    • Part (q) seems pretty hard. I’m trusting Wikipedia for this one.
  2. (Dasgupta 1.13) Well, you can check this easily enough in Python:
    >>> pow(5,30000,31)-pow(6,123456,31)
    0
    
    But, seriously, you should learn to do these things by hand. Use Fermat’s Little Theorem, which says that if $p$ is prime, then $a^{p-1} \equiv 1 \pmod p$. Note that 31 is prime. 30000 is a multiple of 30 so $5^{30000}\:\textrm{mod}\:31$ is 1. 123456 is not a multiple of 30, but 123450 is (remember from grade school how to tell if something is divisible by 3?). So $$\begin{eqnarray} 6^{123456}\;(\textrm{mod}\:31) &=& 6^{123450} \times 6^6\;(\textrm{mod}\:31) \nonumber \\ &=& 6^6\;(\textrm{mod}\:31) \nonumber \\ &=& 36 \times 36 \times 36\;(\textrm{mod}\:31) \nonumber \\ &=& 5 \times 5 \times 5\;(\textrm{mod}\:31) \nonumber \\ &=& 125\;(\textrm{mod}\:31) \nonumber \\ &=& 1 \nonumber \end{eqnarray}$$ Both are 1 (mod 31), so their difference is 0 (mod 31) and hence is divisible by 31.
  3. (Levitin 2.1.5b) We are asked to prove that the number of bits in the binary representation of a positive integer $n$ is: $$ b = \lceil \log_2 (n+1) \rceil $$

    First, let’s see if this makes sense:

    $n$Binary$\log_2 (n+1)$$\lceil \log_2 (n+1) \rceil$
    1111
    2101.584962
    31122
    41002.321933
    51012.584963
    61102.807353
    711133
    810003.169934

    Okay so it looks right. How do we prove it? Well, we can get further intuition like this:

    • The binary numerals of length 1 are: 1
    • The binary numerals of length 2 are: 2, 3
    • The binary numerals of length 3 are: 4, 5, 6, 7
    • The binary numerals of length 4 are: 8, 9, 10, 11, 12, 13, 14, 15
    • The binary numerals of length 5 are: 16–31
    • The binary numerals of length 6 are: 32–63
    • The binary numerals of length 7 are: 64–127

    So let’s prove this thing. The possible values of a $b$-bit number are 100...0 through 111...1, i.e., from $2^{b-1}$ to $2^b-1$. Since we are dealing with integers only we can do some neat things:

    $$ \begin{eqnarray} 2^{b-1} & \leq & n & \leq & 2^b-1 \\ 2^{b-1} & \leq & n & < & 2^b \\ 2^{b-1} & < & n+1 & \leq & 2^b \\ b-1 & < & \log_2(n+1) & \leq & b \\ & & \lceil \log_2(n+1) \rceil & = & b \end{eqnarray} $$
  4. (Levitin 2.1.10)
    1. The number of grains is $1 + 2 + 4 + 8 + \cdots + 2^{63} = 2^{64}-1 = 18,446,744,073,709,551,615$. If you counted one per second, it would take ... oh, let’s see how many years this is using Python:
      >>> (2**64-1) / 60 / 60 / 24 / 365.2522
      584538525256.2511
      
      So over 584 billion years.
    2. Now the number of grains is $1 + 3 + 5 + 7 + \cdots + 127$. Python says this is:
      >>> sum(2*n+1 for n in range(64))
      4096
      
      It would take 4096 seconds to count them all, which is 68 minutes and 16 seconds.
  5. Horner’s Rule in Python, with unit tests!

    poly.py
    class Polynomial:
        def __init__(self, coefficients):
            self.coefficients = coefficients
    
        def eval(self, x):
            result = 0
            for e in reversed(range(max(self.coefficients.keys())+1)):
                result = result * x + self.coefficients.get(e, 0)
            return result
    
    import unittest
    class EvaluatorTest(unittest.TestCase):
        TEST_CASES = [
            ({0:5},             [(2,5), (12,5)]),     # 5
            ({1:2},             [(2,4), (10,20)]),    # 2x
            ({1:2, 0:4},        [(2,8), (-3,-2)]),    # 2x + 4
            ({3:2, 1:5, 0:-1},  [(1,6), (3,68)]),     # 2x^3 + 5x - 1
            ({8:-6, 4:1, 2:3},  [(1,-2), (2,-1508)]), # -6x^8 + x^4 + 3x^2
            ({7:2, 1:-5, 2:-2}, [(0,0), (2,238)]),    # 2x^7 - 2x^2 - 5x
            ]
        def test_eval(self):
            for (coefficients, cases) in EvaluatorTest.TEST_CASES:
                for (x, expected) in cases:
                    self.assertEqual(Polynomial(coefficients).eval(x), expected)
    
    unittest.main()
    

    Here it is in Java:

    import java.util.Collections;
    import java.util.Map;
    
    public class Polynomial {
    
        private Map<Integer, Integer> coefficients;
    
        public Polynomial(Map<Integer, Integer> coefficients) {
            this.coefficients = coefficients;
        }
    
        public double eval(double x) {
            double result = 0;
            for (int e = Collections.max(coefficients.keySet()); e >= 0; e--) {
               result = result * x + coefficients.getOrDefault(e,  0);
            }
            return result;
        }
    }
    
    import org.junit.Assert;
    import org.junit.Test;
    
    import com.google.common.collect.ImmutableMap;
    
    public class PolynomialTest {
    
        @Test
        public void test5() {
            Polynomial p = new Polynomial(ImmutableMap.of(0, 5));
            Assert.assertEquals(p.eval(2), 5, 0.0000001);
            Assert.assertEquals(p.eval(12), 5, 0.0000001);
        }
    
        @Test
        public void test2x() {
            Polynomial p = new Polynomial(ImmutableMap.of(1, 2));
            Assert.assertEquals(p.eval(2), 4, 0.0000001);
            Assert.assertEquals(p.eval(10), 20, 0.0000001);
        }
    
        @Test
        public void test2xPlus4() {
            Polynomial p = new Polynomial(ImmutableMap.of(1, 2, 0, 4));
            Assert.assertEquals(p.eval(2), 8, 0.0000001);
            Assert.assertEquals(p.eval(-3), -2, 0.0000001);
        }
    
        @Test
        public void testSlightlyLongerPolynomials() {
            Polynomial p = new Polynomial(ImmutableMap.of(3, 2, 1, 5, 0, -1));
            Assert.assertEquals(p.eval(1), 6, 0.0000001);
            Assert.assertEquals(p.eval(3), 68, 0.0000001);
            p = new Polynomial(ImmutableMap.of(8, -6, 4, 1, 2, 3));
            Assert.assertEquals(p.eval(1), -2, 0.0000001);
            Assert.assertEquals(p.eval(2), -1508, 0.0000001);
            p = new Polynomial(ImmutableMap.of(7, 2, 1, -5, 2, -2));
            Assert.assertEquals(p.eval(0), 0, 0.0000001);
            Assert.assertEquals(p.eval(2), 238, 0.0000001);
        }
    }
    
  6. Three Partition in Python. Remember it is okay for now to create an inefficient brute-force solution, which is what I’ve done here:
    def helper(a, target):
        if len(a) == 3:
            return True
        for i in range(1, len(a)-1):
            for j in range(i+1, len(a)):
                if a[0] + a[i] + a[j] == target:
                    if helper(a[1:i] + a[i+1:j] + a[j+1:], target):
                        return True
        return False
    
    def three_partition(a):
        if len(a) in (0, 3):
            return True
        if len(a) % 3 != 0:
            return False
        chunks = len(a) // 3
        if sum(a) % chunks != 0:
            return False
        return helper(a, sum(a) // chunks)
    
  7. If not memoized, we call the function 335,919 times. If memoized, we make only 199 calls.
  8. The Python decorator from the very first page of course notes will work. To memoize directly, you could do this:
    cache = {}
    def c(n, k):
        if (n, k) in cache:
            return cache[(n,k)]
        result = 1 if k <= 0 or k >= n else c(n-1,k-1) + c(n-1,k)
        cache[(n,k)] = result
        return result
    
    import unittest
    class CominationTester(unittest.TestCase):
        def test_some_things(self):
            self.assertEqual(c(8,0), 1)
            self.assertEqual(c(17,12), 6188)
            self.assertEqual(c(20,11), 167960)
            self.assertEqual(c(99999,99999), 1)
            self.assertEqual(c(200,195), 2535650040)
    
    unittest.main()
    

    If you do this in Java, you should probably index the hashmap by a string combination of $n$ and $k$. You could also make a Pair class but you would have to remember to override both equals and hashCode.