DSC 40B

Problems tagged with "lecture-04"

Problem #038

Tags: asymptotic notation, lecture-04

Suppose Algorithm A takes \(\Theta(n^2)\) time, while Algorithm B takes \(\Theta(2^n)\) time. True or False: there could exist an input on which Algorithm \(B\) takes less time than Algorithm A.

True False
Solution

True.

Asymptotic notation only describes how the time grows for large \(n\) and hides constants. For example, if A takes \(1000n^2\) steps and B takes \(2^n\) steps, then B is faster on every input of size \(n = 10\)(1024 steps vs. 100,000).

Problem #039

Tags: asymptotic notation, lecture-04

Suppose algorithm A takes \(\Theta(n^2)\) time, while algorithm B takes \(\Theta(n^3)\) time.

True or False: there must exist an input of size 100 on which Algorithm A takes less time than Algorithm B.

Solution

False. Algorithm A could have really large constants. For example, it could take 1,000,000 \(n^2\) seconds to run, while algorithm B takes \(.0001 n^3\) seconds to run. Algorithm B will still be better for \(n = 100\).

Problem #074

Tags: lecture-04, theoretical lower bounds

Which of the below is a tight theoretical lower bound for the problem of finding a mode of a collection of \(n\) numbers?

Solution

\(\Theta(n)\). In the worst case, all numbers are distinct and we must loop through all of them to discover this, taking linear time.

Problem #075

Tags: lecture-04, theoretical lower bounds

Suppose you are given a (possibly unsorted) array containing \(n\) elements. Each element is either zero or one. You are tasked with determining if the majority (at least half) of the array's elements are one.

What is the tight theoretical lower bound for the worst case time complexity of this problem?

Solution

\(\Theta(n)\)

Problem #076

Tags: best and worst case, lecture-04

What is the best case time complexity of the following code?

def insertion_sort(arr):
    """Sort `arr` in ascending order."""
    n = len(arr)
    for i in range(1, n):
        x = arr[i]
        j = i - 1
        # find where to place x
        while j >= 0 and x < arr[j]:
            arr[j+1] = arr[j]
            j -= 1
        arr[j+1] = x
Solution

\(\Theta(n)\). The best case occurs when the array is already sorted. Notice that in this case, the while loop will never actually execute.

Problem #077

Tags: best and worst case, lecture-04

What is the worst case time complexity of the following code?

def insertion_sort(arr):
    """Sort `arr` in ascending order."""
    n = len(arr)
    for i in range(1, n):
        x = arr[i]
        j = i - 1
        # find where to place x
        while j >= 0 and x < arr[j]:
            arr[j+1] = arr[j]
            j -= 1
        arr[j+1] = x
Solution

\(\Theta(n^2)\). The worst case occurs when the array is sorted in reverse order. In this case, the while-loop executes on every iteration of the for-loop, and must move x from its current position all the way to the beginning of the array.

Problem #078

Tags: best and worst case, lecture-04

What is the best case time complexity of the following function?

def foo(arr):
    """arr is an array of size n"""
    for x in arr:
        for y in arr:
            if sum([x,y]) == 5:
                return sum(arr)
    return False
Solution

\(\Theta(n)\). In the best case, we return sum(arr) on the very first iteration.

Problem #079

Tags: best and worst case, lecture-04

What is the worst case time complexity of the following function?

def foo(arr):
    """arr is an array of size n"""
    for x in arr:
        for y in arr:
            if sum([x,y]) == 5:
                return sum(arr)
    return False
Solution

\(\Theta(n^2)\). In the worst case, we never find x + y == 5, and go through all \(\Theta(n^2)\) iterations just to return False at the end.

Problem #080

Tags: best and worst case, lecture-04

What is the best case time complexity of the following function in terms of \(n\)?

def foo(arr):
    """`arr` is a list containing n numbers."""
    previous = None
    n = len(arr)
    for x in arr:
        if x == previous:
            for i in range(n**2):
                print('Equal!')
            return
        previous = x
Solution

\(\Theta(n)\)

Problem #081

Tags: best and worst case, lecture-04

What is the best case time complexity of the following function in terms of \(n\)?

def foo(arr):
    """`arr` is a list containing n numbers."""
    previous = None
    n = len(arr)
    for x in arr:
        if x == previous:
            for i in range(n**2):
                print('Equal!')
            return
        previous = x
