"Foundations of CS Supervision 1 Questions -- Atticus Kuhn"

Written By Atticus Kuhn
Tags: "public", "project"
:PROPERTIES: :ID: 1dbb5e3c-b468-4dd9-99e1-849b897de4b5 :mtime: 20231013112933 20231012032909 20231012001337 20231011020124 20231008091438 20231007034631 20231007001506 20231006231454 :ctime: 20231006231451 :END: #+title: Foundations of CS Supervision 1 Questions -- Atticus Kuhn #+filetags: :public:project: * Source of the Questions The questions can be found at https://www.cl.cam.ac.uk/teaching/2223/FoundsCS/materials.html * 1999 Paper 1 Question 1 ** Question A permutation of a list is any list that can be derived from it by re-arranging the elements. Write an OCaml function that returns the list of all the permutations of its argument. Explain your code clearly and carefully. For example, applied to the list [1; 2; 3], your function should return the list whose elements are [1; 2; 3], [2; 1; 3], [2; 3; 1], [1; 3; 2], [3,1,2] and [3,2,1]. You may assume that the elements of the argument are distinct. The elements of the result may appear in any order ** Solution Define a function called "insertInto", which inserts an element into a list. #+BEGIN_SRC ocaml :exports code let rec insertInto (x :'a) (xs : 'a list) : 'a list list = match xs with | [] -> [[x]] | h :: t -> (x :: xs) :: (List.map (fun x -> h :: x) (insertInto x t)) #+END_SRC #+RESULTS: : <fun> Use the function "insertInto" to recursively insert each element of the list, thus giving us all valid permutations. #+BEGIN_SRC ocaml :exports code let rec permutations (l : 'a list) : 'a list list = match l with | [] -> [[]] | h :: t -> List.concat_map (insertInto h) (permutations t) #+END_SRC #+RESULTS: : <fun> Let us test that "insertInto" has the correct behavior: #+BEGIN_SRC ocaml :exports both insertInto 6 [1 ; 2 ; 3] #+END_SRC #+RESULTS: | 6 | 1 | 2 | 3 | | 1 | 6 | 2 | 3 | | 1 | 2 | 6 | 3 | | 1 | 2 | 3 | 6 | Let us test that "permutations" has the correct behavior: #+BEGIN_SRC ocaml :exports both permutations [1 ; 2 ; 3 ; 4] #+END_SRC #+RESULTS: | 1 | 2 | 3 | 4 | | 2 | 1 | 3 | 4 | | 2 | 3 | 1 | 4 | | 2 | 3 | 4 | 1 | | 1 | 3 | 2 | 4 | | 3 | 1 | 2 | 4 | | 3 | 2 | 1 | 4 | | 3 | 2 | 4 | 1 | | 1 | 3 | 4 | 2 | | 3 | 1 | 4 | 2 | | 3 | 4 | 1 | 2 | | 3 | 4 | 2 | 1 | | 1 | 2 | 4 | 3 | | 2 | 1 | 4 | 3 | | 2 | 4 | 1 | 3 | | 2 | 4 | 3 | 1 | | 1 | 4 | 2 | 3 | | 4 | 1 | 2 | 3 | | 4 | 2 | 1 | 3 | | 4 | 2 | 3 | 1 | | 1 | 4 | 3 | 2 | | 4 | 1 | 3 | 2 | | 4 | 3 | 1 | 2 | | 4 | 3 | 2 | 1 | * 2006 Paper 1 Question 1 ** Question his question has been translated from Standard ML to OCaml Give an example of an OCaml function belonging to each of the following complexity classes: (a) O(1); (b) O(n); (c) O(n log n); (d ) O(n^2); (e) O(2^n). Each answer may contain code fragments (involving well-known functions) rather than self-contained programs, but must include justification. (The upper bound in each case should be reasonably tight.) ** Solution (a) The constant function is O(1) because it does no work. #+BEGIN_SRC ocaml let const x = 1 #+END_SRC (b) Adding one to each element of a list is O(n) because we must access each element of the list. #+BEGIN_SRC ocaml let addOne = List.map (fun x -> x + 1) #+END_SRC (c) Sorting a list using mergesort (This is what the Ocaml standard library uses, I looked in the documentation) is O(n ln n). The reason that mergesort is $O(n \log n)$ is that its work follows the recurrence relation $T(n) = 2T(n / 2) + M(n / 2 , n / 2)$, where $M(a , b) = a + b$. This means that $T(n) = O(n \log n)$. #+BEGIN_SRC ocaml let sort = List.sort #+END_SRC (d) These functions are from the lecture. "nsum" is $O(n)$ because for each of $n$ steps it does constant work. "nsumsum" is $O(n^2)$ because for each of $n$ steps it does linear work. #+BEGIN_SRC ocaml let nsum n = if n = 0 then 0 else n + nsum n - 1 let nsumsum n = if n = 0 then 0 else sum n + nsumsum n - 1 #+END_SRC (e) "slow" is $O(2^n)$ because its work satisfies the recurrence relation $T(n) = 1 + T(n-1) + T(n - 1)$. Let us assume our Ocaml implemenetation has no caching and no memoization. Solving this recurrence gives that $T(n) = O(2^n)$ #+BEGIN_SRC ocaml let rec slow n = if n < 0 then 1 else (slow n - 1) + (slow n - 1) #+END_SRC #+RESULTS: : <fun> * 2008 Paper 1 Question 1 ** Question The list l2 is a sublist of l provided there exist lists l1 and l3 such that l = l1@l2@l3. Write an OCaml function sublist to test whether one list is a sublist of another one. Include a clear explanation of how your code works ** Solution Since Ocaml is not as developed as Haskell, I had to define my own "take" and "drop" functions. #+BEGIN_SRC ocaml let rec drop n xs = match xs with | [] -> [] | h :: t -> if n = 0 then xs else drop (n - 1) t let rec take n xs = match xs with | [] -> [] | h :: t -> if n = 0 then [] else h :: take (n - 1) t #+END_SRC Using "take" and "drop", we can take out a "slice" from a list. #+BEGIN_SRC ocaml let slice list startIndex range = take range (drop startIndex list) #+END_SRC Here are some simple utility functions. "range" generates a list of numbers from 1 to n. "makeSlices" creates all slices of length b out of list a. #+BEGIN_SRC ocaml let range i = List.init i Fun.id let makeSlices a b = List.map (fun i -> slice a i (List.length b)) (range (List.length a - List.length b + 1)) #+END_SRC #+RESULTS: : <fun> And now we can define "isSublist", which checks if one list is a sublist of another list. #+BEGIN_SRC ocaml let isSublist a b = List.find_opt ((=) b) (makeSlices a b) #+END_SRC #+RESULTS: : <fun> #+BEGIN_SRC ocaml :exports both isSublist [1 ; 2 ; 3] [ 2 ; 3] #+END_SRC #+RESULTS: : Some [2, 3]

Leave your Feedback in the Comments Section