| from __future__ import annotations |
|
|
| import hashlib |
| import math |
|
|
|
|
| def _u64(seed: bytes, counter: int) -> int: |
| h = hashlib.blake2b(digest_size=8) |
| h.update(seed) |
| h.update(counter.to_bytes(8, "little", signed=False)) |
| return int.from_bytes(h.digest(), "little", signed=False) |
|
|
|
|
| def uniform01(seed: bytes, counter: int) -> float: |
| """ |
| Deterministic uniform float in (0, 1), derived from (seed, counter). |
| """ |
| |
| x = _u64(seed, counter) |
| u = (x + 1.0) / (2**64 + 1.0) |
| |
| if u <= 0.0: |
| return 1.0 / (2**64 + 1.0) |
| if u >= 1.0: |
| return 1.0 - (1.0 / (2**64 + 1.0)) |
| return float(u) |
|
|
|
|
| def uniform(seed: bytes, counter: int, low: float, high: float) -> float: |
| return low + (high - low) * uniform01(seed, counter) |
|
|
|
|
| def normal(seed: bytes, counter: int, mean: float = 0.0, std: float = 1.0) -> float: |
| """ |
| Deterministic normal via Box-Muller transform. |
| |
| Uses counters (counter, counter+1) internally. |
| """ |
| u1 = uniform01(seed, counter) |
| u2 = uniform01(seed, counter + 1) |
| r = math.sqrt(-2.0 * math.log(u1)) |
| theta = 2.0 * math.pi * u2 |
| z0 = r * math.cos(theta) |
| return mean + std * z0 |
|
|
|
|
| def uint31(seed: bytes, counter: int) -> int: |
| return int(_u64(seed, counter) & 0x7FFF_FFFF) |
|
|