Solution

\(\Theta(n)\)

Problem #082

Tags: best and worst case, lecture-04

What is the worst case time complexity of the following function in terms of \(n\)?

def foo(arr):
    """`arr` is a list containing n numbers."""
    previous = None
    n = len(arr)
    for x in arr:
        if x == previous:
            for i in range(n**2):
                print('Equal!')
            return
        previous = x
Solution

\(\Theta(n^2)\)

Problem #083

Tags: lecture-04, theoretical lower bounds

Suppose you are given a (possibly unsorted) array of \(n\) floating point numbers and are tasked with counting the number of elements in the array which are within ten of the array's maximum (you are not told the maximum). That is, you must count the number of elements \(x\) such that \(m - x \geq 10\), where \(m\) is the array's maximum. You may assume that the array does not contain duplicate numbers.

What is the tight theoretical lower bound for the worst case time complexity of this problem?

Solution

\(\Theta(n)\).

It takes \(\Theta(n)\) time to find the maximum. After this, we loop through and keep track of how many elements are within ten of the maximum -- this also takes \(\Theta(n)\) time.

Problem #084

Tags: best and worst case, lecture-04

The below code takes in an array of numbers and returns the index of the first maximum. What is this code's best case time complexity?

def index_of_maximum(arr):
    """`arr` is an array with n elements."""
    for i, x in enumerate(arr):
        if x == max(arr):
            return i
Solution

\(\Theta(n)\)

Problem #085

Tags: best and worst case, lecture-04

The below code takes in an array of numbers and returns the index of the first maximum.

def foo(arr):
    """`arr` is an array with n elements."""
    for i, x in enumerate(arr):
        if x == max(arr):
            return i

What is the worst case time complexity of the function?

Solution

\(\Theta(n^2)\)

Problem #086

Tags: best and worst case, lecture-04

The code below takes in an array of \(n\) numbers and checks whether there is a pair of numbers in the array which, when added together, equal the maximum element of the array.

What is the best case time complexity of this code as a function of \(n\)? State your answer using asymptotic notation.

def exists_pair_summing_to_max(arr):
    n = len(arr)
    maximum = max(arr)
    for i in range(n):
        for j in range(i + 1, n):
            if arr[i] + arr[j] == maximum:
                return True
    return False
Solution

\(\Theta(n)\)

Problem #087

Tags: lecture-04, expected time

What is the expected time complexity of the following function?

import random

def foo(n):
    # randomly draw a number from 0, 1, 2, ..., n-1
    x = random.randint(0, n-1)
    if x == 1:
        for i in range(n):
            print(i)
    else:
        print("lucky!")

    for i in range(n):
        print(i)
Solution

\(\Theta(n)\). The last loop always runs \(n\) times. With probability \(1/n\) we have x == 1, and the first loop also runs, taking \(\Theta(n)\) time; otherwise we do constant work before the last loop. So the expected time is \(\frac{1}{n} \cdot \Theta(n) + \Theta(1) + \Theta(n) = \Theta(n)\). (In fact, every case takes \(\Theta(n)\) time.)

Problem #088

Tags: time complexity, lecture-04

What is the expected time complexity of the following function? State your answer using asymptotic notation.

import random

def boo(n):
    # draw a number uniformly at random from 0, 1, 2, ..., n-1 in Theta(1)
    x = random.randrange(n)

    for i in range(x): # <-- note that the range is random!
        print(i)
Solution

\(\Theta(n)\)

Problem #089

Tags: lecture-04, expected time

What is the expected time complexity of the function below?

import random

def foo(n):
    # draw a random number uniformly from 0, 1, 2, ..., 99
    # in constant time
    x = random.randrange(100)
    for i in range(x):
        for j in range(n):
            print("Here!")
Solution

\(\Theta(n)\). Note that in this question, the range of \(x\) is between 0 and 99. Therefore the outer loop will run a constant number of times no matter what \(x\) is. Then we can focus on the inner loop, which runs for \(n\) times the print() operation, which is \(\Theta(n)\) time. Therefore, the expected time complexity for this piece of code is \(\Theta(n)\).

Problem #090

Tags: lecture-04, theoretical lower bounds, sorted structure

