From 5e5ab24f2f56257a7d86ff30b9d18579230917dd Mon Sep 17 00:00:00 2001 From: MstrPikachu <31784486+MstrPikachu@users.noreply.github.com> Date: Sun, 30 Jan 2022 22:13:23 -0500 Subject: [PATCH 1/4] A1 P1 Q2 Split up test units for all 3 linked lists into 3 test units --- assignments/A1/a1_part1_test.py | 69 +++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/assignments/A1/a1_part1_test.py b/assignments/A1/a1_part1_test.py index efaceb1..f763189 100644 --- a/assignments/A1/a1_part1_test.py +++ b/assignments/A1/a1_part1_test.py @@ -28,22 +28,34 @@ import pytest from a1_part1 import MoveToFrontLinkedList, SwapLinkedList, CountLinkedList -def test_all_lists_true() -> None: - movetofront = [MoveToFrontLinkedList(range(i)) for i in range(100)] - swap = [SwapLinkedList(range(i)) for i in range(100)] - count = [CountLinkedList(range(i)) for i in range(100)] - assert(all(all(x in movetofront[i] for x in range(i)) for i in range(100))) - assert(all(all(x in swap[i] for x in range(i)) for i in range(100))) - assert(all(all(x in count[i] for x in range(i)) for i in range(100))) +def test_movetofront_true() -> None: + lst = [MoveToFrontLinkedList(range(i)) for i in range(100)] + assert(all(all(x in lst[i] for x in range(i)) for i in range(100))) -def test_all_lists_false() -> None: - movetofront = [MoveToFrontLinkedList(range(i)) for i in range(100)] - swap = [SwapLinkedList(range(i)) for i in range(100)] - count = [CountLinkedList(range(i)) for i in range(100)] - assert(all(all(x not in movetofront[i] for x in range(i, i + 100)) for i in range(100))) - assert(all(all(x not in swap[i] for x in range(i, i + 100)) for i in range(100))) - assert(all(all(x not in count[i] for x in range(i, i + 100)) for i in range(100))) +def test_swap_true() -> None: + lst = [SwapLinkedList(range(i)) for i in range(100)] + assert(all(all(x in lst[i] for x in range(i)) for i in range(100))) + + +def test_count_true() -> None: + lst = [CountLinkedList(range(i)) for i in range(100)] + assert(all(all(x in lst[i] for x in range(i)) for i in range(100))) + + +def test_movetofront_false() -> None: + lst = [MoveToFrontLinkedList(range(i)) for i in range(100)] + assert(all(all(x not in lst[i] for x in range(i, i + 100)) for i in range(100))) + + +def test_swap_false() -> None: + lst = [SwapLinkedList(range(i)) for i in range(100)] + assert(all(all(x not in lst[i] for x in range(i, i + 100)) for i in range(100))) + + +def test_count_false() -> None: + lst = [CountLinkedList(range(i)) for i in range(100)] + assert(all(all(x not in lst[i] for x in range(i, i + 100)) for i in range(100))) def generate_ordering(n: int): @@ -54,27 +66,32 @@ def generate_ordering(n: int): """ arr = [(2 << i) % n for i in range(1, n)] arr.append(0) - assert set(arr) == set(range(n)) + # assert set(arr) == set(range(n)) return arr def test_movetofront_mutation() -> None: + """Tests the mutation of a MoveToFrontLinkedList by searching for every number from 0-100 inclusive.""" lst = MoveToFrontLinkedList(range(101)) arr = list(range(101)) operations = generate_ordering(101) + bools = [] # test results of mutation after each iteration for x in operations: i = arr.index(x) # find index arr.insert(0, arr.pop(i)) # simulate moving to front x in lst - assert lst.to_list() == arr + bools.append(lst.to_list() == arr) + assert all(bools) def test_swap_mutation() -> None: + """Tests the mutation of a SwapLinkedList by searching for every number from 0-100 inclusive.""" lst = SwapLinkedList(range(101)) arr = list(range(101)) operations = generate_ordering(101) + bools = [] # test results of mutation after each iteration for x in operations: i = arr.index(x) # find index if i > 0: # simulate swapping @@ -82,21 +99,31 @@ def test_swap_mutation() -> None: arr[i] = arr[i - 1] arr[i - 1] = temp x in lst - assert lst.to_list() == arr + bools.append(lst.to_list() == arr) + assert all(bools) def test_count_mutation() -> None: + """Tests the mutation of a CountLinkedList by searching for every number from 0-100 inclusive, and then + every number from 0-49 inclusive.""" lst = CountLinkedList(generate_ordering(101)) - operations = list(range(101)) - for x in operations: + for x in list(range(101)): # reorder the linked list to natural ordering x in lst - assert lst.to_list() == operations - for x in reversed(range(50)): + for x in reversed(range(50)): # reverse the first 50 numbers x in lst assert lst.to_list() == list(reversed(range(50))) + list(range(50, 101)) if __name__ == '__main__': pytest.main(['a1_part1_test.py', '-v']) + + import python_ta + + python_ta.check_all(config={ + 'max-line-length': 100, + 'disable': ['E1136'], + 'extra-imports': ['a1_linked_list'], + 'max-nested-blocks': 4 + }, output='pyta_report.html') From 51dcf5e7aed0f0e6c0e8d0affe6238181aa5b706 Mon Sep 17 00:00:00 2001 From: MstrPikachu <31784486+MstrPikachu@users.noreply.github.com> Date: Sun, 30 Jan 2022 22:14:07 -0500 Subject: [PATCH 2/4] A1 P1 Q4 finish proofs --- assignments/A1/a1.tex | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/assignments/A1/a1.tex b/assignments/A1/a1.tex index 9d651a3..d20a0b1 100644 --- a/assignments/A1/a1.tex +++ b/assignments/A1/a1.tex @@ -2,9 +2,11 @@ \usepackage{amsmath} \usepackage[utf8]{inputenc} \usepackage[margin=0.75in]{geometry} +\usepackage{amsfonts} +\usepackage{amssymb} \title{CSC111 Assignment 1: Linked Lists} -\author{Azalea Gui \& Peter Lin} +\author{Azalea Gui and Peter Lin} \date{\today} \begin{document} @@ -26,14 +28,27 @@ Do \textbf{not} include your solution in this file. \begin{enumerate} \item[(a)] - TODO: Write your answer here. + Let $n, m \in \mathbb Z^+$. Because \texttt{\_\_contains\_\_} doesn't mutate the \texttt{LinkedList}, each function call is independent from each other. Therefore, we can just find the total running time of one call to \texttt{\_\_contains\_\_} and multiply it with $m$.\\ + Because $n - 1$ is the $n^{th}$ element in the \texttt{LinkedList}, the loop must iterate $n$ times. Everything else other than the loop are constant time operations and can be counted as 1. + So the total running time is $m(n + 1) = nm + m$ or $\Theta(nm + m)$. \item[(b)] - TODO: Write your answer here. + The first search operation moves the element to the front, and because that's the only one being searched for, it will stay there for the rest of the searches. So the first search takes $n$ iterations and some constant time, while the rest of the searches are constant time because it will be the first element. Therefore the total running time is $n + m + 1$ or $\Theta(n + m)$.\\ + + The second search operation moves the element closer to the beginning by 1 element. So if $m < n$, the number of loop iterations is $n + (n - 1) + ... + (n - m + 1) = \frac{m(2n - m + 1)}{2}$, adding constant time operations to be a total running time of $\frac{m(2n - m + 3)}{2}$ or $\Theta(m(2n - m))$. Because $n < 2n - m \leq 2n$, this is also $\Theta(mn)$.\\ + + If $m \geq n$, then there will be constant time searches as the element will already be at the very beginning. So the total number of loop iterations is $\frac{n(n + 1)}{2}$, adding constant time operations to get a total running time of $\frac{n(n + 1)}{2} + m$ or $\Theta(n^2 + m)$.\\ + + The last search operation is equivalent to the first. Because all elements start with a count of 0, the first search will bring the element to the beginning with $n$ loop iterations, and every other search becomes constant time. Therefore the answer is the same as the first search operation, having a total running time of $n + m + 1$ or $\Theta(n + m)$. \end{enumerate} \item[4.] -TODO: Write your answer here. +Assume $n > 3$. Also assume $m > n$. +Consider a sequence $[n - 1, n - 2, ..., 0, n - 1, n - 2, ...]$. Each time a search is performed in the \texttt{MoveToFrontLinkedList}, it must iterate to the last element each time. However, the \texttt{SwapLinkedList} does not have to do that. The first 2 times it will access the $n^{th}$ element, however the next 2 times it will access the $n-2^{th}$, and the next 2 will access the $n-4^{th}$, etc.\\ + +Thus the total running time for \texttt{MoveToFrontLinkedList} is $T_1 = mn$ and the total running time for \texttt{SwapLinkedList} is $T_2 = n + n + (n - 2) + (n - 2) + ...$. Then $T_1 - T_2 = 0 + 0 + 2 + 2 + 4 + 4 + ... + 0 + 0 + 2 + ...$. Each element of this sum is less than $n$, and there are $m$ elements in the sum. Thus $T_1 - T_2 \in \mathcal O(mn)$.\\ + +To find a lower bound, first find the running time for searching $n$ elements. This would be $0 + 0 + 2 + 2 + ... + n - 4 + n - 4 + n - 2$ if $n$ is odd, and $0 + 0 + 2 + 2 + ... + n - 2 + n - 2$ if $n$ is even. If $n$ is even, then the sum is $n\frac{n - 2}{2}$. If $n$ is odd, then $n - 1$ is even, therefore $(n - 1)\frac{n - 3}{2}$ is a lower bound. Let $L = (n - 1)\frac{n - 3}{2}$ be this lower bound. Now consider a sequence very similar to $T_1 - T_2$, $T_3 = 0 + 0 + ... + L + 0 + 0 + ... + L + 0 + ...$, where the first $n - 1$ elements are 0 and the next element is $L$, and then it repeats. The partial sum of $T_3$ is smaller or equal to the partial sum for $T_2 - T_1$, so this is a lower bound for $T_1 - T_2$. Also, $T_3 \in \Omega (m\frac{L}{n})$, because $m > n$, a constant factor of $\frac{1}{2}$ can be chosen, and then $T_3$ will always be greater or equal to $m\frac{L}{2n}$. So $T_1 - T_2 \in \Omega (m\frac{L}{n}) = \Omega (m\frac{(n - 1)(n - 3)}{n} = \Omega (mn)$. Therefore $T_1 - T_2 \in \Theta (mn)$. \end{enumerate} From d363a518e83057e8e64da312ac82baa7434a438e Mon Sep 17 00:00:00 2001 From: MstrPikachu <31784486+MstrPikachu@users.noreply.github.com> Date: Sun, 30 Jan 2022 22:24:22 -0500 Subject: [PATCH 3/4] A1 P1 Q2 remove print statement --- assignments/A1/a1_part1.py | 1 - 1 file changed, 1 deletion(-) diff --git a/assignments/A1/a1_part1.py b/assignments/A1/a1_part1.py index 7202a13..b90fa25 100644 --- a/assignments/A1/a1_part1.py +++ b/assignments/A1/a1_part1.py @@ -167,7 +167,6 @@ class CountLinkedList(LinkedList): cur = cur.next cur.next = _CountNode(item, cur.next, count) # insert node - print(count) return True def append(self, item: Any) -> None: From 9fdafdfae90e85ced5a44bcda727f949cc25ea58 Mon Sep 17 00:00:00 2001 From: MstrPikachu <31784486+MstrPikachu@users.noreply.github.com> Date: Sun, 30 Jan 2022 22:47:14 -0500 Subject: [PATCH 4/4] A1 P1 Q2 Fix all pyta errors except forbidden import --- assignments/A1/a1_part1_test.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/assignments/A1/a1_part1_test.py b/assignments/A1/a1_part1_test.py index f763189..27db764 100644 --- a/assignments/A1/a1_part1_test.py +++ b/assignments/A1/a1_part1_test.py @@ -29,37 +29,43 @@ from a1_part1 import MoveToFrontLinkedList, SwapLinkedList, CountLinkedList def test_movetofront_true() -> None: + """Test MoveToFrontLinkedList contains with elements in the linked list""" lst = [MoveToFrontLinkedList(range(i)) for i in range(100)] assert(all(all(x in lst[i] for x in range(i)) for i in range(100))) def test_swap_true() -> None: + """Test SwapLinkedList contains with elements in the linked list""" lst = [SwapLinkedList(range(i)) for i in range(100)] assert(all(all(x in lst[i] for x in range(i)) for i in range(100))) def test_count_true() -> None: + """Test CountLinkedList contains with elements in the linked list""" lst = [CountLinkedList(range(i)) for i in range(100)] assert(all(all(x in lst[i] for x in range(i)) for i in range(100))) def test_movetofront_false() -> None: + """Test MoveToFrontLinkedList contains with elements not in the linked list""" lst = [MoveToFrontLinkedList(range(i)) for i in range(100)] assert(all(all(x not in lst[i] for x in range(i, i + 100)) for i in range(100))) def test_swap_false() -> None: + """Test SwapLinkedList contains with elements not in the linked list""" lst = [SwapLinkedList(range(i)) for i in range(100)] assert(all(all(x not in lst[i] for x in range(i, i + 100)) for i in range(100))) def test_count_false() -> None: + """Test CountLinkedList contains with elements not in the linked list""" lst = [CountLinkedList(range(i)) for i in range(100)] assert(all(all(x not in lst[i] for x in range(i, i + 100)) for i in range(100))) -def generate_ordering(n: int): - """generates a pseudorandom ordering of n integers from 0 to n-1, inclusive +def generate_ordering(n: int) -> list[int]: + """Generates a pseudorandom ordering of n integers from 0 to n-1, inclusive Preconditions: - n is prime @@ -71,7 +77,7 @@ def generate_ordering(n: int): def test_movetofront_mutation() -> None: - """Tests the mutation of a MoveToFrontLinkedList by searching for every number from 0-100 inclusive.""" + """Tests the mutation of a MoveToFrontLinkedList by searching for every number from 0-100.""" lst = MoveToFrontLinkedList(range(101)) arr = list(range(101)) operations = generate_ordering(101) @@ -80,13 +86,13 @@ def test_movetofront_mutation() -> None: for x in operations: i = arr.index(x) # find index arr.insert(0, arr.pop(i)) # simulate moving to front - x in lst + lst.__contains__(x) bools.append(lst.to_list() == arr) assert all(bools) def test_swap_mutation() -> None: - """Tests the mutation of a SwapLinkedList by searching for every number from 0-100 inclusive.""" + """Tests the mutation of a SwapLinkedList by searching for every number from 0-100.""" lst = SwapLinkedList(range(101)) arr = list(range(101)) operations = generate_ordering(101) @@ -98,21 +104,21 @@ def test_swap_mutation() -> None: temp = arr[i] arr[i] = arr[i - 1] arr[i - 1] = temp - x in lst + lst.__contains__(x) bools.append(lst.to_list() == arr) assert all(bools) def test_count_mutation() -> None: - """Tests the mutation of a CountLinkedList by searching for every number from 0-100 inclusive, and then - every number from 0-49 inclusive.""" + """Tests the mutation of a CountLinkedList by searching for every number from 0-100, + and then every number from 0-49 inclusive.""" lst = CountLinkedList(generate_ordering(101)) for x in list(range(101)): # reorder the linked list to natural ordering - x in lst + lst.__contains__(x) for x in reversed(range(50)): # reverse the first 50 numbers - x in lst + lst.__contains__(x) assert lst.to_list() == list(reversed(range(50))) + list(range(50, 101)) @@ -126,4 +132,4 @@ if __name__ == '__main__': 'disable': ['E1136'], 'extra-imports': ['a1_linked_list'], 'max-nested-blocks': 4 - }, output='pyta_report.html') + })