A model learns from a small table of examples by going over all of it several times. Each full pass is called an epoch, and before every epoch the rows are put into a fresh random order, so the model never meets them in the same sequence twice running.
X holds the examples, one row of features each, and y holds their labels: y[i] is the answer for row X[i]. Two promises have to hold:
seed must give exactly the same sequence of orders every time the code runs.Task: write shuffled_epochs(X, y, seed, epochs). It returns a list with one entry per epoch, and each entry is a tuple (X_epoch, y_epoch): the rows of X as a list in that epoch's order, and the labels as a list in the same order.
Follow this recipe exactly, so everyone's random numbers come out the same:
rng = random.Random(seed). It is Python's built-in cousin of np.random.default_rng(seed) — a seeded generator object with its own methods. One of them, rng.shuffle(some_list), rearranges that list in place into a random order.rng.shuffle exactly once, on a brand-new list with one item per example, built in the examples' original order (the item for row 0 first, then row 1, and so on). How you use that one shuffle to put both X and y in order is up to you.rng, carrying on from where the previous epoch left off.Don't change X or y themselves — the caller still needs them in their original order. If epochs is 0, return an empty list.
Use Python's
randommodule rather than NumPy here: NumPy's generator produces a different sequence from the same seed, so its orders wouldn't match the tests.