I knew Fisher-Yates going in, so the code part was fine.
Start by describing the Fisher-Yates shuffle algorithm: iterate from the last index down to 1, and for each index i, swap the element at i with the element at a randomly chosen index j in [0, i]. Then prove uniformity by induction, showing that each element is equally likely to end up in each position, and state that the algorithm runs in O(n) time and O(1) extra space.
Pro tip: Mention that the naive approach of swapping each element with a random index in [0, n-1] produces a biased shuffle, and emphasize that the Fisher-Yates algorithm is optimal and widely used in practice (e.g., in Python's random.shuffle).
Confirm that the array has distinct elements, the shuffle must be in-place, and every permutation must be equally likely. Note that rand_int(l, r) is inclusive and uniform.
Explain the Fisher-Yates shuffle: for i from n-1 down to 1, pick j = rand_int(0, i) and swap arr[i] and arr[j]. Alternatively, iterate forward from 0 to n-2, picking j in [i, n-1].
Use induction: after step i, the suffix arr[i..n-1] is a uniformly random permutation of the original elements that end up there. Show that each element has probability 1/n to be placed at each position.
Time complexity is O(n) because we perform n-1 swaps, each O(1). Space complexity is O(1) extra space since we shuffle in-place.
Mention handling of empty or single-element arrays, and contrast with the biased naive shuffle. Optionally, note that the algorithm can be adapted for other random APIs.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.