Suppose you are given a sorted array containing \(n\) elements. Each element is either zero or one. You are tasked with determining if the majority (at least half) of the array's elements are one. What is the tight theoretical lower bound for the worst case time complexity of any algorithm which solves this problem?

Solution

\(\Theta(1)\)

Problem #091

Tags: best and worst case, lecture-04

The code below takes in two lists, each containing \(n\) integers, and determines if the any number in the second list appears at least \(n/2\) times in the first list.

def boo(first_list, second_list):
    """first_list and second_list are both of size n"""
    n = len(first_list)
    for x in second_list:
        count = 0
        for y in first_list:
            if x == y:
                count += 1
            if count >= n // 2:
                return True
    return False

Part 1)

What is the best case time complexity of this code as a function of \(n\)? State your answer using asymptotic notation.

Solution

\(\Theta(n)\)

Part 2)

What is the worst case time complexity of this code as a function of \(n\)? State your answer using asymptotic notation.

Solution

\(\Theta(n^2)\)

Solution

The best case is when the first element of the second list appears at least \(n/2\) times in the first list. In this case, the code will return True after iterating \(n/2\) times through the inner loop, taking \(\Theta(n)\) time total.

In the worst case, there is no element in the second list that appears \(n/2\) times in the first. We make all \(n\) iterations of the outer loop, and during each of the outer iterations, make \(n\) iterations of the inner loop, for a total of \(\Theta(n^2)\) time.

Problem #092

Tags: best and worst case, lecture-04

The code below takes in two lists, each containing \(n\) integers.

def are_lists_equal(list1, list2):
    if len(list1) != len(list2):
        return False
    for i in range(len(list1)):
        if list1[i] != list2[j]:
            return False
    return True


def foo(list1, list2):
    if are_lists_equal(list1, list2):
        for i in list1:
            print(i)
    else:
        for i in list1:
            for j in list2:
                for k in range(len(list1)):
                    print(i, j, k)

What is the best case time complexity of foo as a function of \(n\)? State your answer using asymptotic notation.

Solution

The best case is where the lists are equal. In this case the time complexity is \(\Theta(n)\). The worst case is when the lists are not equal in which case the time complexity is \(\Theta(n^3)\).

Problem #093

Tags: lecture-04, expected time

What is the expected time complexity of the function below?

import random

def foo(n):
    # draw a random number uniformly from 0, 1, 2, ..., n-1
    # in constant time
    x = random.randrange(n)
    for i in range(x):
        for j in range(n):
            print("Here!")
Solution

\(\Theta(n^2)\). The inner loop is not random, and the time complexity of it is always \(\Theta(n)\). Therefore the expected time complexity of this function will be (using the arithmetic sequence sum formula)

\[\frac{1}{n}\sum_{i = 0}^{n - 1} i \times\Theta(n) = \Theta(n^2). \]

Problem #094

Tags: lecture-04, expected time

What is the expected time complexity of the following function?

import random

def foo(n):
    for i in range(n):
        # draw a number uniformly at random from 0, 1, 2, ..., n-1 in Theta(1)
        x = random.randrange(n)
        for j in range(x):
            print(j)
Solution

\(\Theta(n^2)\). For this function, the outer loop wil execute \(n\) times no matter what, hence we only have to find what the expected time complexity for the inner loop is, then combine that together with the outer loop. For the inner loop, the expected time complexity is

\[\frac{1}{n}\sum_{i = 1}^{n - 1} i = \frac{1}{n}\cdot\Theta(n^2) = \Theta(n). \]

Then, the expected complexity for the whole function will be

\[\Theta(n) \times\Theta(n) = \Theta(n^2). \]

Problem #095

Tags: lecture-04, expected time

What is the expected time complexity of the function below?

import random

def foo(n):
    # draw a number uniformly at random from 0, 1, 2, ..., n-1
    # in constant time
    x = random.randrange(n)
    if x < 100:
        for j in range(n**2):
            print(j)
Solution

\(\Theta(n)\). The for loop is only executed if \(x\) is less than 100, therefore we can write the expected time as (ignoring constants \(c\)):

\[ f(n) = \frac{1}{n}\bigg(\sum_{i = 0}^{99} n^2 + \sum_{i = 100}^{n - 1} 1 \bigg)= \frac{1}{n}\cdot(\Theta(n^2) + \Theta(n)) = \Theta(n). \]

