diff options
| author | Arnaud Bailly <arnaud@pankzsoft.com> | 2026-08-31 16:27:17 +0200 |
|---|---|---|
| committer | Arnaud Bailly <arnaud@pankzsoft.com> | 2026-09-01 07:59:01 +0200 |
| commit | 19c352276211106884078495a2244fe454f92cbe (patch) | |
| tree | 915d36b906acbde5b5fb29e48a647590b2dca91e /pbt/ts/src/gen.ts | |
| parent | 32f9be77d18d3ad0cb86e7290c6df7c51ab99ca5 (diff) | |
| download | lambda-nantes-19c352276211106884078495a2244fe454f92cbe.tar.gz | |
Fix test for uniformity of distribution for generation of integers
This lead me into a rabbit hole to understand how one should assert
the shape of the distribution of some empirical data w.r.t. to the
"true" expected distribution which uses the Pearson coefficient
test. Unfortunately for very large number of samples deviations sum up
quickly to a point where the computed p-value is basically 0, thus
rejecting the null hypothesis.
While this is theoretically true, there are a lot of ways to introduce
biases that will lead to rejection, one of them being the use of
modulus to select numbers from the generator's output!
Diffstat (limited to 'pbt/ts/src/gen.ts')
| -rw-r--r-- | pbt/ts/src/gen.ts | 59 |
1 files changed, 58 insertions, 1 deletions
diff --git a/pbt/ts/src/gen.ts b/pbt/ts/src/gen.ts index 531a118..c27cfec 100644 --- a/pbt/ts/src/gen.ts +++ b/pbt/ts/src/gen.ts @@ -1,4 +1,4 @@ -import {Option, none, some} from 'fp-ts/lib/Option.js'; +import { Option, none, some } from 'fp-ts/lib/Option.js'; import Prando from 'prando'; // a shrinkable generator needs to maintain some state, @@ -71,3 +71,60 @@ export class ReplayBytes implements GenBytes { return chunk; } } + +class GenInt implements Gen<number> { + private bytes: BytesGen; + + constructor(seed: number | undefined = undefined) { + this.bytes = new BytesGen(new Prando(seed ? seed : Math.random() * 1000)); + } + + generate(): number { + const bytes = this.bytes.generate(1); + return bytes[0]; + } + + generateN(n: number): number[] { + const result = []; + for (let i = 0; i < n; i++) { + result.push(this.bytes.generate(1)[0]); + } + + return result; + } +} + +class GenIntWithin implements Gen<number> { + private bytes: BytesGen; + + constructor(private min: number, private max: number, seed: number | undefined = undefined) { + this.bytes = new BytesGen(new Prando(seed ? seed : Math.random() * 1000000000)); + this.min = min; + this.max = max; + } + + generate(): number { + const range = this.max - this.min; + const numBytes = Math.ceil(Math.log2(range) / 8); + const bytes = this.bytes.generate(numBytes); + let value = 0; + for (let i = 0; i < bytes.length; i++) { + value = (value << 8) | bytes[i]; + } + return this.min + (value % range); + } + + generateN(n: number): number[] { + const result = []; + for (let i = 0; i < n; i++) { + result.push(this.bytes.generate(1)[0]); + } + + return result; + } + +} + +export const genInt = (seed : number | undefined = undefined) => new GenInt(seed); + +export const genIntWithin = (min: number, max: number, seed : number | undefined = undefined) => new GenIntWithin(min, max, seed); |
