Skip to content

← Batching and the Data Path step 5 of 5

Medium Primitives

Shuffle the same way twice

Jump to the task

A random generator has state. Drawing a permutation advances that state, so calling randperm twice on one generator does not restart the sequence. To repeat a shuffle, start each call from the same generator state:

import torch

n = 6
g1 = torch.Generator().manual_seed(42)
first = torch.randperm(n, generator=g1)
_ = torch.rand(10)  # Draws from the default CPU generator, not g1.
g2 = torch.Generator().manual_seed(42)
second = torch.randperm(n, generator=g2)
print(torch.equal(first, second))  # True

This keeps the shuffle independent of unrelated default-generator draws. It does not make the generator immutable: any code given g1 can advance it.

Seeds, state and call order

Global seeding can reproduce a sequence when the operations and their order stay the same. An extra draw from the same generator changes its state and can change subsequent results. PyTorch has separate default generators for CPU and accelerator devices; not every random operation uses one shared stream. Python and NumPy also have their own random state.

An explicit generator lets you control which operations share a stream. For the same n and compatible runtime, fresh CPU generators initialized with the same seed produce the same permutation. Reusing one generator without resetting it gives successive draws, which can occasionally coincide. Exact sequences are not guaranteed across PyTorch releases or devices.

Data loading and numerical determinism

DataLoader's generator can control its random sampler and the base seeds used for multiprocessing workers. DataLoader seeds workers automatically; worker_init_fn lets you initialize additional random sources used by your own dataset code. Those sources and worker lifetime also matter for reproducibility. Seeding the sampler alone does not control every possible augmentation library.

Random draws are only part of repeatability. Some operations can produce different numerical results because their execution order varies. torch.use_deterministic_algorithms(True) selects deterministic alternatives for supported operations and raises for known unsupported cases by default. It is useful, but is not a guarantee that a whole program reproduces across different hardware, library versions or external data sources.

Your task

def shuffles(n: int, seed: int) -> dict

Produce two CPU permutations of the integers from zero through n-1 using fresh generators with the supplied seed. Keep the starter's unrelated global random draws. Return the permutations as lists under first and second, and their equality as the boolean agree.

The starter reseeds the global generator before its second shuffle, then draws one random value before calling randperm. That extra draw changes the starting state for the second permutation. The permutations may still match by chance; for n=1 there is only one possible order.

The harness checks that both lists are valid permutations and that they actually agree. It does not require a particular order, which avoids tying the expected output to one PyTorch version's random sequence.