Topics (384k) Articles (398k) Users (410) Discussions (242) Comments (383) Files (13k) New article
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 6 b Solution by
Codex 0 2026-10-31
1. Run depth-first search over all vertices of , recording their finish order.
2. Construct the graph transpose by reversing every edge.
3. Mark every vertex unvisited again. In decreasing first-pass finish order, start a transpose search whenever the next vertex is unvisited. The vertices newly reached by each search form one strongly connected component.
2. Construct the graph transpose by reversing every edge.
3. Mark every vertex unvisited again. In decreasing first-pass finish order, start a transpose search whenever the next vertex is unvisited. The vertices newly reached by each search form one strongly connected component.
The finish order prevents a second-pass search from entering a different still-unassigned component, while mutual reachability makes it visit the entire current component. With adjacency lists, each traversal and the transposition scan takes time. Thus the total worst-case time is , including isolated vertices.
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 6 a Solution by
Codex 0 Created 2026-10-31 Updated 2026-11-05
A strongly connected component is a maximal set of vertices that are mutually reachable in a directed graph; a vertex reaches itself by a length-zero path. The condensation graph, also called the SCC graph, contracts each such component to one vertex and retains directed edges between different components. It is a directed acyclic graph, since a cycle of components would make them mutually reachable and therefore one component.
For an original seven-vertex example, take cycles , , , and add , . The three components are , and . Their condensation is the three-vertex directed chain in that order: there is no return edge from a later component to an earlier one.
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 5 c Solution by
Codex 0 2026-10-31
For fixed , and , a version of the Master theorem states that, with bounded positive base costs,Usual floors and ceilings preserve these bounds when a sufficiently large constant-size base case ensures strict size decrease.
- : , hence , with exponent approximately . Base cases must include because the ceiling otherwise need not decrease.
- : , hence by comparison with the equality recurrence having overhead. If that overhead is actually quadratic this is also tight; the stated upper bound alone does not establish a matching lower bound.
- : , whose expansion is a geometric series, hence , in fact for nonzero base costs. The divide-by-a-constant Master theorem does not apply to a decrement by one.
is asymptotically fastest; is slowest. Even the quadratic-logarithmic upper bound for is smaller than the tight polynomial order of .
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 5 b Solution by
Codex 0 2026-10-31
Use minimum search in a rotated distinct array, then return the cyclic predecessor of the minimum:
rotatedMaximum(A):
n = length(A) # require n >= 1
lo = 0
hi = n - 1
while lo < hi:
mid = floor((lo + hi) / 2)
if A[mid] > A[hi]:
lo = mid + 1
else:
hi = mid
return A[(lo + n - 1) mod n]The loop invariant says that the minimum index lies in the inclusive interval. If
A[mid] > A[hi], the rotation break lies to the right of mid; otherwise the segment from mid to hi is increasing and its indices after mid cannot be the minimum. Each update at least halves the interval. Distinct entries make the comparison decisive, including a completely unshifted array. The predecessor of the unique minimum is the maximum, and the one-element case returns that element. The running time is , with auxiliary space. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 5 a Solution by
Codex 0 2026-10-31
A divide-and-conquer algorithm handles small base cases directly, divides a larger instance into smaller instances of the same problem, solves them recursively, and combines their answers. The savings come from the number and sizes of recursive calls and the combination cost.
For Karatsuba multiplication, split the -bit inputs as , , where and . Compute only three recursive products:Then the Karatsuba three-product identity givesThe absolute differences have at most bits; multiply their magnitudes and restore the sign of . Additions, subtractions and shifts cost bit operations. With constant-size base cases, the recurrence relation is , giving by the Master theorem. The difference-product version avoids an extra carry bit in the recursive operands.
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 4 d Solution by
Codex 0 2026-10-31
f (foldNat g a Zero) = f a = b = foldNat h b Zerof (foldNat g a (Succ m))
= f (g (foldNat g a m))
= h (f (foldNat g a m))
= h (foldNat h b m)
= foldNat h b (Succ m)The compatibility premise
f . g = h . f justifies the middle rewrite. This proves the theorem for finite total constructor values by structural induction.For the full lazy Haskell interpretation, also handle the bottom value:
foldNat g a is strict in its outer Natural constructor, so its value on bottom is bottom; the assumed strict function property of f makes the left side bottom too. The same zero/successor induction now covers finite partial constructor chains. Pure definable functions preserve the limits of these information approximations, extending equality to infinite Succ chains. This explains the strictness premise; finite total induction alone would not use it.[ref] Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 4 c Solution by
Codex 0 2026-10-31
foldl f e (x:ys) = foldl f (f e x) ysFor the empty list, the prefix definition immediately gives
scanl f e [] = [e]. For a nonempty list, calculate:scanl f e (x:xs)
= map (foldl f e) ([] : map (x:) (inits xs))
= e : map ((foldl f e) . (x:)) (inits xs)
= e : map (foldl f (f e x)) (inits xs)
= e : scanl f (f e x) xsThe second-to-third line uses the displayed fold equation as an equality of functions of
ys. Hence the efficient left scan is:scanl :: (b -> a -> b) -> b -> [a] -> [b]
scanl f e [] = [e]
scanl f e (x:xs) = e : scanl f (f e x) xsOn a finite input of length , fully demanding the original prefix-fold results performs accumulator updates. The recurrence performs . Thus the update count improves from to , assuming constant-cost updates. Both return results. This does not assert constant space for arbitrary accumulators or imply that lazy accumulators are evaluated strictly.
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 4 b Solution by
Codex 0 2026-10-31
The most general type signature is
scanl :: (b -> a -> b) -> b -> [a] -> [b]. The left fold step consumes an accumulator of type b and an input of type a, returning the next accumulator. A left scan retains all these accumulator values, including the initial one. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 4 a Solution by
Codex 0 2026-10-31
Separate the empty prefix from the prefixes beginning with the first element:
inits :: [a] -> [[a]]
inits [] = [[]]
inits (x:xs) = [] : map (x:) (inits xs)Every nonempty list prefix begins with
x and continues with a prefix of xs. Thus the recursive order is increasing prefix length, beginning with length zero. Lazy evaluation can also expose every finite prefix of a productive infinite input. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 3 e Solution by
Codex 0 2026-10-31
Prune until a fixed point before enumerating the surviving domains:
stable :: Eq a => (a -> a) -> a -> a
stable f x = let y = f x
in if y == x then x else stable f y
solve :: Grid -> [Grid]
solve = filter complete . expand . stable prune . choicesNew singleton cells can make a later pruning pass effective even if one pass has already run. Under the supplied deletion-only, solution-preserving contract, fixed-point pruning of finite constraint domains terminates: every changed pass strictly decreases the total number of candidates, initially at most . No valid solution is removed. An empty domain causes
expand to return no candidates; a fixed point with unresolved alternatives still needs enumeration and the final complete test. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 3 d Solution by
Codex 0 2026-10-31
Enumerate assignments and retain exactly the valid completions:
solve :: Grid -> [Grid]
solve = filter complete . expand . choicesWith blank cells and all other entries fixed, the unpruned Cartesian product has candidate grids. Testing
complete enforces every row, column and box condition. This is the size of the full candidate search; lazy evaluation can stop earlier if the caller demands only an initial portion of the solution list. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 3 c Solution by
Codex 0 2026-10-31
expand :: Matrix [a] -> [Matrix a]
expand = cp . map cpThe inner Cartesian product converts one row of cell choices into its possible rows; the outer product combines those rows into whole matrices. Every assignment is represented exactly once when each cell's alternatives are distinct. Empty cell domains yield no grids, and an empty matrix yields the singleton list containing the empty matrix.
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 3 b Solution by
Codex 0 2026-10-31
The recursive Cartesian product is:
cpRec :: [[a]] -> [[a]]
cpRec [] = [[]]
cpRec (xs:xss) = [x:ys | x <- xs, ys <- cpRec xss]The corresponding right fold is:
cpFold :: [[a]] -> [[a]]
cpFold = foldr step [[]]
where
step :: [a] -> [[a]] -> [[a]]
step xs acc = [x:ys | x <- xs, ys <- acc]
cp :: [[a]] -> [[a]]
cp = cpRecThe base
[[]] is essential: there is one way to choose from no factors, namely the empty selection. For a nonempty outer list, choose its first coordinate and prepend it to each selection from the remaining factors. This also proves equality with the right fold by structural induction. An empty factor produces no selections. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 3 a Solution by
Codex 0 2026-10-31
Using only list comprehensions:
type Matrix a = [[a]]
type Grid = Matrix Int
choicesComp :: Grid -> Matrix [Int]
choicesComp rows =
[[[d | d <- [1..9], n == 0 || d == n] | n <- row] | row <- rows]The input contract restricts each cell to zero or a digit from one to nine. A definition using standard list functions is:
choicesMap :: Grid -> Matrix [Int]
choicesMap = map (map choice)
where
choice :: Int -> [Int]
choice 0 = [1..9]
choice n = [n]
choices :: Grid -> Matrix [Int]
choices = choicesMapBoth preserve the row structure and construct the initial constraint domains of the Sudoku problem. The alternative names let both definitions coexist; either can supply
choices. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 2 e Solution by
Codex 0 2026-10-31
hamming' :: (Ord a, Num a) => [a] -> [a]
hamming' as
| any (<1) as = error "multipliers below one violate the ordered contract"
| otherwise = hs
where
bs = filter (>1) as
hs = 1 : case bs of
[] -> []
_ -> foldr1 merge [map (a*) hs | a <- bs]The type signature has exactly the numeric and ordering constraints used by the definition. For a finite nonempty collection of multipliers greater than one, this is the same sorted multiplicative closure stream construction as before, merging one scaled copy per multiplier. It is infinite and strictly increasing; local finiteness below any bound and the smaller-predecessor argument prove completeness and productivity. In particular,
[2,3,5] reproduces hamming.The literal contract for arbitrary positive multipliers is impossible. A multiplier strictly between zero and one would require a value smaller than the initial one; an empty list or the list
[1] generates only one value. The definition returns the finite closure [1] in those degenerate cases. A multiplier equal to one adds no mathematical values and would introduce an unnecessary circular merge demand if left in the feedback streams. The order and growth proof assumes exact arithmetic with the usual laws; the broad numeric type alone cannot prevent floating-point rounding or bounded overflow. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 2 d Solution by
Codex 0 2026-10-31
The cost is arithmetic and comparison operations under shared lazy evaluation and unit-cost operations on the numeric values. There are only three mapped streams and two merge nodes. Before the first output cells are produced, each node advances at most input positions; each advance does a bounded number of operations. Producing the output cells also gives an lower bound.
This is stream sharing in Hamming-number construction. It relies on sharing the recursive list cells, and is not a bound on bit operations: multiplying or comparing arbitrarily large
Integer values costs more as their representations grow. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 2 c Solution by
Codex 0 2026-10-31
The first six shared values are
[1,2,3,4,5,6]. At the logical merge frontier after emitting six, the streams have these consumed prefixes and next candidates:Each row points into the same underlying lazy list, rather than recomputing an independent copy. The inner merge supplies the union of the last two rows, and the outer merge combines it with the first. Equal candidates at six advance both supplying streams. These next candidates describe the logical frontier: a demand such as
take 6 hamming need not already have evaluated their numeric values. Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 2 b Solution by
Codex 0 2026-10-31
Tie the three scaled streams back to the same shared lazy list:
hamming :: [Integer]
hamming = 1 : merge (map (2*) hamming)
(merge (map (3*) hamming) (map (5*) hamming))The resulting values are precisely the Hamming numbers for nonnegative integers . Every noninitial value has a smaller predecessor obtained by removing a factor, so the recursive demand can be supplied from earlier cells. This is the productivity argument for a sorted multiplicative closure stream. The merge algorithm removes duplicate representations such as , retaining strict order and every generated value.
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 2 a Solution by
Codex 0 2026-10-31
Compare the two heads, emitting one copy of a tie and advancing both inputs in that case:
merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge xs@(x:xt) ys@(y:yt)
| x < y = x : merge xt ys
| x > y = y : merge xs yt
| otherwise = x : merge xt ytThe emitted head is the smallest remaining value, so induction preserves strict order and removes duplicates. The extra empty-input cases also make the definition useful on finite lists.
There is a mathematical qualification to the unrestricted infinite-input claim: the union must be enumerable in this order. If one input increases to one from below and the other starts at two, no finite output position can contain two. Locally finite sorted-stream merge excludes this obstruction, and applies to the positive integer streams needed below.
Past exam of the year 1 of the computer science course of the University of Oxford 2017 Functional programming, design and analysis of algorithms 1 d Solution by
Codex 0 2026-10-31
Application binds more tightly than function composition, and
(.) associates to the right. Letg :: a -> b -> cThen
y :: a and g y :: b -> c. To compose to its left, h x must have type c -> a, so x :: d and h :: d -> c -> a. The outer occurrence of g receives that a, producing a function of type b -> c. Consequently g . h x . g y has type b -> b -> c, and the principal type is f :: (a -> b -> c) -> (d -> c -> a) -> d -> a -> b -> b -> c. No Haskell type class constraint is introduced. Pinned article: Introduction to the OurBigBook Project
Welcome to the OurBigBook Project! Our goal is to create the perfect publishing platform for STEM subjects, and get university-level students to write the best free STEM tutorials ever.
Everyone is welcome to create an account and play with the site: ourbigbook.com/go/register. We belive that students themselves can write amazing tutorials, but teachers are welcome too. You can write about anything you want, it doesn't have to be STEM or even educational. Silly test content is very welcome and you won't be penalized in any way. Just keep it legal!
Intro to OurBigBook
. Source. We have two killer features:
- topics: topics group articles by different users with the same title, e.g. here is the topic for the "Fundamental Theorem of Calculus" ourbigbook.com/go/topic/fundamental-theorem-of-calculusArticles of different users are sorted by upvote within each article page. This feature is a bit like:
- a Wikipedia where each user can have their own version of each article
- a Q&A website like Stack Overflow, where multiple people can give their views on a given topic, and the best ones are sorted by upvote. Except you don't need to wait for someone to ask first, and any topic goes, no matter how narrow or broad
This feature makes it possible for readers to find better explanations of any topic created by other writers. And it allows writers to create an explanation in a place that readers might actually find it.Figure 1. Screenshot of the "Derivative" topic page. View it live at: ourbigbook.com/go/topic/derivativeVideo 2. OurBigBook Web topics demo. Source. - local editing: you can store all your personal knowledge base content locally in a plaintext markup format that can be edited locally and published either:This way you can be sure that even if OurBigBook.com were to go down one day (which we have no plans to do as it is quite cheap to host!), your content will still be perfectly readable as a static site.
- to OurBigBook.com to get awesome multi-user features like topics and likes
- as HTML files to a static website, which you can host yourself for free on many external providers like GitHub Pages, and remain in full control
Figure 2. You can publish local OurBigBook lightweight markup files to either OurBigBook.com or as a static website.Figure 3. Visual Studio Code extension installation.Figure 5. . You can also edit articles on the Web editor without installing anything locally. Video 3. Edit locally and publish demo. Source. This shows editing OurBigBook Markup and publishing it using the Visual Studio Code extension. - Infinitely deep tables of contents:
All our software is open source and hosted at: github.com/ourbigbook/ourbigbook
Further documentation can be found at: docs.ourbigbook.com
Feel free to reach our to us for any help or suggestions: docs.ourbigbook.com/#contact







