2021 CollegeBoard Practice MC Corrections & Reflection

Overall score: 64/70

Question 9: Digital representation of audio signal

  • Which of the following best explains how an analog audio signal is typically represented by a computer?
  • Answer I put: An analog audio signal is measured as a sequence of operations that describe how the sound can be reproduced. The operations are represented at the lowest level as programming instructions.
  • Correct Answer: An analog audio signal is measured at regular intervals. Each measurement is stored as a sample, which is represented at the lowest level as a sequence of bits.
  • Explanation: While some programming languages can be used to manipulate audio data, this is not how audio data are represented digitally. At the lowest level, all digital data are represented as sequences of bits. Analog signals are sampled digitally at discrete intervals over time. These samples, like all digital data, are represented at the lowest level as a sequence of bits.

Question 24: Lossy compression scenario

  • In which of the following situations would it be most appropriate to choose lossy compression over lossless compression?
  • Answer I put: Storing a video file on an external device in order to preserve the highest possible video quality
  • Correct Answer: Storing music files on a smartphone in order to maximize the number of songs that can be stored
  • Explanation: In situations where quality is maximally important, lossless compression algorithms are typically chosen. In situations where minimizing data size or transmission time is maximally important, lossy compression algorithms are typically chosen.

Question 33: Efficiency of driving route algorithms

  • A company delivers packages by truck and would like to minimize the length of the route that each driver must travel in order to reach delivery locations. The company is considering two different algorithms for determining delivery routes.

  • Algorithm I: Generate all possible routes, compute their lengths, and then select the shortest possible route. This algorithm does not run in reasonable time.
  • Algorithm II: Starting from an arbitrary delivery location, find the nearest unvisited delivery location. Continue creating the route by selecting the nearest unvisited location until all locations have been visited. This algorithm does not guarantee the shortest possible route and runs in time proportional to n squared.

  • Which of the following best categorizes algorithm II?
  • Answer I put: Algorithm II provides no improvement over algorithm I because neither algorithm runs in reasonable time.
  • Correct Answer: Algorithm II uses a heuristic approach to provide an approximate solution in reasonable time.
  • Explanation: Heuristic means proceeding to a solution by trial and error or by rules that are only loosely defined. Algorithm II runs in time proportional to n squared, which is considered reasonable time because n squared is a polynomial. This is considered a heuristic approach because it finds an approximate solution in reasonable time when the technique that finds an exact solution (algorithm I) does not run in reasonable time.

Question 43: Keylogging example

  • Which of the following best exemplifies the use of keylogging to gain unauthorized access to a computer system?
  • Answer I put: A user logs into an unsecure Web site. Someone else is able to view unencrypted log-in information as it is transmitted over the Internet. The user has the same username and password for multiple accounts, so the user’s log-in information for multiple systems may be compromised.
  • Correct Answer: A user unintentionally installs a program on their computer that records all user input and forwards it to another computer. A few weeks later, someone else is able to access the user’s computer using the recorded data.
  • Explanation: Keylogging is the use of a program to record every keystroke made by a computer to gain fraudulent access to passwords and other confidential information.

Question 55: Move element from end of list to beginning

  • A code segment is intended to transform the list utensils so that the last element of the list is moved to the beginning of the list.

  • For example, if utensils initially contains [“fork”, “spoon”, “tongs”, “spatula”, “whisk”], it should contain [“whisk”, “fork”, “spoon”, “tongs”, “spatula”] after executing the code segment.

  • Which of the following code segments transforms the list as intended?
  • Answer I put:
  • Correct Answer:
  • Explanation: This code segment assigns the value of the last element of the list to the variable temp, then removes the last element of the list, then inserts temp as the first element of the list.

Question 64: Error in ageGroup procedure

  • In the following procedure, the parameter age represents a person’s age. The procedure is intended to return the name of the age group associated with age. People who are under 18 are considered minors, people who are 65 and older are considered senior citizens, and all other people are considered adults. The procedure does not work as intended.
  • Removing which two lines of code will cause the procedure to work as intended?
  • Answer I put: A - Line 3, C - Line 10
  • Correct Answer: B - Line 8, C - Line 10
  • Explanation: Line 8 should be removed. This return statement causes execution of the procedure to end early. As a result, if age is less than 18, result will never be assigned the value “minor”. Line 10 should be removed. This statement causes result to be assigned the value “adult”, even if it should have been assigned the value “senior citizen”.

