One train/test split gives you one score, and that score depends on which examples happened to land in the test set. K-fold cross-validation removes that luck: chop the data into k equal blocks, then run k experiments, each using a different block as the test set and everything else for training. Every example gets tested exactly once.
Task: write k_fold_indices(n, k) returning a list of k pairs, [train_indices, test_indices], for a dataset of n examples numbered 0 to n - 1.
How the blocks are sized and placed:
n rarely divides evenly by k. Spread the remainder over the earliest folds, one extra each: with n = 7 and k = 3 the block sizes are 3, 2, 2.test_indices is its own block and train_indices is everything else, both in ascending order.k is never larger than n, so no fold is ever empty.
The pay-off is that you end up with k scores instead of one. Their average is a far steadier estimate of real performance, and how much they disagree tells you how sensitive your model is to which data it happened to see.