Initial Commit
This commit is contained in:
51
packages/foundation/random/pseudo-random/src/utils/sample.md
Normal file
51
packages/foundation/random/pseudo-random/src/utils/sample.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# utils/sample
|
||||
|
||||
## Purpose
|
||||
|
||||
The `sample` module covers the two sampling primitives that sit above `shuffle`: unique-without-replacement `sample` for drawing `k` items from `n`, and `weightedChoice` for proportional index selection. Both accept any `Rng` and throw typed `PseudoRandomError`s on invalid input.
|
||||
|
||||
These primitives are intentionally small and allocation-frugal: `sample` reuses `shuffle` then slices, so we never allocate auxiliary structures beyond the shuffled copy; `weightedChoice` walks the weights once to compute the total, then once more to find the tipping point.
|
||||
|
||||
## Features
|
||||
|
||||
- `sample(input, count, rng)` — unique draw without replacement
|
||||
- `weightedChoice(weights, rng)` — proportional index draw; rejects negative, NaN or all-zero weights
|
||||
- Both deterministic for equal seeds + equal RNG class
|
||||
- Strict input validation (positive integer counts, finite non-negative weights)
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
|
||||
import { sample, weightedChoice } from "@suaveplan/pseudo-random/utils";
|
||||
|
||||
const rng = new Mulberry32Rng(1);
|
||||
const hand = sample(["A", "K", "Q", "J", "10"], 3, rng);
|
||||
const bucket = weightedChoice([1, 2, 4], rng); // 0, 1 or 2
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { Sfc32Rng } from "@suaveplan/pseudo-random";
|
||||
import { weightedChoice } from "@suaveplan/pseudo-random/utils";
|
||||
|
||||
type Drop = { id: string; weight: number };
|
||||
const drops: Drop[] = [
|
||||
{ id: "common", weight: 70 },
|
||||
{ id: "rare", weight: 25 },
|
||||
{ id: "epic", weight: 5 },
|
||||
];
|
||||
const rng = new Sfc32Rng("loot-table");
|
||||
const idx = weightedChoice(drops.map((d) => d.weight), rng);
|
||||
const chosen = drops[idx];
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `sample<T>(input, count, rng)` — `PseudoRandomError.ARG.NEGATIVE_COUNT` or `ARG.INVALID` on bad count; `count === 0` returns `[]`.
|
||||
- `weightedChoice(weights, rng)` — `ARG.EMPTY_ARRAY` on empty input; `ARG.WEIGHTS_INVALID` on negative, NaN or all-zero weights.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`weightedChoice` draws `rng.nextFloat()` (a 32-bit uniform in `[0, 1)`), multiplies by the total weight, and walks the cumulative sum. The final fallback (`return weights.length - 1`) guards against floating-point rounding that would otherwise fail to find a bucket when the target sits exactly on the total.
|
||||
Reference in New Issue
Block a user