Popcorn Hacks From Frequently Missed Questions

4, 5, 11, 50, 56, 64, 65

Q4: Cause of Overflow Error (1D, binary math)

as a popcorn hack (binary challenge), describe an overflow in 8 binary digits

Overflowing occurs when a calculation results in a value that exceeds the maximum representable value in the given number of bits (in this case 8-bits).

Add 1 to 11111111 (255)

11111111 + 1 = 1000000000, truncates to 00000000 (0 in decimal) This is an overflow situation where the result exceeds the maximum representable value and wraps around to 0.

Subtract 1 from 00000000 (0)

00000000 - 1 = 11111111, truncates to 11111111 (255 in decimal) This is an underflow situation where the result is smaller than the minimum representable value and wraps around to the maximum representable value.

Q5: Inputs to Logic Circuit (2D, binary logic)

as a popcorn hack (coding challenge), create multiple new circuits and gates

NOT, NAND, NOR, XOR, XNOR

Create a hypothetical circuit, such as burglar alarm, decision tree for autonomous car, etc.

# Define logic gates
def NOT(A):
    return not A

def NAND(A, B):
    return not (A and B)

def NOR(A, B):
    return not (A or B)

def XOR(A, B):
    return (A or B) and not (A and B)

def XNOR(A, B):
    return not ((A or B) and not (A and B))

# Burglar alarm circuit
def burglar_alarm(sensor1, sensor2):
    # Control unit
    control_unit_output = NOR(sensor1, sensor2)
    
    # Alarm
    alarm_output = NOT(control_unit_output)
    
    return alarm_output

# Simulate burglar alarm activation
sensor1_status = True  # Sensor 1 triggered
sensor2_status = False  # Sensor 2 not triggered

alarm_status = burglar_alarm(sensor1_status, sensor2_status)
print("Alarm Status:", alarm_status)  # Should trigger the alarm (True)
Alarm Status: True

Q11: Color Represented by Binary Triplet (2D, binary)

as a hack (binary challenge), make the rgb standard colors

Standard RGB colors:

  • Red: RGB(255, 0, 0)
  • Green: RGB(0, 255, 0)
  • Blue: RGB(0, 0, 255)
  • Yellow: RGB(255, 255, 0)
  • Cyan: RGB(0, 255, 255)
  • Magenta: RGB(255, 0, 255)
  • White: RGB(255, 255, 255)
  • Black: RGB(0, 0, 0)

    as a 2nd hack, make your favorite color pattern

# Define RGB values for colors
blue = (0, 0, 255)
green = (0, 255, 0)
yellow = (255, 255, 0)

# Create a color gradient pattern
color_pattern = []
for i in range(101):  # 100 steps for smooth transition
    if i <= 50:
        # Transition from blue to green
        r = blue[0] + int((green[0] - blue[0]) * i / 50)
        g = blue[1] + int((green[1] - blue[1]) * i / 50)
        b = blue[2] + int((green[2] - blue[2]) * i / 50)
    else:
        # Transition from green to yellow
        r = green[0] + int((yellow[0] - green[0]) * (i - 50) / 50)
        g = green[1] + int((yellow[1] - green[1]) * (i - 50) / 50)
        b = green[2] + int((yellow[2] - green[2]) * (i - 50) / 50)
    color_pattern.append((r, g, b))

# Print the custom color pattern
for idx, color in enumerate(color_pattern):
    print(f"Step {idx}: RGB{color}")
