MCD4710 Workbook July 22, 2020 Contents Preface 3 I Programming Fundamentals 4 1 Expressions 5 1.1 Numerical Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2 Boolean Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2 Control Flow 9 2.1 Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.1 While-Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.2 For-Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.3 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3 Sequences 20 3.1 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.2 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.3 Range . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4 Functions 24 5 Code Traces 31 6 Recursion 32 II Data Structures 36 7 Tables and Matrices 37 7.1 Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 7.2 Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 8 Graphs 45 8.1 Paths, cycles, and connectivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 8.2 Spanning Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 8.3 Graph Traversals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 9 Stacks and Queues 52 III Algorithm Analysis 53 10 Invariants 54 10.1 Finding Loop Invariants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 1 11 Computational Complexity 59 11.1 Big-Oh Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 11.2 Computational Complexity of Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 IV Additional Resources 67 12 Practice Exam Questions 68 12.1 Discussion of Theoretical Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 12.2 Computational Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 12.3 Invariants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 13 Solutions to Practice Exam Questions 72 13.1 Solutions: Discussion of Theoretical Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 13.2 Solutions: Computational Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 13.3 Solutions: Invariants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 2 Preface This workbook is an additional learning resource for FIT1045 and FIT1053. This is our first semester offering a workbook, so the workbook will grow as the semester progresses. This workbook is not sufficient for the unit. You must also complete lectures, workshops, and tutorials. The workbook will be useful in tutorials during time dedicated to self-directed-learning; as a tool for identifying any gaps in your knowledge; and for providing materials with which to practice. You can use the workbook as much or as little as is useful for you. The questions in this workbook reflect the questions you can expect to see on the in-semester tests, and the final exam. The difficulty of the questions can be understood like so: 1. Clarify questions allow you to identify any misunderstandings you may have with the fundamental tools you will learn. These are very simple questions. If you can do the practice questions, there is no need to do these questions. 2. Practice questions allow you to practice the kinds of questions you will see in your tests and exam. Some will be knowledge-based, some will be problem solving. If you can do all the practice questions, you will be fully prepared for the content of the tests and exam. Always start with the practice questions, and if you cannot do them switch to clarify; if they are too easy switch to challenge. 3. Challenge questions allow you to practice questions that are more difficult than most of the questions you will see in your tests and exam. It can be difficult to problem-solve under test conditions, so practicing on questions that are more difficult than what you might expect to see can be useful. Some questions on the final exam will be similar to challenge level questions. Solutions will not be provided for Challenege questions. 4. Extension questions are questions that contain non-assessable content. They may contain a taster of material that will be taught in later units, or they may show you something cool you can do with the tool you are being taught. If you are ever confused by a workbook question, feel free to ask about it in your tutorial, on Moodle, or at a consult. Enjoy! 3 Part I Programming Fundamentals 4 1 | Expressions 1.1 Numerical Expressions Clarify What do each of these numerical expressions yield? 1. 9+5 2. 7/2 3. 3**2 4. 10%3 5. 4/2 6. -9%2 7. -10%-4 8. 8//4 9. 14//5 10. 9.5//3 11. 6//3.0 12. 9.5%2 13. 8.0%3 14. 2**2**3 15. 4-3*2 16. 13/(5%3) Practice What do each of these numerical expressions yield? Make sure you understand when the expression yields a float and when it yields an integer. 1. 10/(5%2) 2. 4+11%6//2**2 3. 2**(9%3+2)+12//(5.5-7.5) 4. 1*2**(3%2+2)+14//5 5. 3*2**2**(5//7+1) 6. 3+2*3**(10//5)-7 7. 7*-4**4 8. -8%-6//3 9. 3*3/-4 10. -1//-5*-8 11. 5**1-4 12. -2-7%4 13. 1+0%7-9%1 14. -8*6//2%2+-1 15. 6*8*4/-6//-4 16. 8+4+2*-3/-3 17. -9+-1%-7--8%-4 18. 9/1-7//7%9 19. 4--8*-4-5%-9 20. -8/-7*-7//-5*-9 21. 0-5-4%6//-2 22. -6%8+2+-8//-2 23. -6/8//8%8/4 24. 7--4//-4**6*9 25. -5+-2**-3**-9**2 26. -4**4%-1**2//8 27. 7**-10//2**-10/6 28. -4+2**2**3--9 29. -1**7//4*-2**4 30. -5+4+-3//-1*2 31. -2%4**3-2**9 32. -4--2+8**-7*0 5 Test 1 Test 2 Exam Challenge What do each of these numerical expressions yield? 1. int(False or True)*2**2**(14//5*2%3+2)+2 Answers Write the expression in the Python shell and compare output. 6 1.2 Boolean Expressions When answering the following questions, keep in mind Python operator precedence given in the lectures, or found in the Python documentation.1 It can be helpful to think of operators with higher precedence as having brackets around them. For example, to think of False or not True and True as being False or ((not True) and True). Also keep in mind the behavior of the boolean operators given in the lectures, or found in the Python documen- tation.2 Finally, be aware of when type coercion occurs. Clarify What do each of these function calls return or boolean expressions yield?3 1. bool(3) 2. bool('') 3. bool([0]) 4. bool('cat') 5. not 'dog' 6. not [] What do each of these boolean expressions yield? Assume each expression is self-contained and identify which will result in a NameError. Boolean operators 1. True or False 2. True and False 3. False or False 4. not True or True 5. not (True or True) 6. not not True Booleans - with short-circuiting 1. x or True 2. True or x 3. False and x 4. False or x 5. True and False or x 6. False and True or True or x Comparison operators 1. 7 <= 3 2. 2 > 2 3. 1 != 0 4. 10 < 5 < 20 5. 3 > 2 < 20 6. 2 == 2 > -4 Comparisons - with booleans 1. True == 1 2. False < 3 3. False >= 0 4. True == 6 5. True == (not not 6)4 6. False == [] Practice What do each of these boolean expressions yield? Assume each expression is self-contained and identify which will result in a NameError. 1. '' or not 0 2. False or False and True or True or x 3. True or False or 2 4. 5 or True and False 5. 3>7 or 9>=15 and 4<5 or not 10==4 6. not [] and 2<2 and 'cat'or 7>4<12 1Operator precedence at url: https://docs.python.org/3/reference/expressions.html#operator-precedence 2Boolean Operator behavior at url: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not. 3Truth value testing at url: https://docs.python.org/3/library/stdtypes.html#truth 4Note that == has higher precedence than not, which means brackets must be used to avoid a SyntaxError. 7 Test 1 Test 2 Exam 7. 'word'or range(1,2) or -6>=8 or -3<=-3 8. -3<=-3 or ''and -4==-1 or -4==-1 9. [] and 'word'or range(1,2) and [''] 10. -6>=8 and 0 and -3<=-3 or 'word' 11. '' and -4==-1 or -5 or range(0) 12. 2<0 and None and None or 2<0 13. not(-4>-1 or not [''] or not 'w'or 'word') 14. '0' and 4 or range(0) or -5 15. 0 and 3==4 or range(0) or '' 16. None and ''and 2<0 and range(1,2) 17. range(3,1) or None or [] or 'word'or '' 18. None and range(1,2) or None or -5 and -4==-1 19. 2<0 or [''] and [] or range(5,3) and 'word' 20. not([''] and -5 or -4==-1) or ''and 'word' 21. -3<=-3 and ''or -5 and '0'or range(-3, -9) 22. '0' or -4==-1 or not ('word'or 2<0 and None) 23. '' or 7 and -4==-1 and -6>=8 or [] 24. 0 and not 'word'or 'string'or '0'and -4==-1 Answers Write the expression in the Python shell and compare output. 8 2 | Control Flow 2.1 Conditionals Clarify Code trace What is the value of x after the code sequence is executed? 1. x = 5 if x < 10: x = 'small' 2. x = True if x: x = 'Yay!' else: x = 'No....' 3. x = 'dog' if x == 'cat': x = 'meow' elif x == 'dog': x = 'woof' else: x = 'animal sound' 4. x = 6 if x > 10: x = 'big' elif x < 5: x = 'small' 5. x = 25 if x < 10: x = 'cold' elif x > 25: x = 'hot' elif x < 25: x = 'average ' else: x = 'perfect ' 6. x = -10 if x < 0: x = x * -1 7. a = 5 b = 8 x = 0 if a < b: x = b else: x = a 8. x = 25 if x%2 == 0: x = x // 2 if x%3 == 0: x = x // 3 if x%5 == 0: x = x // 5 Coding Write a control sequence that ... 1. sets x to the smaller of two numbers, a and b. 9 Test 1 Test 2 Exam 2. sets x to the largest of three numbers, a, b, and c. 3. sets parity to even or odd, based on the value of num. 4. sets opt to 'different', 'pair', or 'triple', based on the value of a, b, and c. 5. sets message to 'Wear {option}' based on the value of temp. The value option should be 'a jacket' if the temperature is cold, and 'sunscreen' if the temperature is hot. You may decide what temperature is hot, and what is cold. Practice Write a control sequence that ... 1. sets shape to 'triangle' or 'not triangle' given the lengths of three lines.1 2. sets type to 'equilateral', 'isosceles', or 'scalene', given the lengths of the three sides of a triangle, x, y, and z. 3. sets rounded to the decimal num rounded to the nearest integer. Follow the IEEE rounding rule ‘round to nearest, ties to even’ (i.e. if rounding x.5, round to the nearest even integer).2 4. sorts a list of three numbers.3 Answers Clarify Code trace 1. x == 'small' 2. x == 'Yay!' 3. x == 'woof' 4. x == 6 5. x == 'perfect' 6. x == 10 7. x == 8 8. x == 5 Coding 1. a = ? b = ? x = a if b < a: x = b 2. a = ? b = ? c = ? x = a if x < b: x = b if x < c: x = c 3. num = ? parity = None if num%2 == 0: parity = 'even' else: parity = 'odd' 1For three lines to make a triangle, every line must be shorter than the sum of the remaining two. 2For more information on the IEEE rounding rules, see here: https://en.wikipedia.org/wiki/IEEE_754 3Attempt after completing section Lists. 10 4. a = ? b = ? c = ? opt = 'different ' if a == b or b == c or a == c: opt = 'pair' if a == b and b == c: opt = 'triple ' 5. #15 or lower is cold; above 15 is hot temp = ? message = 'Wear ' if temp <= 15: message = message + 'a jacket ' else: message = message + 'sunscreen ' Practice Note that there are many potential implementations. The answers given here are just an example of a possible implementation. 2. x = ? y = ? z = ? type = None if x == y and y == z: type = 'equilateral ' elif x==y or y==z or x==z: type = 'isosceles ' else: type = 'scalene ' 4. lst = [?, ?, ?] a = lst [0] b = lst [1] c = lst [2] #correct position of a if a > b or a > c: if a > b and a > c: lst [2] = a else: lst [1] = a #correct position of b if b < a and b < c: lst [0] = b elif: if b > a and b > c: lst [2] = b #correct position of c if c < a or c < b: if c < a and c < b: lst [0] = c else: lst [1] = c 11 2.2 Loops 2.2.1 While-Loops All questions are Clarify. For Practice and higher, go to the Loops subsection. Fill in the blanks Fill in the blanks, _, so that each loop creates a list of the numbers 1 to 10. 1. x = _ lst = [] while x <= 10: lst = lst + [x] x = x + 1 2. x = 1 lst = [] while True: lst = lst + [_] x = x _ _ if x == _ : _ 3. x = 2 lst = [] while x < _: lst = lst + [x//2] x = x + _ 4. x = _ lst = [] while True: if x > _: break x = x _ _ lst = lst + [x] Coding Write a while-loop that ... 1. ... adds all the numbers from 1 to 100 (inclusive) and stores the total in the variable x. 2. ... adds all even numbers from 1 to 50 (inclusive) and stores the total in the variable x. 3. ... creates the list lst = [1,2,3,4,5,6,7,8,9]. 4. ... creates the list lst = [10, 8, 6, 4, 2, 0]. 5. ... creates the list lst = [1,1,1,1,1,1,1,1,1]. Use the length of the list in the while condition. 6. ... creates the list lst = [1,1,1,1,1,1,1,1,1]. Use a counter. 7. ... creates the list lst = [1,2,3,4,5,6,7,8,9]. Use a break statement. 8. ... creates the list lst = [2,2,2,2,2,2,2,2,2]. Use a break statement. 2.2.2 For-Loops All questions are Clarify. For Practice and higher, go to the Loops subsection. This section should be done after you have completed the Range subsection. Fix the problem Identify the problem with the given loop and implement a corrected version. 1. #should add the numbers 1 to 10 total = 0 for num in range(2, 10): total = total + num 12 Test 1 Test 2 Exam 2. #should create the string n='12345' n = '' lst = [1,2,3,4,5] for i in range(len(lst )): n = n + str(i) 3. #should double each number in lst lst = [1,2,3,4,5] for n in lst: n = n*2 4. #should replace all 'o's with 'a's b = 'bonono ' for i in range (1,6,2): b[i] = 'a' Coding Write a for-loop that ... 1. ... adds all the numbers from 1 to 100 (inclusive) and stores the total in the variable x. 2. ... adds all even numbers from 1 to 50 (inclusive) and stores the total in the variable x. 3. ... triples all the numbers in the given list lst. 4. ... creates a list containing ten 5s. 5. ... creates a list of all the even numbers that are greater than 1 and less than 100. 6. ... takes a string word and creates a new string identical word but with all 'a's removed. 2.2.3 Loops Clarify Choose What kind of loop would you use for the following scenarios? 1. Halving a number until it is smaller than 100. 2. Doubling every number in a list. 3. Summing an infinite series to estimate an irrational number. 4. Summing every digit in a string. 5. Count the frequency of a character in a string. 6. Asks for input from the user, and keeps asking until input is valid. Coding Write a while-loop with the same behaviour as the given for-loop. 1. total = 0 for num in range(55, 105, 5): total = total + num 2. x = 100 for num in range(-1, -10, -1): x = x + num 3. lst = [1, 2, 3, 4, 5, 6] total = 0 for num in lst: total = total + num 4. lst = ['a', 'b', 'a', 'a', 'b', 'a'] for i in range(len(lst )): if lst[i] == 'b': lst[i] = 'aa' 13 Write a for-loop with the same behaviour as the given while-loop: 1. total = 0 num = 20 while num < 42: total = total + num num = num + 2 2. total = 0 num = 25 while num <= 100: total = total + num num = num + 25 3. lst = [2, 4, 3, 5, 2, 2, 3] i = 0 while i < len(lst): lst[i] = lst[i] // 2 i = i + 1 4. lst = ['P', 'y', 't', 'h', 'o', 'n'] word = '' i = 0 while i < len(lst): word = word + lst[i] i = i + 1 Practice Write loops with the following behaviours: 1. Generates a list containing the first 64 powers of 2. 2. Generates a list containing all the powers of 2 that are less than 1,000,000. 3. Generates a list containing the first 100 numbers of the Fibonacci sequence. I.e. [0,1,1,2,3,...].4 4. Determines whether a given list of numbers is in ascending order. 5. Finds the factorial of the given number.5 6. Finds the greatest common divisor of two given numbers. 7. Finds the lowest common multiple of two given numbers. 8. Finds the sum of all numbers in a given string. Assume numbers are positive integers. For example, '4 and 20 blackbirds' sums to 24. 9. Finds the sum of all digits in a given number. 10. Finds the mean of a list of numbers. 11. Generates the reverse of a given string. Do not use string slicing or the function reversed(). 12. Determines whether a given string is a palindrome. Do not use string slicing or the function reversed(). 13. Generates a list of all factors of a given number. 14. Generates the complement of a given binary number. For example, the complement of '110101' is '001010'. Challenge Write loops with the following behaviours: 1. Generates a list containing all the perfect numbers between 1 and 1000. I.e. it should return this list: [6, 28, 496]. A perfect number is a positive integer that is equal to the sum of its positive divisors, excluding the number itself. For example, the divisors of 6 are 1, 2, 3, and 1 + 2 + 3 = 6.6 2. Generates a list of the first 100 primes. 4Read about Fibonacci numbers here: https://en.wikipedia.org/wiki/Fibonacci_number 5Read about factorials here: https://en.wikipedia.org/wiki/Factorial 6Read about perfect numbers here: https://en.wikipedia.org/wiki/Perfect_number 14 3. Checks whether the given number num is a happy number. A happy number is a number where repeatedly calculating the sum of the squares of the number’s digits leads to 1. For example, if num=28 then when the loop terminates is_happy = True; if num=22 then when the loop terminates is_happy = False.7 4. Generates a list containing all permutations of a given string. For example, 'cat' would generate ['cat', 'cta', 'act', 'atc', 'tca', 'tac']. 7Let s0 be our number. Let the sum of the squares of the digits of s0 be s1. Let the sum of the squares of the digits of s1 be s2. Let the sum of the squares of the digits of sn be sn+1. A number x0 is happy if there exists an n whereby xn = 1. For instance, 28 is happy as: 22 + 82 =68 62 + 82 =100 12 + 02 + 02 =1 Whereas 22 is unhappy as: 22 + 22 =8 82 =16 ... and 16 is a known unhappy number. The following numbers are known unhappy numbers: 0, 4, 16, 20, 37, 42, 58, 89, or 145. Read more about happy numbers here: https://mathworld.wolfram.com/HappyNumber.html. Note that the description given here is a limited definition of happy numbers and not accurate if you are a number theory mathematician. 15 Answers While-loops Fill in the blanks 1. x = 1 lst = [] while x <= 10: lst = lst + [x] x = x + 1 2. x = 1 lst = [] while True: lst = lst + [x] x = x + 1 if x == 10: break 3. x = 2 lst = [] while x < 21: lst = lst + [x//2] x = x + 2 4. x = 0 lst = [] while True: if x > 9: break x = x + 1 lst = lst + [x] Coding 2. x = 0 num = 1 while num <= 50: if num%2 == 0: x = x + num num = num + 1 4. lst = [] num = 10 while num >= 0: lst = lst + [num] num = num - 2 6. lst = [] counter = 0 while counter < 9: lst = lst + [1] counter = counter + 1 8. lst = [] while True: if len(lst) == 9: break lst = lst + [2] For-loops Fix the problem 1. #should add the numbers 1 to 10 total = 0 for num in range(1, 11): total = total + num 2. #should create the string n='12345' n = '' lst = [1,2,3,4,5] for i in range(len(lst )): n = n + str(lst[i]) #alternative implementation n = '' lst = [1,2,3,4,5] for num in lst: n = n + str(num) 16 3. #should double each number in lst lst = [1,2,3,4,5] for i in range(len(lst )): lst[i] = lst[i]*2 4. #should replace all 'o's with 'a's b = 'bonono ' ban = '' for char in b: if char == 'o': ban = ban + 'a' else: ban = ban + char Coding 1. x = 0 for num in range(1, 101): x = x + num 2. x = 0 for num in range(0, 51, 2): x = x + num 3. lst = ?? for i in range(len(lst )): lst[i] = lst[i]*3 4. lst = [] for _ in range (10): lst = lst + [5] 5. lst = [] for num in range(2, 100, 2): lst = lst + [num] 6. word = ?? new = '' for char in word: if word != 'a': new = new + char Loops Clarify 1. As there is a condition to be met, should use a while-loop. 2. This can be done with a while-loop or for-loop. A for-loop will likely be simpler to implement. 3. Usually when estimating an irrational number, the intention is to get within a certain level of error. This means there is a condition to be met, so should use a while-loop. 4. This can be done with a while-loop or for-loop. A for-loop will likely be simpler to implement. 5. This can be done with a while-loop or for-loop. A for-loop will likely be simpler to implement. 6. As there is a condition to be met, should use a while-loop. For-loops to while-loops 1. total = 0 num = 55 while num <= 100: total = total + num num = num + 5 2. x = 100 num = -1 while num > -10: x = x + num num = num -1 17 3. lst = [1, 2, 3, 4, 5, 6] total = 0 i = 0 while i < len(lst): total = total + lst[i] i = i + 1 4. lst = ['a', 'b', 'a', 'a', 'b', 'a'] i = 0 while i < len(lst): if lst[i] == 'b': lst[i] = 'aa' i = i + 1 While-loops to for-loops 1. total = 0 for num in range(20, 42, 2): total = total + num 2. total = 0 for num in range(25, 125, 25): total = total + num 3. lst = [2, 4, 3, 5, 2, 2, 3] for i in range(len(lst )): lst[i] = lst[i] // 2 4. lst = ['P', 'y', 't', 'h', 'o', 'n'] word = '' for letter in lst: word = word + letter Practice Note that there are many potential implementations. The answers given here are just an example of a possible implementation. 2. lst = [] index = 0 while 2** index < 1000000: lst = lst + [2** index] index = index + 1 4. lst = ?? in_order = True for i in range(len(lst)-1): if lst[i] > lst[i+1]: in_order = False 6. big_num = ?? small_num = ?? while small_num != 0: remainder = big_num % small_num big_num = small_num small_num = remainder gcd = big_num 8. string = ?? numbers = \ ['0','1','2','3','4', '5','6','7','8','9'] i = 0 sum = 0 while i < len(string ): if string[i] in numbers: j = i while j < len(string) and \ string[j] in numbers: j = j+1 num = string[i:j] sum = sum + int(num) i = j i = i+1 10. sum = 0 for num in lst: sum = sum + num mean = sum/len(lst) 12. string = ?? palindrome = True for i in range(len(string )//2): if string[i] != string[-(i+1)]: palindrome = False 18 14. binary = ?? complement = '' for digit in binary: if digit == '0': complement = complement + '1' else: complement = complement + '0' 19 3 | Sequences Summary of sequence operations: Operator/Function Result Type x in seq whether x is contained in seq Boolean x not in seq whether x is not contained in seq Boolean len(seq) number of elements contained in seq integer seq[i] the ith element of seq varies seq[a:b] the sub-sequence of seq from the ath element (inclusive) to the bth element (exclusive) same as seq 3.1 Strings Summary of useful string methods:1 Operation Description str.count(sub) Returns the number of times substring sub is in string str str.find(sub) Returns the lowest index sub is in str, or −1 if not in str str.format(*args) Formats the string str str.index(sub) Returns the lowest index sub is in str, throws error if not in str str.join(iter) Returns a string which is the concatenation of the strings in iter. Strings in iter are joined by str. str.lower() Returns a copy of str with all cased characters in lower case str.split(sep) Returns a list of the words in str, using sep as the delimiter string str.strip() Removes all white space from beginning and end of str str.upper() Returns a copy of str with all cased characters in upper case Clarify Basic Operators What do each of these expressions yield? Ensure you include all information. 1. 'I have '+ str(2) + 'cats.' 2. len('animals') 3. 'ing' in 'Coding is fun!' 4. 'Coding is fun!'[0] 5. 'Coding is fun!'[10:13] 6. len('Python') 7. 'FIT1045'[:3] 8. 'Hello, world.'[3:5] + 'magic'[-3:len('magic')] 1For all string methods, see here: https://docs.python.org/3.8/library/stdtypes.html#string-methods 20 Test 1 Test 2 Exam String Methods What do each of these expressions yield? Ensure you include all information. 1. 'antelopes, ants, and alpacas are all animals'.count('an') 2. 'Python'.find('y') 3. 'Hello'.find('y') 4. ' and '.join(['lions','tigers','bears']) 5. 'Learn Python in FIT1045'.lower() 6. 'Do you know methods?'.split() 7. '1+3+5=2'.split('+') 8. ' Clean up... '.strip() Practice Evaluate What do each of these expressions yield? Ensure you include all information. Which expressions throw errors or do not behave as they ought, and how can they be fixed? 1. 'One wonders on a pond'.lower().count('on') 2. len('FIT1045,FIT1008,FIT2004,FIT3155'.split(',')[2]) 3. 'and'.join('7 + 9 + 4'.split('+')) 4. 'oar'.upper().join('fat rat sat with bat and goat at math'.split('at')[2:5]) 5. '{} {}s'.format('Emit'.lower()[::-1], 'Drawer'.lower()[::-1]) 6. 'na'*8 + 'batman'.upper() 7. 'This question is number {}'.format('5 + 2'.split()[0] + '5 + 2'.split()[-1]) 8. 'There are '+ 3+7 + 'movies with Ironman.' Code In the following questions the only strings you can use are those you are given. Write an expression that ... 1. ... evaluates to 'python' given the string string = 'Students in FIT1045 code in Python.' 2. ... evaluates to 'saidallinonebreath' given the string string = 'Said all in one breath!' 3. ... replaces all es in a string with os, given e='e' and o='o'. Assume the string is string. 4. ... evaluates to the given string, but with the words in reverse order. Assume the string is string. E.g. 'This is weird.' becomes 'weird. is This' Answers Write the expression in the Python shell and compare output. 21 3.2 Lists Summary of useful string methods:2 Operation Description lst.append(x) Appends item x to list lst lst.extend(iter) Extends list lst by adding all items from iterable iter lst.count(x) Returns the number of times item x is in list lst lst.insert(k,x) Inserts item x at index k in list lst lst.pop() Returns the last item in list lst and removes it from the list lst.remove(x) Removes the first occurrence of item x in list lst lst.reverse() Reverses all the items in list lst Clarify Basic Operators What do each of these expressions yield? Ensure you include all information. 1. `I have '+ 2 + ` cats' List Methods What do each of these expressions yield? Ensure you include all information. Practice Evaluate What do each of these expressions yield? Ensure you include all information. Which expressions throw errors or do not behave as they ought, and how can they be fixed? 1. `I have '+ 2 + ` cats' Code Answers Write the expression in the Python shell and compare output. 2For all string methods, see here: https://docs.python.org/3.8/library/stdtypes.html#string-methods 22 Test 1 Test 2 Exam 3.3 Range Clarify Evaluate What sequence of numbers is equivalent to the given ranges? 1. range(6) 2. range(2,5) 3. range(4,10) 4. range(-3,5) 5. range(4,2) 6. range(2,8,2) 7. range(1,10,2) 8. range(25,5,-3) 9. range(-2,-20,-4) 10. range(10,-10,-5) 11. range(-14,-2,-3) 12. range(-4,15,6) Coding Express each of the following as a range. 1. [11,12,13,14,15] 2. [9,8,7,6,5,4] 3. [-2,-4,-6,-8,-10] 4. [-5,-4,-3,-2,-1,0,1,2,3,4,5] 5. [0,1,2,3,4,5,6,7,8,9] 6. [0,10,20,30,40,50,60,70,80] 7. [0,-4,-8,-12,-16,-20,-24] 8. -15,-10,-5,0,5,10,15,20 Practice What do each of these expressions yield? 1. len(range(2,8)) 2. 3 in range(-2,5) 3. range(-7,-2)[4] 4. range(4,20)[2:5] 5. range(12,18)[-2] 6. len(range(25,-5,-4)) 7. list(range(-4,-18,-3)[2:8]) 8. list(range(3,9)[-2:-5:-1]) 9. list(range(10,20)[-5:7]) 10. list(range(5,20,3)[-4:4]) 11. len(range(17,-5,-6)) 12. 18 in range(10,20,-2) 13. list(range(0,75,5)[2:-1:3]) 14. list(range(10,60,4)[-2:1:-4] 15. range(3,12,4)[2] 16. len(range(2,-7,5)) Challenge What do each of these expressions yield? 1. range(0,9,2)[:] 2. range(-2,20,5)[1:4] 3. range(3,18,2)[::2] 4. range(1,33,6)[1::2] 5. range(2,20)[-2:-10:-1] 6. range(-4,15,4)[-1:-5:-1] 7. range(-5,20,3)[-8:5:2] 8. range(100,50,-7)[1:-3:3] Answers Write the expression in the Python shell and compare output. 23 Test 1 Test 2 Exam 4 | Functions Clarify Fill in the blanks 1. #should return the sum of two numbers def sum(x, y): _ x + y 2. #should return the given string concatenated with itself _ double_string(_): _ my_string + my_string 3. #should return the sound the animal makes _ animal_sound(_): if animal == 'cat': _ 'meow' _ 'unknown animal ' 4. #should return whether the number is even _ is_even(_): _ num%2 == 0 Exiting Early Rewrite the following functions to use multiple return statements, and fewer elif and else statements. 1. def factors(num): statement = '' if num%2 == 0: statement = '2 is the smallest non -1 factor ' elif num%3 == 0: statement = '3 is the smallest non -1 factor ' else: statement = 'neither 2 nor 3 are factors ' return statement 2. def contains_even(lst): contains = False for num in lst: if num%2 == 0: contains = True return contains 24 Test 1 Test 2 Exam 3. def scissors_paper_rock(player1 , player2 ): result = None if player1 == player2: result = 'Noone wins' elif (player1 == 'scissors ' and player2 == 'paper' or player1 == 'paper' and player2 == 'rock' or player1 == 'rock' and player2 == 'scissors '): result = 'Player1 wins' else: result = 'Player2 wins' return result 4. def find_mode(x, y, z): mode = None if x==y or x==z: mode = x elif y==z: mode = y else: mode = (x, y, z) return mode Print vs. Return Identify which of the following code blocks will throw errors. 1. def sum(x, y): print(x + y) result = sum(2, 8) double = result *2 2. def letter_in_word(word , letter ): in_word = letter in word return in_word result = letter_in_word('apple', 'p') print('Letter p in word apple: ' + str(result )) 3. def divide(x, y): print(x/y) result = divide (6,3) print('6/3 is shown above.') 4. def double_string(string ): print(string *2) my_string = 'phone' result = double_string(my_string) print(my_string + ' doubled is ' + result) 25 Variable arguments Identify which of the following code blocks will throw errors. 1. def sum(*nums): total = 0 for n in nums: total = total + n return total sum(3, 7, 2) 2. def multiply(start_val , *nums): for n in nums: start_val = start_val * n return start_val multiply (10, 2, 2, 3) 3. def divide(numer , denom , *nums): total = numer/denom for n in nums: total = total / n return total divide (5) 4. def subtract (*nums): total = 0 for n in nums: total = total - n return total subtract () Sequence Unpacking Identify which of the following code blocks will throw errors. 1. def find_range(lst): minimum = min(lst) maximum = max(lst) return minimum , maximum my_min , my_max = find_range ([2, 2, 7, 1, 3, 9, 3]) 2. def move_point_up(x, y, z): return x+1, y, z x, y, z = move_point_up (2, 3, 1) 3. def move_point_right(x, y, z): return x, y+1, z new_point = move_point_right (2, 3, 1) 26 4. def move_point_down(x, y, z): return x-1, y, z x, y = move_point_down (2, 3, 1) 5. def move_point_left(x, y): return x, y-1 x, y, z = move_point_left (2, 3) 6. def move_point_left(x, y): return x, y-1 x, y, z = move_point_left (2, 3) Coding Write a function that ... 1. ... takes integers x and y as input, where x
2. ... takes integers x and y as input, and returns the minimum integer (if they are the same size, than both are the minimum). 3. ... takes as input a list lst containing only 0s and 1s, and returns True if there are more 1s than 0s, else returns False. 4. ... takes as input a list lst containing only numbers, and returns the list with all numbers inside it doubled. 5. ... takes as input a list lst containing exactly five numbers, and returns True if there are three or more identical numbers in a row, else returns False. 6. ... takes as input a string word, and returns a new string identical to word but with all vowels removed (a,e,i,o,u). Practice Write a function that ... 1. ... takes as input three integers, date, month, year, and returns True if the three integers make a valid date, else returns False. 2. ... takes as input a list numbers containing exactly five numbers, and returns an integer that is the maximum number of identical numbers in a row. 3. ... takes as input a list numbers containing only numbers, and an integer n, and returns the sum of every nth number in numbers. Keep an eye on this section. It will likely grow over the semester. 27 Answers Clarify Fill in the blanks 1. #should return the sum of two numbers def sum(x, y): return x + y 2. #should return the given string concatenated with itself def double_string(my_string ): return my_string + my_string 3. #should return the sound the animal makes def animal_sound(animal ): if animal == 'cat': return 'meow' return 'unknown animal ' 4. #should return whether the number is even def is_even(num): return num%2 == 0 Exiting Early These are suggested alternative implementations. Note that the following implementations are not necessarily better than the alternatives. 1. def factors(num): if num%2 == 0: return '2 is the smallest non -1 factor ' if num%3 == 0: return '3 is the smallest non -1 factor ' return 'neither 2 nor 3 are factors ' 2. def contains_even(lst): for num in lst: if num%2 == 0: return True return False 3. def scissors_paper_rock(player1 , player2 ): if player1 == player2: return 'Noone wins' if (player1 == 'scissors ' and player2 == 'paper' or player1 == 'paper' and player2 == 'rock' or player1 == 'rock' and player2 == 'scissors '): return 'Player1 wins' return 'Player2 wins' 28 4. def find_mode(x, y, z): if x==y or x==z: return x if y==z: return y return (x, y, z) Print vs. Return 1. Code block throws an error, as the function call sum(2,8) returns None, and the assignment double = None*2 throws an error. 2. Code block does not throw an error. The function call letter_in_word('apple', 'p') returns True, and the print statement print('Letter p in word apple: '+ str(True)) succeeds in printing. 3. Code block does not throw an error. The function call divide(6,3) prints 2.0 and returns None, and the print statement print('6/3 is shown above.') succeeds in printing as it makes no reference to the function call. 4. Code block throws an error, as the function call double_string('phone') returns None, and the print statement print('phone'+ 'doubled is '+ None) throws an error. Variable arguments 1. Code block does not throw an error, as the function definition sum(*nums) allows *nums to take any number of arguments, thus sum(3, 7, 2) is a valid function call. 2. Code block does not throw an error, as the function definition multiply(start_val, *nums) requires at least one argument for start_val, and any number of arguments for *nums, thus multiply(10, 2, 2, 3) is a valid function call. 3. Code block throws an error, as the function definition divide(numer, denom, *nums) requires at least two arguments, one for numer and one for denom, and any number of arguments for *nums, thus divide(5) is an invalid function call as it has insufficient arguments. 4. Code block does not throw an error, as the function definition subtract(*nums) allows *nums to take any number of arguments (including zero arguments), thus subtract() is a valid function call. Sequence Unpacking 1. Code block does not throw an error, as the function returns two values, which are unpacked and assigned to two variables. 2. Code block does not throw an error, as the function returns three values, which are unpacked and assigned to three variables. 3. Code block does not throw an error, as the function returns three values as a tuple, and the tuple is assigned to one variable. 4. Code block throws an error, as the function returns three values, thus they cannot be unpacked into two variables. 5. Code block throws an error, as the function returns two values, thus they cannot be unpacked into three variables. 6. Code block throws an error, as the function returns two values, thus they cannot be unpacked into three variables. 29 Coding 1. def sum_range(x, y): return sum(range(x,y+1)) 2. def minimum(x, y): if xreturn x return y 3. def triples(lst): ones = lst.count (1) zeroes = lst.count (0) return ones > zeroes 4. def double_values(lst): for i in range(len(lst )): lst[i] = lst[i]*2 return lst 5. def find_triple(lst): return (lst[0] == lst [1] and lst [1] == lst[2] or lst [1] == lst[2] and lst [2] == lst [3] or lst [2] == lst[3] and lst [3] == lst [4] ) 6. def remove_vowels(word): vowels = 'aeiou' new_word = '' for char in word: if char not in vowels: new_word = new_word + char return new_word Practice Ask for assistance in tutorials and consultations. 30 5 | Code Traces To be completed. 31 6 | Recursion Clarify 1. The following recursive function find_min returns the minimum of a list. Fill in the base case def find_min(lst): #base case return min(lst[0], find_min(lst [1:])) For example: >>> find_min ([5, 7, 8 , -5, 10]) -5 2. The following recursive function first_upper returns the first upper case letter of a string. It returns an empty string if it cannot find any. Fill in the base case. def first_upper(string ): #base case else: return first_upper(string [1:]) For example: >>> first_upper('abc Xyz) X 3. The following recursive function count_repeat counts how many times a target element appears in a list. Fill in the recursive case. def count_repeat(lst ,target ): if len(lst) == 0: return 0 #recursive case For example: >>> count_repeat ([15, 1, 10, 9, 10], 10) 2 4. Write a function which returns factorial of a given number n. For example: >>> factorial (5) 120 32 Test 2 Exam Practice 1. Write a recursive function digit_sum that sums the digits of an integer number, without converting the number to string. For example: >>> digit_sum (523) 10 2. Write a recursive function remove_neg to remove all negative numbers from a list. For example: >>> remove_neg ([2, -5, 3, -7, 10]) [2, 3, 10] 3. Write a recursive function which returns the value of the Fibonacci series for the nth element. For example: >>> fib(6) 8 >>> fib (10) 55 Challenge 1. Write a recursive function replace_between(string, deli, ch) to replace all characters bounded by a delimiter deli (special word) in a string with a character ch. Note that the string contains zero or at most one special word. For example: >>> replace_between('zzz$1234$xx ', '$', '0') 'zzz$0000$xx ' >>> replace_between('aaa$bbb ', '$', '0') 'aaa$bbb ' Hints: – try to reduce string by one or two characters (if possible) in each recursion. – think about the smallest string you might have and what should be returned with such string. – you might have more than one base case and recursive case. 2. Write a function that returns all the permutations of a given string. For example: >>> permute('abc') ['abc', 'acb', 'bac', 'bca', 'cab', 'cba'] Hints: – the permutations of a single character is itself. – the permutations of ’abc’ is given by permute('abc') == ['a' + permute('bc'), 'b' + permute('ac'), 'c' + permute('ab')] Answers Clarify 1. def find_min(lst): if len(lst) == 1: return lst[0] return min(lst[0], find_min(lst [1:])) 33 2. def first_upper(string ): if string =='': return '' elif string [0] != ' ' and string [0] == string [0]. upper (): return string [0] else: return first_upper(string [1:]) 3. def count_repeat(lst ,target ): if len(lst) == 0: return 0 if lst[0] == target: return 1 + count_repeat(lst[1:], target) else: return count_repeat(lst[1:], target) 4. def factorial(n): if n == 1 or n == 0: return 1 else: return n * factorial(n-1) Practice 1. def digit_sum(number ): if number == 0: return 0 return number %10 + digit_sum(number //10) 2. def remove_neg(lst): if len(lst) == 0: return [] if lst [0] >=0: return [lst [0]] + remove_neg(lst [1:]) else: return remove_neg(lst [1:]) 3. def fib(n): if n == 0: return 0 elif n == 1: return 1 return fib(n-1) + fib(n-2) 34 Challenge 1. def replace_between(string , ch1 , ch2): if len(string) <= 2: return string if string [0] == string [-1] == ch1: return ch1 + ch2*(len(string )-2) + ch1 if string [0] == ch1: return replace_special_word(string [:-1], ch1 , ch2) + string [-1] if string [-1] == ch1: return string [0] + replace_special_word(string [1:], ch1 , ch2) return string [0] + replace_special_word(string [1:-1], ch1 , ch2) + string [-1] 2. def permute(s): newlst = [] if len(s) == 1: return s else: for i,j in enumerate(s): for perm in permute(s[:i] + s[i+1:]): newlst += [j + perm] return newlst 35 Part II Data Structures 36 7 | Tables and Matrices 7.1 Tables Clarify Code trace What is the value of x after the code sequence is executed? 1. table = [] for _ in range (2): row = [None , None] table.append(row) x = table 2. table = [] row = [1, 2, 3] for _ in range (3): table.append(row) table [2][2] = 5 x = table [0] 3. table = [] row = [None ]*3 for _ in range (4): table.append(row) for i in range(len(table )): table[i][0] = i+1 table[i][1] = i+2 table[i][2] = i+3 x = table [0] 4. table = [] for _ in range (3): row = [1, 2] table.append(row) table [1][0] = 3 x = table [0] 5. table = [] for _ in range (3): row = [None ]*3 table.append(row) for i in range(len(table )): table[i][0] = i+1 table[i][1] = i+2 table[i][2] = i+3 x = table [0] 6. table = [] for i in range (4): table.append ([]) for j in range (2): table[i]. append(j) table [2][1] = 3 x = table Accessing elements Assuming table is an n×m table, how can the following items be accessed? 1. The element in the first row and first column. 2. The (entire) second row. 3. The element in the final row and first column. 4. The element in the ith row and second column. 5. The third, fourth, and fifth element from the nineth row. 37 Test 1 Test 2 Exam 6. The second-from-the-bottom element in the fourth column. Coding Write a function that returns a table that ... 1. ... is n× n, and all elements are None. 2. ... is 3× 3, and each row contains the letters 'a', 'b', and 'c' in alphabetical order. 3. ... is 6× 3, and each row is a different permutation of three given items. 4. ... is 4× 4, and contains the numbers 1 to 16. 5. ... is n× n, and contains the numbers 1 to n2. Practice Code tables Write a function that returns a table that ... 1. ... is an n× n multiplication table. E.g. if n=3, the function returns [[1,2,3],[2,4,6],[3,6,9]]. 2. ... concatenates each entry in two given sequences. E.g. if the given sequences are 'abc' and '12', the function returns [['a1','a2'], ['b1','b2'], ['c1','c2']]. 3. ... is a 2 × 2 truth table. The function should take a string version of a boolean operator as input. E.g. if operator = 'and', the function returns [[True, False],[False, False]]. Access and alter tables Unless otherwise stated, each of the following functions takes as input an n×m table of numbers. 1. Implement a function that returns the mean of the row with the largest mean. 2. Implement a function that returns the mean of the column with the smallest mean. 3. Implement a function that returns True if at least one row contains all the same values, else returns False. 4. Implement a function that returns True if at least one column contains all the same values, else returns False. 5. Implement a function that takes as input an n × n table of numbers, and returns True if both diagonals contains all the same values, else returns False. 6. Implement a function that returns the mode of all values in the table. 7. Implement a function that replaces the highest and lowest value in each column with the median of that column. 8. Implement a function that replaces the highest and lowest value in each row with the mean of the row. 9. Implement a function that appends the sum of the row to the end of each row. Challenge Tic-Tac-Toe Tic-tac-toe, aka noughts and crosses, is a simple two-person game where both players, X and O, take turns placing their mark, with the aim of placing three of their marks in a horizontal, vertical, or diagonal row. Implement a function, ttt_winning_move(board), that takes an n × n board, represented as an n × n table, where each row contains n items from: 'x', 'o', and None. The function should return a list of players with an immediate winning move, where the aim is to place n marks in a horizonal, vertical, or diagonal row. The function should return an empty list [] if no one has the winning move. 38 Knights Chess is a two-person strategy board game where both players, black and white, take turns moving pieces, with the aim of placing the other player’s king in check. One of the pieces in chess is the knight. The knight moves in an L-shape: either two squares vertically and one square horizontally, or two squares horizontally and one square vertically. Implement a function, move_knight(board, start, move), that takes an 8× 8 board, represented as an 8× 8 table, where each row contains 8 items from: 'K', and '_'; a tuple start that contains the rank and file (row and column) of the current position of the knight; and a tuple move that contains the rank and file where the player wishes to move the knight. If the move is legal, the function should return the board with the knight moved to its new position; else the function should return the original board. 39 Answers Clarify Code trace 1. x == [[None, None], [None, None]] 2. x == [1, 2, 5] 3. x == [4, 5, 6] 4. x == [1, 2] 5. x == [1, 2, 3] 6. x == [[0,1], [0,1], [0,3], [0,1]] Accessing elements 1. table[0][0] 2. table[1] 3. table[-1][0] 4. table[i][1] 5. table[8][2:5] 6. table[2][3] Coding 1. def n_table(n): table = [] for _ in range(n): table.append ([None]*n) return table 2. def abc_table (): table = [] for _ in range (3): table.append (['a', 'b', 'c']) return table 3. def perm_table(x, y, z): table = [] table.append ([x,y,z]) table.append ([x,z,y]) table.append ([y,x,z]) table.append ([y,z,x]) table.append ([z,x,y]) table.append ([z,y,x]) return table 4. def sixteen_table (): table = [] for i in range (4): row = [] for j in range (4): row = row + [i*4 + j + 1] table.append(row) return table 40 5. def n_squared_table(n): table = [] for i in range(n): row = [] for j in range(n): row = row + [i*n + j + 1] table.append(row) return table Practice Ask for assistance in tutorials and consultations. 41 7.2 Matrices Practice Solve the following systems of linear equations. 2x2 Linear Systems 1. Easy Example ( 2 1 6 4 )( x1 x2 ) = ( 7 22 ) 2. Easy Example (−1 2 4 4 )( x1 x2 ) = ( 7 −4 ) 3x3 Linear Systems 1. Standard Example 4 1 0−4 −2 2 8 1 3 x1x2 x3 = 6−6 15 2. Standard Example 3 −2 59 −2 16 3 −10 1 x1x2 x3 = −10 −5 3. Pivot Required 2 −3 1−4 6 2 6 −8 4 x1x2 x3 = 2−4 8 42 Exam Answers Practice 2x2 Linear Systems 1. Easy Example ( 2 1 6 4 )( x1 x2 ) = ( 7 22 ) Perform row operation: R2← R2− 3×R1. This gives:( 2 1 0 1 )( x1 x2 ) = ( 7 1 ) Back substitution gives x2 = 1, 2x1 + 1 = 7 =⇒ x1 = 3 2. Easy Example (−1 2 4 4 )( x1 x2 ) = ( 7 −4 ) Perform row operation: R2← R2 + 4×R1. This gives:(−1 2 0 12 )( x1 x2 ) = ( 7 24 ) Back substitution gives 12x2 = 24 =⇒ x2 = 2, −x1 + 2x2 = 7 =⇒ x1 = −3 3x3 Linear Systems 1. Standard Example 4 1 0−4 −2 2 8 1 3 x1x2 x3 = 6−6 15 Perform row operations: R2← R2 +R1 and R3← R3− 2×R1. This gives:4 1 00 −1 2 0 −1 3 x1x2 x3 = 60 3 Perform row operation: R3← R3−R2. This gives:4 1 00 −1 2 0 0 1 x1x2 x3 = 60 3 Back substitution gives x3 = 3, −x2 + 2x3 = 0 =⇒ x2 = 6, 4x1 + x2 = 6 =⇒ x1 = 0. 2. Standard Example 3 −2 59 −2 16 3 −10 1 x1x2 x3 = −10 −5 Perform row operations: R2← R2− 3×R1 and R3← R3−R1. This gives:3 −2 50 4 1 0 −8 −4 x1x2 x3 = −13 −4 Perform row operation: R3← R3 + 2×R2. This gives:3 −2 50 4 1 0 0 −2 x1x2 x3 = −13 2 Back substitution gives −2x3 = 2 =⇒ x3 = −1, 4x2+x3 = 3 =⇒ x2 = 1, 3x1−2x2+5x1 = −1 =⇒ x1 = 2 43 3. Pivot Required 2 −3 1−4 6 2 6 −8 4 x1x2 x3 = 2−4 8 Perform row operations: R2← R2 + 2×R1 and R3← R3− 3×R1. This gives:2 −3 10 0 4 0 1 1 x1x2 x3 = 20 2 Swap rows 2 and 3 to get: 2 −3 10 1 1 0 0 4 x1x2 x3 = 22 0 Back substitution gives x3 = 0, x2 + x3 = 2 =⇒ x2 = 2, 2x1 − 3x2 + x3 = 2 =⇒ x1 = 4. 44 8 | Graphs 8.1 Paths, cycles, and connectivity Clarify For each of the graphs in Figure 8.1: 1. How many cycles are in the graph? Here, we consider two cycles to be identical if they contain the same vertices. That is, we do not care which vertex is the start/end vertex. 2. How many paths exist between points A and B? 3. What is the minimum number of edges which, if removed, would disconnect the graph? 0 1 2 3 4 A B (a) 0 1 2 5 4 3 A B (b) 0 1 2 3 4 5 6 7 A B (c) 0 1 2 5 4 3 A B (d) 0 2 1 6 3 5 4 A B (e) 0 1 2 3 7 6 5 4 A B (f) Figure 8.1: Graphs 45 Test 2 Exam Answers Clarify Graph 8.1a • Cycles: 0 • Edges to remove: 1 • Paths A→B: 1 – 0→ 1→ 2→ 3 Graph 8.1b • Cycles: 1 – 0→ 1→ 2→ 3→ 4→ 5→ 0 • Edges to remove: 2 • Paths A→B: 2 – 0→ 1→ 2→ 3 – 0→ 5→ 4→ 3 Graph 8.1c • Cycles: 3 – 0→ 3→ 5→ 0 – 1→ 3→ 6→ 4→ 1 – 2→ 4→ 7→ 2 • Edges to remove: 2 • Paths A→B: 8 – 0→ 3→ 1→ 4→ 2→ 7 – 0→ 3→ 1→ 4→ 7 – 0→ 3→ 6→ 4→ 2→ 7 – 0→ 3→ 6→ 4→ 7 – 0→ 5→ 3→ 1→ 4→ 2→ 7 – 0→ 5→ 3→ 1→ 1→ 7 – 0→ 5→ 3→ 6→ 4→ 2→ 7 – 0→ 5→ 3→ 6→ 4→ 7 Graph 8.1d • Cycles: 3 – 0→ 5→ 4→ 1→ 0 – 1→ 4→ 3→ 2→ 1 – 0→ 1→ 2→ 3→ 4→ 5→ 0 • Edges to remove: 2 • Paths A→B: 4 – 0→ 1→ 2→ 3 – 0→ 1→ 4→ 3 – 0→ 5→ 4→ 1→ 2→ 3 – 0→ 5→ 4→ 3 Graph 8.1e • Cycles: 3 – 1→ 0→ 6→ 1 – 1→ 5→ 4→ 1 – 1→ 3→ 2→ 1 • Edges to remove: 2 • Paths A→B: 4 – 0→ 1→ 2→ 3 – 0→ 1→ 3 – 0→ 6→ 1→ 2→ 3 – 0→ 6→ 1→ 3 Graph 8.1f • Cycles: 15 – 0→ 1→ 7→ 0 – 3→ 2→ 4→ 3 – 6→ 1→ 7→ 6 – 5→ 2→ 4→ 5 – 0→ 1→ 6→ 7→ 0 – 0→ 1→ 2→ 5→ 6→ 7→ 0 – 0→ 1→ 2→ 3→ 4→ 5→ 6→ 7→ 0 – 1→ 2→ 5→ 6→ 1 – 1→ 2→ 3→ 4→ 5→ 6→ 1 – 2→ 3→ 4→ 5→ 2 – 1→ 7→ 6→ 5→ 4→ 2→ 1 – 1→ 7→ 6→ 5→ 2→ 1 – 1→ 7→ 6→ 5→ 4→ 3→ 2→ 1 – 2→ 4→ 5→ 6→ 1→ 2 – 2→ 4→ 5→ 6→ 7→ 0→ 1→ 2 • Edges to remove: 2 • Paths A→B: 18 – 0→ 7→ 6→ 5→ 4 – 0→ 7→ 6→ 5→ 2→ 4 – 0→ 7→ 6→ 5→ 2→ 3→ 4 – 0→ 7→ 6→ 1→ 2→ 5→ 4 46 – 0→ 7→ 6→ 1→ 2→ 4 – 0→ 7→ 6→ 1→ 2→ 3→ 4 – 0→ 7→ 1→ 6→ 5→ 4 – 0→ 7→ 1→ 6→ 5→ 2→ 4 – 0→ 7→ 1→ 6→ 5→ 2→ 3→ 4 – 0→ 7→ 1→ 2→ 4 – 0→ 7→ 1→ 2→ 5→ 4 – 0→ 7→ 1→ 2→ 3→ 4 – 0→ 1→ 6→ 5→ 2→ 3→ 4 – 0→ 1→ 6→ 5→ 2→ 4 – 0→ 1→ 6→ 5→ 4 – 0→ 1→ 2→ 3→ 4 – 0→ 1→ 2→ 4 – 0→ 1→ 2→ 5→ 4 47 8.2 Spanning Trees Clarify How many spanning trees are there for the graphs in Figures 8.1b and 8.1e? Practice Use Prim’s algorithm to find a minimum spanning tree for graph G in Figure 8.2. List the edges added to the tree after each iteration of the algorithm, and state the weight of the final spanning tree. 0 1 2 3 4 5 6 7 8 1 1 1 3 2 3 3 2 4 2 4 3 3 Figure 8.2: Graph G 48 Exam Answers Clarify • The graph in Figure 8.1b has six spanning trees. Each one is a path with five edges: – a path from vertex 0 to vertex 5; – a path from vertex 1 to vertex 0; – a path from vertex 2 to vertex 1; – a path from vertex 3 to vertex 2; – a path from vertex 4 to vertex 3; and – a path from vertex 5 to vertex 4. • The graph in Figure 8.1e has 27 spanning trees. Each triangular subgraph must be covered by exactly two edges in a spanning tree of the graph (any more will create a cycle; any fewer will result in a disconnected graph, or one that doesn’t cover all the vertices). There are 3 different ways of choosing 2 edges in a triangle, and 33 different ways of combining these choices for all three triangles. Practice After each iteration, the set of edges in the partial spanning tree will be as follows (note the ordering given here is not a unique solution): • {(0, 1)} • {(0, 1), (0, 3)} • {(0, 1), (0, 3), (1, 2)} • {(0, 1), (0, 3), (1, 2), (0, 7)} • {(0, 1), (0, 3), (1, 2), (0, 7), (4, 7)} • {(0, 1), (0, 3), (1, 2), (0, 7), (4, 7), (4, 5)} • {(0, 1), (0, 3), (1, 2), (0, 7), (4, 7), (4, 5), (6, 7)} • {(0, 1), (0, 3), (1, 2), (0, 7), (4, 7), (4, 5), (6, 7), (7, 8)} The total weight of the minimum spanning tree is 15. 49 8.3 Graph Traversals Practice Apply Breadth First Search and Depth First Search to each of the graphs in Figure 8.3. Start your traversal from vertex 0, and write down the order in which vertices will be visited during the traversal. 0 1 2 3 4 5 6 7 8 9 (a) 0 1 2 3 4 5 6 7 8 9 10 (b) Figure 8.3: Graphs for BFS and DFS 50 Test 2 Exam Answers Practice Note that these solutions are not unique — there are other possible correct orderings for both the BFS and DFS traversals. • Graph 8.3a – BFS traversal: 0→ 3→ 4→ 1→ 6→ 7→ 2→ 8→ 9→ 5 – DFS traversal: 0→ 3→ 6→ 8→ 9→ 7→ 5→ 2→ 1→ 4 • Graph 8.3b – BFS traversal: 0→ 4→ 5→ 1→ 7→ 8→ 9→ 2→ 10→ 6→ 3 – DFS traversal: 0→ 4→ 7→ 5→ 8→ 9→ 10→ 2→ 6→ 3→ 1 51 9 | Stacks and Queues to be written... 52 Part III Algorithm Analysis 53 10 | Invariants 10.1 Finding Loop Invariants Clarify For the following Python functions, identify the loop exit condition, and the loop invariants that, together with the loop exit condition, show that the function produces the correct output. 1. def factorial(n): """ Input : positive integer n Output: n! """ res = 1 m = 1 while m <= n: res *= m m = m+1 return res 2. def reverse(lst): """ Input : list lst of elements Output: list of elements lst in reverse order """ revlst = [] while len(lst) > 0: revlst.append(lst.pop ()) return revlst 3. def exponentiation(x,n): """ Input : integers x, n Output: x**n """ res = 1 for i in range(n): res *= x return res 54 Test 2 Exam 4. def quotient_and_remainder(x,y): """ Input : positive integers x, y Output: x//y, x%y """ q=0 r=x while y <= r: r = r-y q = 1+q return q,r Practice For the following Python functions, identify the loop invariant(s), the loop exit condition, and the loop post- condition(s) which show the algorithm’s correctness. 1. def sum_change(list_of_cents ): """ Input: A list list_of_cents of non -negative integers Output: Two non -negative integers (total_dollars , total_cents), such that the sum of all numbers in list_of_cents is total_dollars *100 + total_cents , and total_cents < 100. For example: >>> sum_change ([50 ,35 ,165 ,20]) (2, 70) """ total_dollars , total_cents = 0, 0 while len(list_of_cents) > 0: total_cents += list_of_cents.pop() if total_cents >= 100: total_dollars += 1 total_cents -= 100 return (total_dollars , total_cents) 2. def even_sum(lst): """ Input: A list lst of integers Output: True if sum(lst) is even , False otherwise. For example: >>> even_sum ([1,2,3,4]) True >>> even_sum ([1,2,3,4,5]) False """ is_even = True while len(lst) > 0: if lst.pop() % 2 == 1: is_even = not is_even return is_even 55 Answers Clarify 1. def factorial(n): """ Input : positive integer n Output: n! """ res = 1 m = 1 while m <= n: #Invariant: res == (m-1)! res *= m m = m+1 #Invariant: res == (m-1)! #Exit condition: m == n+1 #Post -condition: res == n! return res 2. def reverse(lst): """ Input : list lst of elements Output: list of elements lst in reverse order """ revlst = [] while len(lst) > 0: #Invariant: lst + reverse(revlst) gives the original list revlst.append(lst.pop ()) #Invariant: lst + reverse(revlst) gives the original list #Exit condition: lst is empty #Post -condition: revlst contains the entire original list in reverse order return revlst 3. def exponentiation(x,n): """ Input : integers x, n Output: x**n """ res = 1 for i in range(n): #Invariant: x**i == res res *= x #Invariant: x**(i+1) == res #Exit condition: i == n - 1 #Post -condition: res == x**n return res 56 4. def quotient_and_remainder(x,y): """ Input : positive integers x, y Output: x//y, x%y """ q=0 r=x while y <= r: #Invariant: x == y*q + r r = r-y q = 1+q #Invariant: x == y*q + r #Exit condition: r < y #Post -condition: x == y*q + r and r < y. In other words , q == x//y and r == x%y. return q,r Practice 1. def sum_change(list_of_cents ): """ Input: A list list_of_cents of non -negative integers Output: Two non -negative integers (total_dollars , total_cents), such that the sum of all numbers in list_of_cents is total_dollars *100 + total_cents , and total_cents < 100. For example: >>> sum_change ([50 ,35 ,165 ,20]) (2, 70) """ total_dollars , total_cents = 0, 0 while len(list_of_cents) > 0: total_cents += list_of_cents.pop() if total_cents >= 100: total_dollars += 1 total_cents -= 100 #INV1: sum of original list_of_cents == (total_dollars * 100) + # total_cents + sum(list_of_cents) #INV2: total_cents < 100 #EXC: len(list_of_cents) == 0 #POC: sum of original list_of_cents == (total_dollars * 100) + # total_cents , and total_cents < 100 return (total_dollars , total_cents) 57 2. def even_sum(lst): """ Input: A list lst of integers Output: True if sum(lst) is even , False otherwise. For example: >>> even_sum ([1,2,3,4]) True >>> even_sum ([1,2,3,4,5]) False """ is_even = True while len(lst) > 0: if lst.pop() % 2 == 1: is_even = not is_even #INV: is_even is True if and only if the sum of # all items removed from lst is even #EXC: len(lst) == 0 #POC: is_even is True if and only if the sum of all # items in the original list is even return is_even 58 11 | Computational Complexity 11.1 Big-Oh Notation The purpose of the big-oh notation is to succinctly describe the “order of growth” of a function. Recall the formal definition from the lecture: Definition 1. A function f(n) is in O(g(n)) (read “f is in big oh of g”) if there are positive numbers c and n0 such that for all n ≥ n0 it holds that t(n) ≤ cg(n). In computational complexity analysis we apply this idea to time complexity functions T (n), i.e., the functions that describe the number of elementary steps that an algorithm needs to perform for an input of size n. Clarify Polynomial bounds For each of the following concrete time complexity give the tightest bound in terms of a simple polynomial function nc using big-Oh notation. That is, determine the smallest c such that T (n) ∈ O(nc). 1. T (n) = n+ 10 2. T (n) = 2n 3. T (n) = 5n+ 15 4. T (n) = n2 5. T (n) = 2n2 + n 6. T (n) = n2 − 10n− 1 7. T (n) = n/2 8. T (n) = 10 9. T (n) = n mod 100 Poly-logarithmic bounds For each of the following concrete time complexity give the tightest bound in terms of a simple poly-logarithmic function nc logd n using big-Oh notation. That is, determine the smallest pair c and d such that T (n) ∈ O ( nc logd n ) . That is, we consider n log2 n to be smaller1 than n2 log n. Generally, the sum log refers to the logarithm of base 2 and by log2 n we mean (log n)2. 1. T (n) = 10 log n+ 10 2. T (n) = n+ log n 3. T (n) = n log n 4. T (n) = log(10n+ 100) 5. T (n) = log10 n 6. T (n) = log1.5 n 7. T (n) = log n2 8. T (n) = log2 n 9. T (n) = log2 n+ n log n Practice For each of the following concrete time complexity give the tightest bound in terms of a simple poly-logarithmic function nc logd n using big-Oh notation (see clarify part for more details). 1Technically, we could say that we use the lexicographic order to determine what is a smaller pair. That is, we consider (c, d) = (1, 2) to be smaller than (c′, d′) = (2, 1). 59 Test 2 Exam 1. T (n) = 3, 700, 000 2. T (n) = 10n2 + 2 3. T (n) = sin(360n/(2pi)) 4. T (n) = 10n2 + 2 log3 n 5. T (n) = n3/10− n2 6. T (n) = 25n cos(2pi(n mod 100)/100) 7. T (n) = n2/2 + 10n log n 8. T (n) = 10n log(2n) + 5 9. T (n) = n3 + n2 log(n2) 10. T (n) = 1 + pi2(log n) 11. T (n) = n+ sin(360n/(2pi)) log n 12. T (n) = (10n2 + 10)/n Challenge 1. T (n) = n2 log n+ n log2 n 2. T (n) = 10 √ n+ log n 60 Answers Clarify Polynomial bounds 1. T (n) ∈ O(n) 2. T (n) ∈ O(n) 3. T (n) ∈ O(n) 4. T (n) ∈ O(n2) 5. T (n) ∈ O(n2) 6. T (n) ∈ O(n2) 7. T (n) ∈ O(n) 8. T (n) ∈ O(1) 9. T (n) ∈ O(1) Poly-logarithmic bounds 1. T (n) ∈ O(log n) 2. T (n) ∈ O(n) 3. T (n) ∈ O(n log n) 4. T (n) ∈ O(log n) 5. T (n) ∈ O(log n) 6. T (n) ∈ O(log n) 7. T (n) ∈ O(log n) 8. T (n) ∈ O(log2 n) 9. T (n) ∈ O(n log n) Practice 1. T (n) ∈ O(1) 2. T (n) ∈ O(n2) 3. T (n) ∈ O(1) 4. T (n) ∈ O(n2) 5. T (n) ∈ O(n3) 6. T (n) ∈ O(n) 7. T (n) ∈ O(n2) 8. T (n) ∈ O(n log n) 9. T (n) ∈ O(n3) 10. T (n) ∈ O(log n) 11. T (n) ∈ O(n) 12. T (n) ∈ O(n) Challenge 1. T (n) ∈ O(n2 log n) 2. T (n) ∈ O(n0.5) 61 11.2 Computational Complexity of Loops Clarify Determine for each variant of the function the computational complexity per line and conclude the overall compu- tational complexity of the function in terms of the input parameter n (in big-oh notation) . 1. def ones(n): res = [1] while len(res) < n: res += [1] return res 2. def ones(n): res = [1] while len(res) < n: res = res + [1] return res 3. def ones(n): res = [1] while len(res) < n: res = res + res return res 4. def ones(n): res = [1] while sum(res) < n: res += [1] return res 5. def ones(n): res = '1' while len(res) < n: res += '1' return res Practice For each of the following functions identify the tightest simple bound (using big-oh notation) of the worst-case computational complexity in terms of the input size and justify your answer. Depending on the specific function this justification could be based on one or more of: • structural properties of the worst-case input for a given input size (e.g., "in the worst case the input list is inversely ordered") • what line(s) is/are dominating the overall computational cost and • additional arguments as necessary. If helpful to make your argument, you can copy+paste the code of the function into your response to annotate individual lines with comments. 1. def loop_eg1(lst): n = len(lst) res = [] for i in range(n): for j in range(n): for k in range(n): res += [lst[i]*lst[j]*lst[k]] return res 2. def loop_eg2(lst): n = len(lst) res = [] for i in range(n): for j in range(i+1,n): res += [lst[i]*lst[j]] return res 62 Test 2 Exam 3. def loop_eg3(lst): n = len(lst) res = [] for i in range(0,n,2): for j in range(1,n,2): res += [lst[i]*lst[j]] return res 4. def loop_eg4(lst): n = len(lst) res = [] for i in range(n): j=1 while jres += [lst[i]*lst[j]] j = j*2 return res 5. def loop_eg5(lst ,target ): n = len(lst) for i in range(n): for j in range(i,n): if lst[i]+lst[j] == target: return True return False 6. def loop_eg6(lst , target ): n = len(lst) res = [] for i in range(1,n): if sum(lst[0:i]) > target: return i return None 63 Answers Clarify 1. Complexity is O(n). The loop iterates n times, and the complexity of the augmented assignment statement within the loop is only constant time, as only a single item is added to the list each time. def ones(n): # complexity O(n) res = [1] # 1 = O(1) while len(res) < n: # 1 + 1 ... + 1 (n times) = O(n) res += [1] # 1 + 1 ... + 1 (n times) = O(n) return res # 0 = O(0) 2. The loop iterates n times. Here, though, the concatenation statement inside the loop is creating a new list of length len(res) + 1 each time, where len(res) ranges from 1..n− 1. So this statement is O(n). Since it is executed n times, the total complexity is O(n2). def ones(n): # complexity O(n^2) res = [1] # 1 = O(1) while len(res) < n: # 1 + 1 ... + 1 (n times) = O(n) res = res + [1] # 2 + 3 ... + n = n(n+1)/2 - 1 = O(n^2) return res # 0 = O(0) 3. The cost of the line inside the loop is roughly 1 + 2 + 4 + . . . + n2 + n. This is less than n times the infinite geometric sum (1 + 12 + 1 4 + 1 8 . . .), so is O(n). def ones(n): # complexity O(n) res = [1] # 1 = O(1) while len(res) < n: # 1 + 1 ... + 1 (log n times) = O(log n) res = res + res # 2 + 4 ... + n = O(n) return res # 0 = O(0) 4. The loop iterates n times, and the augmented assignment statement inside the loop is only constant time. But the comparison operation occurring inside the loop condition contains a call to sum, which is O(n). Since this comparison occurs n times, this gives an overall complexity of O(n2). def ones(n): # complexity O(n^2) res = [1] # 1 = O(1) while sum(res) < n: # 1 + 2 ... + n = n(n+1)/2 = O(n^2) res += [1] # 1 + 1 ... + 1 (n times) = O(n) return res # 0 = O(0) 5. Since there are no in-place updates for strings, as they are immutable objects in Python, the statement inside the loop requires recreating a new copy of the variable res each time, so its complexity is determined by the length of the string that is created, that is, 2 + 3 + 4 + . . .+ n = O(n2). def ones(n): # complexity O(n^2) res = '1' # 1 = O(1) while len(res) < n: # 1 + 1 ... + 1 (n times) = O(n) res += '1' # 2 + 3 ... + n = n(n+1)/2 - 1 = O(n^2) return res # 0 = O(0) Practice 1. The computational complexity of the function is O(n3). This is because there are three nested loops, each of which iterates n times. The assignment operation augmenting the list is constant time, and is executed O(n3) times in the best and worst cases. 64 def loop_eg1(lst): n = len(lst) res = [] for i in range(n): # O(n) for j in range(n): # O(n^2) for k in range(n): # O(n^3) res += [lst[i]*lst[j]*lst[k]] # O(n^3) return res 2. The computational complexity of the function is O(n2). The outer loop iterates n times. The innermost loop iterates (n − 1) + (n − 2) + . . . 2 + 1 times, which is n(n+1)2 , or 12n2 + 12n. The n2 term still dominates the overall cost. The assignment operation inside the two nested loops is a constant time operation, and is executed O(n2) times in the best and worst cases. def loop_eg2(lst): n = len(lst) res = [] for i in range(n): # O(n) for j in range(i+1,n): # O(n^2) res += [lst[i]*lst[j]] # O(n^2) return res 3. The computational complexity of the function is O(n2). The outer loop iterates n/2 times, and the inner loop iterates n/2 times for every outer iteration, which is a total of (n/2)2 = 14n 2 times. The assignment operation inside the two nested loops is a constant time operation, and is executed O(n2) times in the best and worst cases. def loop_eg3(lst): n = len(lst) res = [] for i in range(0,n,2): # O(n) for j in range(1,n,2): # O(n^2) res += [lst[i]*lst[j]] # O(n^2) return res 4. The computational complexity of the function is O(nlogn). The outer loop iterates n times. The inner loop iterates log n times for every outer iteration. The two assignments inside the nested loops are constant time, and will be executed O(n log n) times in the best and worst cases. def loop_eg4(lst): n = len(lst) res = [] for i in range(n): # O(n) j=1 while jres += [lst[i]*lst[j]] # O(nlogn) j = j*2 # O(nlogn) return res 5. The computational complexity of the function is O(n2). In the worst case, no pair of elements in the list add up to the target, so the two nested loops finish iterating completely before the function returns. In this case, the outer loop iterates n times, and the innermost loop iterates (n − 1) + (n − 2) + . . . 2 + 1 times, which is n(n+1)2 , or 1 2n 2 + 12n. The assignment operation inside the two nested loops is a constant time operation which will be executed O(n2) times. In the best case, the first two elements in the list add up to the target, so the function returns before a single loop iteration has completed. This is O(1). 65 def loop_eg5(lst ,target ): n = len(lst) for i in range(n): # O(n) for j in range(i,n): # O(n^2) if lst[i]+lst[j] == target: # O(n^2) return True return False 6. The computational complexity of the function is O(n2). In the worst case, the sum of all elements in the list is still less than the target, so the function runs through to the end before returning. In this case, the outer loop iterates n times. The call to sum has a complexity of O(i) each time it is called, where i ranges from 1 to n-1. So this line requires 1 + 2 + 3 + . . .+ (n− 2) + (n− 1) operations in the worst case, which is O(n2). In the best case, the first element in the list is greater than the target, so the function returns before a single loop iteration has completed, and the call to sum only requires summing a single element. This is O(1). def loop_eg6(lst , target ): n = len(lst) res = [] for i in range(1,n): # O(n) if sum(lst[0:i]) > target: # O(n^2) return i return None 66 Part IV Additional Resources 67 12 | Practice Exam Questions The following collection of questions is an example for the middle part of the exam. It is preceded by a part where you have to write simple Python expression and apply known algorithms to specific inputs, and it is succeeded by a final part where you have to write Python programs to solve unknown problems (on the level of simple workshop problems). The recommended writing time for this middle part is 40 minutes. 12.1 Discussion of Theoretical Concepts For each of the questions in this section, we are looking for a short answer of around 3-5 sentences. Try to be precise and coherent and use the technical terms covered in the unit where relevant. Decrease-and-conquer A decrease-and-conquer algorithm decreases its input in each iteration by a constant c using O(1) time per iteration. That is, in the first iteration it goes from an input of size n to an input of size n− c. In the second iteration it goes from an input of size n− c to an input of n− 2c and so on. Once the algorithm reaches an input of size less than c it directly solves the problem in time O(1). What is the overall computational complexity of the algorithm and why? Vertex Cover Assume a graph G = (V,E) with n vertices has a vertex cover X of size k. What can be said about the size of the largest independent set of G and why? Complexity Classes Recall the greedy algorithm for the Knapsack Problem that constructs solution based on some score function: def greedy_knapsack(values , weights , capacity , score ): n = len(values) items = sorted(range(n), key=score , reverse=True) sel , value , weight = [], 0, 0 for i in items: if weight + weights[i] <= capacity: sel += [i] weight += weights[i] value += values[i] return sel , value , weight Assume you find a score function to be used in this algorithm that • has a computational complexity of O(n5) and • makes the algorithm always return an optimal solution. Based on your knowledge about the complexity classes P, NP, and NP-complete, what does this imply for the Hamiltonian Cycle Problem and why? 68 Exam 12.2 Computational Complexity For each of the following functions identify the tightest simple bound (using big-oh notation) of the worst-case computational complexity in terms of the input size and justify your answer. Depending on the specific function this justification could be based on one or more of: • structural properties of the worst-case input for a given input size (e.g., "in the worst case the input list is inversely ordered") • what line(s) is/are dominating the overall computational cost and • additional arguments as necessary. If helpful to make your argument, you can copy+paste the code of the function into your response to annotate individual lines with comments. Exponential Summary For the following function that accepts as input a lst, identify the tightest simple bound of the worst-case compu- tational complexity in terms of the input size n = len(lst) and justify your answer. def exp_summary2(lst): n = len(lst) res = [] i = 1 while i <= n: res += [lst[i-1]] i *= 2 return res Finding Common Elements For the following function that accepts as input two lists lst1 and lst2, identify the tightest simple bound of the worst-case computational complexity in terms of the combined input size n = len(lst1)+len(lst2) and justify your answer. def contained_in_both3(lst1 , lst2): n1 , n2 = len(lst1), len(lst2) lst1 = sorted(lst1) lst2 = sorted(lst2) res = [] i, j = 0, 0 while i < n1 and j < n2: if lst1[i] < lst2[j]: i += 1 elif lst1[i] > lst2[j]: j += 1 else: res += [lst1[i]] i += 1 j += 1 return res Median Square For the following function that accepts as input two lists lst1 and lst2, identify the tightest simple bound of the worst-case computational complexity in terms of the combined input size n = len(lst1)+len(lst2) and justify your answer. 69 def median_square(lst1 , lst2): squares = [] for x in lst1: # O(n) for y in lst2: # O(n^2) squares += [x*y] # O(n^2) return sorted(squares )[len(squares )//2] # O(n^2 log n) 12.3 Invariants For each of the following functions, identify the loop invariant(s), the loop exit condition, and the loop post-condition which shows the algorithm’s correctness. You can simply list the assertions, or you can copy and paste the function into the answer field and add assertion annotations as comments, as shown in the lecture. Inversions For the following function, identify the loop invariant(s), the loop exit condition, and the loop post-condition which shows the algorithm’s correctness. You can either use the invariant at the end of the inner or the outer loop. def inversions(lst): """ Input : a list of comparable elements Output: the number of 'inversions ' in lst , i.e., the number of index pairs i, j that violate ascending order or in other words: i < j and lst[i] > lst[j] For example: >>> inversions ([1, 3, 2]) 1 >>> inversions ([3, 2, 1, 0]) 6 """ n = len(lst) count = 0 for k in range(n): for l in range(k+1, n): count += lst[k] > lst[l] return count Longest Run For the following function, identify the loop invariant(s), the loop exit condition, and the loop post-condition which shows the algorithm’s correctness. You can either use the invariant at the end of the inner or the outer loop. def longest_run(lst): """ Input : a list of comparable elements Output: a longest sublist of lst that is sorted For example: >>> longest_run ([1, 2, 0, 1, 2, 1, 0, 1]) [0, 1, 2] >>> longest_run ([10, 9, 8, 7, 11]) [7, 11] """ 70 n = len(lst) k, l = 0, 1 i, j = 0, 1 while j < n: if lst[j] < lst[j-1]: i = j j += 1 if j - i > l - k: k, l = i, j return lst[k: l] 71 13 | Solutions to Practice Exam Questions 13.1 Solutions: Discussion of Theoretical Concepts Decrease-and-conquer Such an algorithm has an overall linear time computational complexity, because it requires n//c iterations to solve the problem. Since each iteration costs O(1), this results in a complexity of O(n//c)=O(n). Vertex Cover All vertices that are not in the vertex cover X form an independent set (because if there was an edge between any two of them this edge wouldn’t be covered by X). There are n-k vertices that are not contained in X. Thus, the largest independent set of G must be of at least size n - k (but perhaps there are even larger independent sets). Complexity Classes Using this hypothetical score function would result in an overall O(n**6 log n) algorithm for the Knapsack Problem, which is polynomial time. This would also imply a polynomial time algorithm for the decision variant of the Knapsack problem, which is NP-complete. Since the Hamiltonian Cycle Problem is in NP, it can be polynomially reduced to the Knapsack Problem and, hence, we would also have a polynomial time algorithm for that problem. 13.2 Solutions: Computational Complexity Exponential Summary The computational complexity of the function is O(log n) irrespectively of the structure of the input list. This is because the iteration variable is doubled in each iteration resulting in O(log n) executions of the while loop, and each individual operation inside the loop body as well as each individual check of the loop condition cost O(1). def exp_summary2(lst): # O(log n) n = len(lst) res = [] i = 1 while i <= n: # O(log n) res += [lst[i-1]] # O(log n) i *= 2 # O(log n) return res Finding Common Elements The computational worst-case complexity of the function is O(n log n), which is attained for two unsorted inputs lst1 and lst2 of roughly equal lengths n1 and n2. In this case the two sorting steps dominate the computational cost, which are both of complexity O(n/2 log n/2) = O(n log n). In contrast, the while loop only costs O(n1+n2) = O(n) because in every iteration at least one of i or j is increased, which means that after at most n iterations either i == n1 or j == n2. 72 def contained_in_both3(lst1 , lst2): # O(n log n) n1 , n2 = len(lst1), len(lst2) lst1 = sorted(lst1) # O(n log n) lst2 = sorted(lst2) # O(n log n) res = [] i, j = 0, 0 while i < n1 and j < n2: # O(n) if lst1[i] < lst2[j]: # O(n) i += 1 # O(n) elif lst1[i] > lst2[j]: # O(n) j += 1 # O(n) else: res += [lst1[i]] # O(n) i += 1 # O(n) j += 1 # O(n) return res Median Square The complexity of the function is O(n**2 log n) for the worst case of two input lists of roughly equal size n1=n2 (n=n1+n2). This is because the nested for-loop create an unordered list of length n1*n2=O(n**2), which is then sorted in time O(n**2 log n**2) = O(n**2 log n) dominating the overall computational cost. def median_square(lst1 , lst2): squares = [] for x in lst1: # O(n) for y in lst2: # O(n^2) squares += [x*y] # O(n^2) return sorted(squares )[len(squares )//2] # O(n^2 log n) 13.3 Solutions: Invariants Inversions def inversions(lst): n = len(lst) count = 0 for k in range(n): for l in range(k+1, n): count += lst[k] > lst[l] # INV: count equal to number of index pairs i, j # with i < k < j < l and lst[i] > lst[j] # EXC: k == l == n # POC: count equal to number of index pairs i, j # with i < j < n and lst[i] > lst[j] return count Longest Run def longest_run(lst): n = len(lst) k, l = 0, 1 73 i, j = 0, 1 while j < n: if lst[j] < lst[j-1]: i = j j += 1 if j - i > l - k: k, l = i, j # INV1: lst[i:j] and lst[k: l] are sorted # INV2: if lst[a: b] is sorted for a= b - a # EXC: j == n # POC: if lst[a: b] is sorted for a= b - a return lst[k: l] 74 欢迎咨询51作业君