first commit
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# algorithms/counter-rng
|
||||
|
||||
## Purpose
|
||||
|
||||
`CounterRng` is a hash-based PRNG: its output at logical position `i` is `splitmix64(seed XOR i)`. Because the output depends only on position, every seek is constant time — `at(1_000_000)` does not draw a million values, it just advances the counter. This makes `CounterRng` uniquely suited to massively parallel simulations, tile-based procedural generation, and any scenario where you need to sample an arbitrary index of the stream without replaying it.
|
||||
|
||||
## Features
|
||||
|
||||
- O(1) `at(offset)` and `skip(n)` — no per-step iteration cost
|
||||
- 64-bit seed state stored as two uint32 halves for JSON safety
|
||||
- Full `SeededRng` contract
|
||||
- Output quality on par with splitmix64 (passes BigCrush on the upper 32 bits)
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { CounterRng } from "@suaveplan/pseudo-random";
|
||||
|
||||
const rng = new CounterRng("world");
|
||||
const v = rng.nextUint32();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { CounterRng } from "@suaveplan/pseudo-random";
|
||||
|
||||
// Sample tile (1234, 5678) without materialising everything in between.
|
||||
const rng = new CounterRng("map");
|
||||
const tile = rng.at(1234 * 10000 + 5678);
|
||||
const decoration = tile.nextUint32();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `new CounterRng(seed)` — accepts the standard `string | number` seed.
|
||||
- `nextUint32()` / `nextFloat()` — one counter step, output is `splitmix64(seed XOR pos).out >> 32`.
|
||||
- `skip(n)` / `at(offset)` — both constant-time.
|
||||
- `fork(id)` — derives a new counter keyed on `(seed, position, id)`.
|
||||
- `checkpoint()` / `restore(cp)` — state is `[seedLo, seedHi]` plus counter.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Counter generators expose their index directly, so the usual "position = draws so far" semantics hold regardless of whether you reach position 1000 by calling `nextUint32` 1000 times, calling `skip(1000)`, or jumping via `at(1000)`. The `streamAt` helper is deterministic and stateless, which is what makes the O(1) seek possible.
|
||||
Reference in New Issue
Block a user