Step 0: RGB(0, 0, 255)
Step 1: RGB(0, 5, 250)
Step 2: RGB(0, 10, 245)
Step 3: RGB(0, 15, 240)
Step 4: RGB(0, 20, 235)
Step 5: RGB(0, 25, 230)
Step 6: RGB(0, 30, 225)
Step 7: RGB(0, 35, 220)
Step 8: RGB(0, 40, 215)
Step 9: RGB(0, 45, 210)
Step 10: RGB(0, 51, 204)
Step 11: RGB(0, 56, 199)
Step 12: RGB(0, 61, 194)
Step 13: RGB(0, 66, 189)
Step 14: RGB(0, 71, 184)
Step 15: RGB(0, 76, 179)
Step 16: RGB(0, 81, 174)
Step 17: RGB(0, 86, 169)
Step 18: RGB(0, 91, 164)
Step 19: RGB(0, 96, 159)
Step 20: RGB(0, 102, 153)
Step 21: RGB(0, 107, 148)
Step 22: RGB(0, 112, 143)
Step 23: RGB(0, 117, 138)
Step 24: RGB(0, 122, 133)
Step 25: RGB(0, 127, 128)
Step 26: RGB(0, 132, 123)
Step 27: RGB(0, 137, 118)
Step 28: RGB(0, 142, 113)
Step 29: RGB(0, 147, 108)
Step 30: RGB(0, 153, 102)
Step 31: RGB(0, 158, 97)
Step 32: RGB(0, 163, 92)
Step 33: RGB(0, 168, 87)
Step 34: RGB(0, 173, 82)
Step 35: RGB(0, 178, 77)
Step 36: RGB(0, 183, 72)
Step 37: RGB(0, 188, 67)
Step 38: RGB(0, 193, 62)
Step 39: RGB(0, 198, 57)
Step 40: RGB(0, 204, 51)
Step 41: RGB(0, 209, 46)
Step 42: RGB(0, 214, 41)
Step 43: RGB(0, 219, 36)
Step 44: RGB(0, 224, 31)
Step 45: RGB(0, 229, 26)
Step 46: RGB(0, 234, 21)
Step 47: RGB(0, 239, 16)
Step 48: RGB(0, 244, 11)
Step 49: RGB(0, 249, 6)
Step 50: RGB(0, 255, 0)
Step 51: RGB(5, 255, 0)
Step 52: RGB(10, 255, 0)
Step 53: RGB(15, 255, 0)
Step 54: RGB(20, 255, 0)
Step 55: RGB(25, 255, 0)
Step 56: RGB(30, 255, 0)
Step 57: RGB(35, 255, 0)
Step 58: RGB(40, 255, 0)
Step 59: RGB(45, 255, 0)
Step 60: RGB(51, 255, 0)
Step 61: RGB(56, 255, 0)
Step 62: RGB(61, 255, 0)
Step 63: RGB(66, 255, 0)
Step 64: RGB(71, 255, 0)
Step 65: RGB(76, 255, 0)
Step 66: RGB(81, 255, 0)
Step 67: RGB(86, 255, 0)
Step 68: RGB(91, 255, 0)
Step 69: RGB(96, 255, 0)
Step 70: RGB(102, 255, 0)
Step 71: RGB(107, 255, 0)
Step 72: RGB(112, 255, 0)
Step 73: RGB(117, 255, 0)
Step 74: RGB(122, 255, 0)
Step 75: RGB(127, 255, 0)
Step 76: RGB(132, 255, 0)
Step 77: RGB(137, 255, 0)
Step 78: RGB(142, 255, 0)
Step 79: RGB(147, 255, 0)
Step 80: RGB(153, 255, 0)
Step 81: RGB(158, 255, 0)
Step 82: RGB(163, 255, 0)
Step 83: RGB(168, 255, 0)
Step 84: RGB(173, 255, 0)
Step 85: RGB(178, 255, 0)
Step 86: RGB(183, 255, 0)
Step 87: RGB(188, 255, 0)
Step 88: RGB(193, 255, 0)
Step 89: RGB(198, 255, 0)
Step 90: RGB(204, 255, 0)
Step 91: RGB(209, 255, 0)
Step 92: RGB(214, 255, 0)
Step 93: RGB(219, 255, 0)
Step 94: RGB(224, 255, 0)
Step 95: RGB(229, 255, 0)
Step 96: RGB(234, 255, 0)
Step 97: RGB(239, 255, 0)
Step 98: RGB(244, 255, 0)
Step 99: RGB(249, 255, 0)
Step 100: RGB(255, 255, 0)

Q50: Reasonable Time Algorithms (1D, Big O)

as a popcorn hack (coding challenge), scale list of size by factor of 10 and measure the times

as a 2nd hack, create a slow algorithm and measure its time, which are considered slow algorithms…

O(n^3) which is three nested loops

O(2^n) which is a recursive algorithm with two recursive calls

# as a popcorn hack (coding challenge), scale list of size by factor of 10 and measure the times

import time

# Function to scale a list by a factor of 10
def scale_list(lst, factor=10):
    scaled_list = lst * factor
    return scaled_list