Problem #096

Tags: lecture-04, expected time

What is the expected time complexity of the following function? State your answer using asymptotic notation.

import random

def foo(n):
    x = random.randrange(n)

    if x < 8:
        for j in range(n**3):
            print(j)
    else:
        for j in range(n):
            print(j)
Solution

\(\Theta(n^2)\). The expected time complexity for this function is

\[\frac{1}{n}\bigg(\sum_{i = 0}^{7} n^3 + \sum_{i = 8}^{n - 1} n \bigg)= \frac{1}{n}(\Theta(n^3) + \Theta(n^2)) = \Theta(n^2). \]

Problem #097

Tags: time complexity, lecture-04

What is the expected time complexity of the function below? State your answer using asymptotic notation.

import random
def foo(n):
    # draw a number uniformly at random from 0, 1, 2, ..., n-1 in Theta(1) time
    x = random.randrange(n)
    if x < 20:
        for i in range(n**3):
            print("Very unlucky!")
    elif x < n / 2:
        for i in range(n):
            print("Unlucky!")
    else:
        print("Lucky!")
Solution

\(\Theta(n^2)\).

Problem #098

Tags: lecture-04, expected time

What is the expected time complexity of the function below?

import random

def foo(n):
    # draw a number uniformly at random from 0, 1, 2, ..., n-1 in Theta(1)
    x = random.randrange(n)
    if x < math.sqrt(n):
        for j in range(n):
            print(j)
Solution

\(\Theta(\sqrt n)\). Since the for loop only executes when \(x\) is less than \(\sqrt{n}\), the expected time complexity is

\[\frac{1}{n}\bigg(\sum_{i = 1}^{\lfloor \sqrt{n} \rfloor} n + \sum_{i = \lceil \sqrt{n} \rceil}^{n - 1} 1 \bigg)\approx\frac{1}{n}(n \sqrt{n} + (n - \sqrt{n})) = \Theta(\sqrt{n}). \]

Problem #099

Tags: lecture-04, expected time

What is the expected time complexity of the function below?

You may assume that math.sqrt takes \(\Theta(1)\) time.

import random
import math

def foo(n):
    # draw a number uniformly at random from 0, 1, 2, ..., n-1 in Theta(1)
    x = random.randrange(n)

    if x < math.sqrt(n):
        for j in range(n):
            print(j)
    elif x < n//2:
        print('Ok!')
    else:
        for i in range(n**2):
            print(i)
Solution

\(\Theta(n^2)\). The expected time complexity for this function is

\[ f(n) = \frac{1}{n}\bigg(\sum_{i = 1}^{\lfloor \sqrt{n} \rfloor} n + \sum_{i = \lceil \sqrt{n} \rceil}^{\lfloor n/2 \rfloor} 1 + \sum_{i = \lceil n/2 \rceil}^{n - 1} n^2 \bigg) = \frac{1}{n}(\Theta(n \sqrt{n}) + \Theta(n) + \Theta(n^3)) = \Theta(n^2). \]

Problem #100

Tags: lecture-04, expected time

What is the expected time complexity of the function below? State your answer using asymptotic notation.

You may assume that math.sqrt and math.log2 take \(\Theta(1)\) time.

import random
import math

def boo(n):
    # draw a number uniformly at random from 0, 1, 2, ..., n-1 in Theta(1)
    x = random.randrange(n)

    if x < math.log2(n):
        for i in range(n**2):
            print("Very unlucky!")
    elif x < math.sqrt(n):
        for i in range(n):
            print("Unlucky!")
    else:
        print("Lucky!")
Solution

\(\Theta(n \log n)\) There are three cases: Case 1) \(x < \log_2 n\); Case 2) \(1 < x < \sqrt n\); and Case 3) \(x \geq\sqrt{n}\).

The probability of Case 1 is \(\log n / n\). The time taken in case 1 is \(\Theta(n^2)\).

The probability of Case 2 is

\[\frac{\sqrt n}{n} - \frac{\log_2 n}{n} = \Theta(\sqrt n / n) = \Theta(1/\sqrt n). \]

The time taken in this case is \(\Theta(n)\).

