This commit is contained in:
Hykilpikonna
2021-12-08 16:45:25 -05:00
parent 566ea64863
commit 44a4c9bbe5
4 changed files with 478 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
"""CSC110 Fall 2021: Term Test 2, Question 1 (Cryptography)
Module Description
==================
This module contains instructions for this question. There are THREE
parts of this question, labelled "Part (a)", "Part (b)", etc.
The comments in this file contain instructions on how to complete each part,
so please read those comments carefully.
At the bottom of the file we've provided code to run doctest and python_ta.
python_ta is not required for grading.
SUBMIT THIS FILE FOR GRADING!
Copyright and Usage Information
===============================
This file is provided solely for the personal and private use of students
taking CSC110 at the University of Toronto St. George campus. All forms of
distribution of this code, whether as given or with any changes, are
expressly prohibited. For more information on copyright for CSC110 materials,
please consult our Course Syllabus.
This file is Copyright (c) 2021 Mario Badr and Tom Fairgrieve.
"""
###################################################################################################
# Encrypting Grade Messages
###################################################################################################
# We can represent your first-year courses grades using a tuple of five integers between 0 and 100,
# inclusive. A *grades message* is a list of grades, i.e., a list[tuple[int, int, int, int, int]].
# For example:
#
# [(95, 90, 67, 75, 89), (55, 64, 78, 92, 86), (100, 96, 64, 83, 87)]
###################################################################################################
# Description of the Cryptosystem
###################################################################################################
# We define the following SYMMETRIC-KEY CRYPTOSYSTEM on grade messages:
#
# PLAINTEXT and CIPHERTEXT:
# Grades messages (i.e, list[tuple[int, int, int, int, int]],
# where each int is in 0-100, inclusive.
#
# SECRET KEY:
# The secret key is an integer k.
#
# ENCRYPTION:
# For each index i in the plaintext grade message (starting at i = 0), transform the i-th grade
# tuple by adding (k^(i+1)) to each element of the tuple and then taking the remainder modulo 101.
#
# DECRYPTION:
# Reverse the encryption process.
###################################################################################################
# Part (a) - encryption
###################################################################################################
# The function below is the start of the encryption algorithm described above.
# Complete it by doing two things:
# 1. Complete the doctest example by showing the expected return value of the function call.
# 2. Implement the function body. You may use loops, comprehensions, and/or helper functions.
# You are not required to add doctests for any helper functions you create.
#
# Note: encryption should NOT mutate the input plaintext.
def encrypt_tt2(k: int, plaintext: list[tuple[int, int, int, int, int]]) \
-> list[tuple[int, int, int, int, int]]:
"""Return the ciphertext grade message when plaintext is encrypted with key k.
Preconditions:
- k and plaintext are valid inputs for encryption, based on the cryptosystem description
>>> key = 20
>>> p = [(95, 90, 67, 75, 89), (55, 64, 78, 92, 86)]
>>> encrypt_tt2(key, p)
[(14, 9, 87, 95, 8), (51, 60, 74, 88, 82)]
"""
c = []
for i in range(len(plaintext)):
incr = k ** (i + 1)
n1, n2, n3, n4, n5 = [(n + incr) % 101 for n in plaintext[i]]
c.append((n1, n2, n3, n4, n5))
return c
###################################################################################################
# Part (b) - decryption
###################################################################################################
# The function below is the start of the decryption algorithm described above.
# Complete it by doing two things:
# 1. Add one new doctest example for this function.
# 2. Implement the function body. You may use loops, comprehensions, and/or helper functions.
# You are not required to add doctests for any helper functions you create.
# It is up to you to determine the correct algorithm for decrypting a ciphertext,
# based on the description of the encryption algorithm.
#
# Note: decryption should NOT mutate the input ciphertext.
def decrypt_tt2(k: int, ciphertext: list[tuple[int, int, int, int, int]]) \
-> list[tuple[int, int, int, int, int]]:
"""Return the plaintext grade message when ciphertext is decrypted with key k.
Preconditions:
- k and ciphertext are valid inputs for decryption, based on the cryptosystem description
>>> key = 20
>>> c = [(19, 15, 84, 2, 6)]
>>> decrypt_tt2(key, c)
[(100, 96, 64, 83, 87)]
"""
p = []
for i in range(len(ciphertext)):
incr = k ** (i + 1)
n1, n2, n3, n4, n5 = [(n - incr) % 101 for n in ciphertext[i]]
p.append((n1, n2, n3, n4, n5))
return p
###################################################################################################
# Part (c) - understanding the cryptosystem
###################################################################################################
def why_101() -> str:
"""Return a string describing why we use a modulus of 101 instead of 100 in the cryptosystem
defined above.
"""
return "This is because modding by 100 will return the last two digits of the original number" \
", which will reveal the original content if the key is a whole number like 10 or 20"
BIN
View File
Binary file not shown.
+158
View File
@@ -0,0 +1,158 @@
% Copyright and Usage Information
% ===============================
% This file is provided solely for the personal and private use of students
% taking CSC110 at the University of Toronto St. George campus. All forms of
% distribution of this code, whether as given or with any changes, are
% expressly prohibited. For more information on copyright for CSC110 materials,
% please consult our Course Syllabus.
% This file is Copyright (c) 2021 Mario Badr and Tom Fairgrieve.
\documentclass{article}
\setlength{\parindent}{0pt}
\setlength{\parskip}{5pt}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsthm}
\usepackage{amsfonts}
\usepackage[margin=1in]{geometry}
\title{CSC110 Fall 2021: Term Test 2\\
Question 2 (Analyzing Algorithm Running Time)}
\author{TODO: INSERT YOUR NAME HERE}
\date{Wednesday December 8, 2021}
% Some useful LaTeX commands. You are free to use these or not, and also add your own.
\newcommand{\N}{\mathbb{N}}
\newcommand{\Z}{\mathbb{Z}}
\newcommand{\R}{\mathbb{R}}
\newcommand{\cO}{\mathcal{O}}
\newcommand{\floor}[1]{\left\lfloor #1 \right\rfloor}
\newcommand{\ceil}[1]{\left\lceil #1 \right\rceil}
\newcommand{\C}{\texttt}
\begin{document}
\maketitle
\subsection*{Question 2, Part 1}
\noindent
We define the function $g: \N \to \R^{\geq 0}$ as $g(n) = 7n(n-1)^2$.
Consider the following statement:
\[
g(n) \in \cO(n^4)
\]
\begin{enumerate}
\item[(a)]
Rewrite the statement $g(n) \in \cO(n^4)$ by expanding the definition of Big-O.
\bigskip
\textbf{Solution}:
$\exists c, n_0 \in \R^+ \text{ s.t. } \forall n \in \N, n \ge n_0 \Rightarrow 7n(n-1)^2 \le c \cdot n^4$
\item[(b)]
Write the \emph{negation} of the statement from (a), using negation rules to simplify the statement as much as possible.
\bigskip
\textbf{Solution}:
$\forall c, n_0 \in \R^+, \exists n \in \N \text{ s.t. } n \ge n_0 \land 7n(n-1)^2 > c \cdot n^4$
\item[(c)]
Which of statements (a) and (b) is true? Provide a complete proof that justifies your choice.
In your proof, you may not use any properties or theorems about Big-O/Omega/Theta. Work from the expanded statement from (a) or (b).
\bigskip
\textbf{Solution}:
I think statement (a) is true.
\begin{proof}
Want to show: $\exists c, n_0 \in \R^+ \text{ s.t. } \forall n \in \N, n \ge n_0 \Rightarrow 7n(n-1)^2 \le c \cdot n^4$ \\
Prove using Induction. \\
Take $c = 7, n_0 = 1$ \\
Let $n$ be an arbitrary natural number such that $n \ge (n_0 = 1)$ \\
What we want to prove becomes: $\forall n \in \N, n \ge 1 \Rightarrow 7n(n-1)^2 \le 7n^4$ \\
\\
Since $n \ge 1$, \\
Multiply both sides by $n^2$, we get $n^3 \ge n^2$ \\
From this, we also know that $(n - 1)^2 \le n^3$ \\
Also, since $n \ge 1$, \\
Multiply the inequality by $-2$, we get $-2n \le -2$ \\
Adding $1$ to both sides, we get $-2n + 1 \le -1$ \\
Putting the two inequalities together, we have $n^2 - 2n + 1 \le n^3 - 1 \le n^3$ \\
Factoring the polynomial on the left, we have $(n - 1)^2 \le n^3$ \\
Multiply both sides by $7n$, we get $7n(n - 1)^2 \le 7n^4$\\
Which is what we want to prove.
\end{proof}
\end{enumerate}
\subsection*{Question 2, Part 2}
\noindent
Consider the function below.
\begin{verbatim}
def f(nums: list[int]) -> list[int]: # Line 1
n = len(nums) # Line 2
i = 1 # Line 3
new_list = [] # Line 4
while i < n: # Line 5
if nums[i] % 2 == 0: # Line 6
list.append(new_list, i) # Line 7
else: # Line 8
new_list = [i * j for j in nums] # Line 9
i = i * 3 # Line 10
return new_list # Line 11
\end{verbatim}
\begin{enumerate}
\item[(a)]
Perform an \emph{upper bound analysis} on the worst-case running time of \texttt{f}.
The Big-O expression that you conclude should be \emph{tight}, meaning that the worst-case running time should be Theta of this expression, but you are not required to show that here.
\textbf{To simplify your analysis}, you may omit all floors and ceilings in your calculations (if applicable).
Use ``at most'' or $\leq$ to be explicit about where a step count expression is an upper bound.
\textbf{Solution}:
Let $n$ be the length of the input list \C{nums}
There is one loop in the function which loops through \C{nums} with $i$ increasing exponentially, which will run $\ceil{log_3(n)}$ times. Inside the loop, if then number is even, it takes $\cO(1)$ to append the item at the end of \C{new\_list}. If the number is odd, it sets \C{new\_list} to a list comprehension which iterates through all number in \C{nums}, performing an $\cO(1)$ multiplication every iteration, which takes exactly $n$ steps, which is a larger running time than if the number is even. Therefore, the inside of the loop will take at most $n$ steps, if all numbers \C{nums[i]} iterated are odd.
Since there are only constant-time operations outside the loop, the worst-case running time would be $\ceil{log_3(n)}$ iterations multiplied by at most $n$ steps per iteration, which is $n\ceil{log_3(n)}$ steps.
Since $n\ceil{log_3(n)} \in \cO(n\ceil{log_3(n)})$, we can conclude that $WC_{f}(n) \in \cO(n\ceil{log_3(n)})$
\item[(b)]
Perform a \emph{lower bound analysis} on the worst-case running time of \texttt{f}.
The Omega expression you find should match your Big-O expression from part (a).
\textbf{Hint}: you don't need to try to find an ``exact maximum running-time'' input. \emph{Any} input family whose running time is Omega of (``at least'') the bound you found in part (a) will yield a correct analysis for this part.
\textbf{Solution}:
Let $n$ be the length of the input list \C{nums}, let \C{nums} be the list of length $n$ which every number is 1.
In this case, the if statement inside the loop always runs line 9 that takes $n$ steps, and then the $i = i * 3$ statement, which is 1 step, which is a total of $n + 1$ steps. The loop still iterates $\ceil{log_3(n)}$ times. Since there are only constant-time operations outside the loop, the total number of steps for this input is $(n + 1)\ceil{log_3(n)} + c$ which $c \in \N$ is a constant, which is $WC_{f}(n) \in \Omega(n\ceil{log_3(n)})$
\end{enumerate}
\begin{center}
\textbf{SUBMIT THIS FILE AND THE GENERATED PDF q2.pdf FOR GRADING}
\end{center}
\end{document}
+191
View File
@@ -0,0 +1,191 @@
"""CSC110 Fall 2021: Term Test 2, Question 3 (Object Oriented Design)
Module Description
==================
This module contains instructions for this question. There are TWO
parts of this question, labelled "Part (a)", "Part (b)", etc.
The comments in this file contain instructions on how to complete each part,
so please read those comments carefully.
SUBMIT THIS FILE FOR GRADING!
Copyright and Usage Information
===============================
This file is provided solely for the personal and private use of students
taking CSC110 at the University of Toronto St. George campus. All forms of
distribution of this code, whether as given or with any changes, are
expressly prohibited. For more information on copyright for CSC110 materials,
please consult our Course Syllabus.
This file is Copyright (c) 2021 Mario Badr and Tom Fairgrieve.
"""
from dataclasses import dataclass
###################################################################################################
# Modelling a Problem Domain
###################################################################################################
# In this question, you will model a new problem domain by designing classes in a similar style to
# the food delivery system we studied in lecture.
###################################################################################################
# Description of the Problem Domain
###################################################################################################
# Your client is the University of Toronto. They want new software to manage the grades of their
# students. The grade system will store:
# 1. GRADES that each have the course id (e.g., 'CSC110Y'), term id (e.g., '20219'), and score in
# the course (i.e., an integer ranging from 0 to 100, inclusive).
# 2. STUDENTS that each have a utorid (e.g., 'astudent') and a collection of grades.
###################################################################################################
# Part (a) - Entity data classes
###################################################################################################
# Your first task is to complete the two data classes below, which represent the two main entities
# in our computational model. Read the provided docstrings and use them to complete each data class
# body.
#
# Do NOT add any additional instance attributes or change the class docstring.
# You do NOT need to add any representation invariants or doctest examples.
@dataclass
class Grade:
"""A grade on Acorn.
Instance Attributes:
- course: the course (e.g., 'CSC110Y1') this grade was achieved in.
- term: the term (e.g., '20219') this grade was achieved in.
- score: an integer representing the grade achieved.
"""
course: str
term: str
score: int
@dataclass
class Student:
"""A student who can have courses grades on acorn.
Instance Attributes:
- utorid: the utorid of this student
- grades: a dictionary mapping a course (e.g., 'CSC110Y1') to the student's Grade in that
course.
"""
utorid: str
grades: dict[str, Grade]
###############################################################################
# Part (b) - Manager class
###############################################################################
# The class below is responsible for keeping track of all instances of the entities in our
# model and performing mutating operations on them.
#
# Read through the class and implement the methods that have empty bodies.
# Do NOT change anything else in the class (attributes, methods that we have implemented).
class AcornSystem:
"""A class that tracks students and their grades."""
# Private Instance Attributes:
# - _students: a dictionary mapping utorids to Students
def __init__(self, initial_students: list[Student]) -> None:
"""Initialize a new AcornSystem, adding every student in initial_students to this system.
Precondition:
- Every student in initial_students has a unique utorid
"""
self._students = {}
for student in initial_students:
self._students[student.utorid] = student
def student_count(self) -> int:
"""Return the number of students in this system."""
return len(self._students)
def add_grade(self, utorid: str, grade: Grade) -> bool:
"""Add grade to the student with utorid and return whether the grade was added successfully.
Do not add grade if the student with utorid already has a grade assigned for that course.
Preconditions:
- A student with utorid exists in this system
"""
student = self._students[utorid]
if grade.course in student.grades:
return False
else:
student.grades[grade.course] = grade
return True
def get_grades(self, utorid: str, term: str) -> list[tuple[str, int]]:
"""Return a list of tuples of the courses and scores that the student with utorid achieved
in term.
Preconditions:
- A student with utorid exists in this system
- The student in this system with utorid has a grade for course
>>> acorn_system = AcornSystem([Student('astudent', {})])
>>> acorn_system.add_grade('astudent', Grade('CLA204H1', '20209', 76))
True
>>> acorn_system.get_grades('astudent', '20209')
[('CLA204H1', 76)]
"""
return [(g.course, g.score) for g in self._students[utorid].grades.values()
if g.term == term]
def amend_grades(self, utorid: str, amendments: dict[str, int]) -> int:
"""Update the grades for the student with utorid based on the amendments, which maps courses
to the amended score.
Return the number of successful amendments made. An amendment is not successful if the
student has no grade on record for the course. No mutation should occur for students who
have no grade on record for course.
Preconditions:
- A student with utorid exists in this system
>>> acorn_system = AcornSystem([Student('astudent', {})])
>>> acorn_system.add_grade('astudent', Grade('CLA204H1', '20209', 76))
True
>>> acorn_system.amend_grades('astudent', {'CLA204H1': 80})
1
>>> acorn_system.get_grades('astudent', '20209')
[('CLA204H1', 80)]
"""
grades = self._students[utorid].grades
count = 0
for course in amendments:
if course in grades:
grades[course].score = amendments[course]
count += 1
return count
def calculate_averages(self, utorids: list[str]) -> dict[str, float]:
"""Return a dictionary mapping a student's utorid to the average grade they achieved for
all courses completed.
Preconditions:
- Every utorid in amendments is a student in this system
>>> acorn_system = AcornSystem([Student('astudent', {}), Student('bstudent', {})])
>>> acorn_system.add_grade('astudent', Grade('CLA204H1', '20209', 76))
True
>>> acorn_system.calculate_averages(['astudent'])
{'astudent': 76.0}
>>> acorn_system.add_grade('astudent', Grade('CLA205H1', '20219', 98))
True
>>> acorn_system.calculate_averages(['astudent'])
{'astudent': 87.0}
>>> acorn_system.add_grade('bstudent', Grade('CLA205H1', '20219', 98))
True
>>> temp = acorn_system.calculate_averages(['astudent', 'bstudent'])
>>> temp == {'astudent': 87.0, 'bstudent': 98.0}
True
"""
return {u: sum(g.score for g in self._students[u].grades.values())
/ len(self._students[u].grades) for u in utorids}