# Create a sample list
sample_list = [1, 2, 3, 4, 5]

# Measure the time taken to scale the list
start_time = time.time()
scaled_list = scale_list(sample_list)
end_time = time.time()

print("Scaled List:", scaled_list)
print("Time taken to scale list:", end_time - start_time, "seconds")
Scaled List: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
Time taken to scale list: 4.315376281738281e-05 seconds
# as a 2nd hack, create a slow algorithm and measure its time, which are considered slow algorithms... 
#   O(n^3) which is three nested loops 
#   O(2^n) which is a recursive algorithm with two recursive calls

import time

# Slow algorithm with O(n^3) complexity
def slow_algorithm_n3(n):
    result = 0
    for i in range(n):
        for j in range(n):
            for k in range(n):
                result += 1
    return result

# Slow algorithm with O(2^n) complexity
def slow_algorithm_2n(n):
    if n <= 1:
        return n
    else:
        return slow_algorithm_2n(n-1) + slow_algorithm_2n(n-2)

# Measure time for O(n^3) algorithm
start_time = time.time()
result_n3 = slow_algorithm_n3(10)
end_time = time.time()
print("Result (O(n^3) algorithm):", result_n3)
print("Time taken (O(n^3) algorithm):", end_time - start_time, "seconds")

# Measure time for O(2^n) algorithm
start_time = time.time()
result_2n = slow_algorithm_2n(10)
end_time = time.time()
print("Result (O(2^n) algorithm):", result_2n)
print("Time taken (O(2^n) algorithm):", end_time - start_time, "seconds")
Result (O(n^3) algorithm): 1000
Time taken (O(n^3) algorithm): 0.0002448558807373047 seconds
Result (O(2^n) algorithm): 55
Time taken (O(2^n) algorithm): 0.00018644332885742188 seconds

Q56: Compare Execution Times of Tow Version (1D, analysis)

as a popcorn hack, measure the time to calulate fibonacci sequence at small and large numbers

import time

# Slow recursive approach for Fibonacci sequence
def fibonacci_recursive(n):
    if n <= 1:
        return n
    else:
        return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)

# Measure time for small input
start_time = time.time()
result_small = fibonacci_recursive(10)  # Small input
end_time = time.time()
print("Result (small input):", result_small)
print("Time taken (recursive approach, small input):", end_time - start_time, "seconds")

# Measure time for large input
start_time = time.time()
result_large = fibonacci_recursive(30)  # Large input
end_time = time.time()
print("Result (large input):", result_large)
print("Time taken (recursive approach, large input):", end_time - start_time, "seconds")
Result (small input): 55
Time taken (recursive approach, small input): 0.0002353191375732422 seconds
Result (large input): 832040
Time taken (recursive approach, large input): 0.4174165725708008 seconds

Q64: Error with Multiplication Using Repeated Addition (4C, algorithms and programs)

as a popcorn hack (coding challenge), fix the multiply function to work with negative numbers

def multiply(x, y):
    if y == 0:
        return 0

    result = 0
    sign = 1 if (x >= 0 and y >= 0) or (x < 0 and y < 0) else -1
    y = abs(y)

    while y > 0:
        result += x
        y -= 1

    return result if sign == 1 else -result

# Test cases
print(multiply(2, 5))   # Expected output: 10
print(multiply(2, -5))  # Expected output: -10
print(multiply(-2, 5))  # Expected output: 10
print(multiply(-2, -5)) # Expected output: -10
10
-10
10
-10

Q65: Call to Concat and Substring (4B, string operations)

as a popcorn hack (binary challenge), create string and concatenation options for A, B, C

## Option A

# Extract the substring "antelope" from position 5 with length 4
animal = "antelope"[4:8]

# Concatenate "jackalope" with "antelope"
animal += "jackalope"[0:4] + "a"

print(animal)  # Outputs: lopejacka
lopejacka
## Option B

# Extract the substring "antelope" from position 5 with length 4
animal = "antelope"[4:8]

# Concatenate "jackalope" with "antelope"
animal += "a" + "jackalope"[0:4]

print(animal)  # Outputs: lopeajack
lopeajack
## Option C

# Extract the substring "jack" from "jackrabbit"
animal = "jackrabbit"[0:4]

# Concatenate "jackalope" with "jack"
animal += "a"

print(animal)  # Outputs: jacka
jacka