A statistics notebook runs many small simulations. Each should be reproducible, so it needs a fixed seed, but the notebook's own random numbers must carry on as if the simulation had never happened. Write a context manager seeded(seed=None) for use in a with block:
- On entering, it remembers the current state of the global
randomgenerator. Ifseedis given, it then seeds the generator with it, so the block draws the same numbers every time. - If
seedisNone, it does not reseed: the block simply continues the current sequence. - On leaving the block, however it ends, the generator is put back into exactly the state it had on entering. An exception raised in the block still reaches the caller.
So with seeded(7): ... is a reproducible experiment, and with seeded(): ... is a sandbox whose draws do not shift anything that comes after it. Blocks may be nested.
Setup helpers that use seeded inside with blocks, available with Run: dice_rolls(seed, n), high_total_share(trials) (a Monte Carlo estimate of the chance that two dice total at least 10), estimate_twice(seed, trials), failing_simulation(seed) (raises inside the block on purpose), sandbox(n) and nested().
Examples
Input: dice_rolls(42, 5)
Output: ([6, 1, 1, 6, 3], True)
Input: failing_simulation(3)
Output: ("sensor offline", True)
Constraints
- Use the global generator's
random.getstate(),random.setstate(state)andrandom.seed(seed). 0 <= seed < 10**9. No result depends on the clock.
Goals
- Write a context manager with `contextlib.contextmanager`
- Restore saved state in a `finally`, so it is restored even when the block fails
- Make a random simulation reproducible without disturbing the rest of the program