stouputils.collections.shuffle module#

affine_permutation_generator(
n: int,
seed: int = 0,
) Generator[int][source]#

Generate a memory-efficient pseudo-random permutation of [0, n).

Each i in [0, n) can only be visited once thanks to the affine bijection: (a*i + b) % n where a and b are random integers and coprime with n.

Parameters:
  • n (int) – Number of elements to visit.

  • seed (int) – Random seed for reproducibility.

Yields:

Generator[int] – A random permutation of [0, n).

>>> list(affine_permutation_generator(10))
[6, 3, 0, 7, 4, 1, 8, 5, 2, 9]
>>> list(affine_permutation_generator(10, seed=42))
[4, 5, 6, 7, 8, 9, 0, 1, 2, 3]

Make sure all elements are visited:

>>> sorted(list(affine_permutation_generator(100, seed=42))) == list(range(100))
True

We can have infinite permutations without blowing up memory:

>>> for i, perm in enumerate(affine_permutation_generator(100**2000)):
...     if i > 4: break
...     print(f"Permutation {i}: {str(perm)[:20]}...")
Permutation 0: 60923891346657165714...
Permutation 1: 77383182119683752460...
Permutation 2: 93842472892710339206...
Permutation 3: 10301763665736925951...
Permutation 4: 26761054438763512697...
feistel_permutation_generator(
n: int,
seed: int = 0,
) Generator[int][source]#

Generate a memory-efficient pseudo-random permutation of [0, n).

This uses a Feistel network to build a bijective mapping over a power-of-two domain, then filters values outside [0, n).

Unlike affine permutations, this produces a much more “shuffle-like” order with significantly reduced algebraic structure.

Parameters:
  • n (int) – Number of elements to visit.

  • seed (int) – Random seed for reproducibility.

Yields:

Generator[int] – A pseudo-random permutation of [0, n).

>>> list(feistel_permutation_generator(10))
[1, 8, 0, 5, 3, 6, 9, 7, 4, 2]
>>> list(feistel_permutation_generator(10, seed=42))
[5, 3, 1, 6, 4, 2, 8, 0, 9, 7]

Make sure all elements are visited:

>>> sorted(list(feistel_permutation_generator(100, seed=42))) == list(range(100))
True

We can have infinite permutations without blowing up memory:

>>> for i, perm in enumerate(feistel_permutation_generator(100**2000)):
...     if i > 4: break
...     print(f"Permutation {i}: {str(perm)[:20]}...")
Permutation 0: 56877730339930604433...
Permutation 1: 31140272880303506593...
Permutation 2: 89677538354424247341...
Permutation 3: 94477358131828883980...
Permutation 4: 37089694092416945146...
class FeistelHelpers[source]#

Bases: object

Helper functions for the Feistel permutation generator.

static round_prf(
seed: int,
round_no: int,
value: int,
out_bits: int,
) int[source]#

Small deterministic Pseudo-Random Function (PRF) used inside the Feistel network.

Parameters:
  • seed (int) – Seed for the PRF.

  • round_no (int) – Round number (0-3).

  • value (int) – Input value.

  • out_bits (int) – Output bit-width.

Returns:

The PRF output.

Return type:

int

static permute(
x: int,
seed: int,
bits: int,
rounds: int = 4,
) int[source]#

Feistel permutation over a fixed bit-width domain.

Parameters:
  • x (int) – Input value.

  • seed (int) – Random seed for the permutation.

  • bits (int) – Bit-width of the domain.

  • rounds (int) – Number of rounds to apply (default: 4).

Returns:

Permuted value.

Return type:

int