The probability of Case 3 is 1 minus the probability of the sum of the other two cases, which will simply be \(\Theta(1)\):

\[ 1 - \Theta(1 / n) - \Theta(1 / \sqrt{n}) = \Theta(1). \]

The time taken is \(\Theta(1)\).

Therefore, the expected time is:

Problem #101

Tags: lecture-04, expected time

What is the expected time complexity of the function below? State your answer using asymptotic notation.

You may assume that math.sqrt and math.log take \(\Theta(1)\) time. math.log computes the natural log.

import random
import math

def foo(n):
    # draw a number uniformly at random from 0, 1, 2, ..., n-1 in Theta(1)
    x = random.randrange(n)

    if x < math.log(n):
        for j in range(n**2):
            print(j)
    elif x < math.sqrt(n):
        print('Ok!')
    else:
        for i in range(n):
            print(i)
Solution

\(\Theta(n \log n)\) There are three cases: Case 1) \(x < \log n\); Case 2) \(\log n \geq x < \sqrt n\); and Case 3) \(x \geq\sqrt{n}\).

The probability of Case 1 is \(\log n / n\). The time taken in case 1 is \(\Theta(n^2)\).

The probability of Case 2 is

\[\frac{\sqrt n}{n} - \frac{\log n}{n} = \Theta(\sqrt n / n) = \Theta(1/\sqrt n). \]

The time taken in this case is \(\Theta(1)\).

The probability of Case 3 is 1 minus the probability of the sum of the other two cases, which will simply be \(\Theta(1)\):

\[ 1 - \Theta((\log n) / n) - \Theta(1 / \sqrt{n}) = \Theta(1) \]

Therefore, the expected time is:

\[\begin{aligned} \frac{\log n}{n} \times \Theta(n^2) + \Theta(1/\sqrt n) \times \Theta(1) + \Theta(1) \times \Theta(n) &= \Theta(n \log n) + \Theta(1 / \sqrt n) + \Theta(n) \\ &= \Theta(n \log n) \end{aligned}\]

Problem #102

Tags: lecture-04, theoretical lower bounds

Consider the following problem: given a (possibly unsorted) list of size \(n\) containing only ones and zeros, determine if the number of ones is equal to the number of zeros.

Part 1)

What is a tight theoretical lower bound for this problem? State your answer as a function of \(n\) using asymptotic notation.

Solution

\(\Theta(n)\)

Part 2)

Now assume that the input array is guaranteed to have the following structure: there will be \(a\) ones, followed by \(b\) zeros, followed again by \(a\) ones. An example of such an array is: [1, 1, 0, 0, 0, 1, 1]. In this example, \(a = 2\) and \(b = 3\).

Consider the same problem as before: that is, given an array satisfying the above assumption, determine if the number of ones in the array is equal to the number of zeros. Of course, \(a\) and \(b\) are not known ahead of time.

What is a tight theoretical lower bound for this problem?

Solution

\(\Theta(1)\) Let \(n\) be the length of the array. Assume that \(n\) is even (if it is odd, we can immediately say that there are not an equal number of ones and zeros). In fact, we can assume that \(n\) is divisible by four, since \(n = a + b + a = 2a + b\), which must equal \(4a\) if there are an equal number of ones and zeros.

For there to be an equal number of ones as zeros, the middle \(n/2\) elements must be zero, while the first \(n/4\) and last \(n/4\) must be ones. If that is the case, then the last one in the first group of ones will occur at index \(n / 4 - 1\), and the first zero will occur at \(n / 4 \) A constant-time algorithm is to check the element at index \(n/4 - 1\) and verify that it is a one. Then we check the element at index \(n/4 \) and check that it is a zero.

Consider, for example, this array with \(n = 10\):

\[\mintinline{python}{[1, 1, 1, 0, 0, 0, 0, 1, 1, 1]}\]

Then \(n / 10 = 10 / 4 = 2.5 = 2\). Therefore, we check the elements at index 1 and 2 in constant time and find them to both be one, which tells us that this array does not have an equal number of zeros and ones.

Now consider the array below:

\[\mintinline{python}{[1, 1, 0, 0, 0, 0, 1, 1]}\]

Since \(n = 8\), we again check the elements at index 1 and 2. We find 1 and 0, which tells us that the array does have an equal number of zeros and ones, as expected.