@byloth/core 2.2.6 → 2.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.esm.js +382 -327
- package/dist/core.esm.js.map +1 -1
- package/dist/core.global.js +1 -1
- package/dist/core.global.js.map +1 -1
- package/dist/core.umd.cjs +1 -1
- package/dist/core.umd.cjs.map +1 -1
- package/package.json +10 -10
- package/src/index.ts +1 -1
- package/src/utils/random.ts +623 -140
package/src/utils/random.ts
CHANGED
|
@@ -2,15 +2,194 @@ import { ValueException } from "../models/index.js";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A wrapper class around the native {@link Math.random} function that
|
|
5
|
-
* provides a set of methods to generate random values more easily.
|
|
5
|
+
* provides a set of methods to generate random values more easily.
|
|
6
6
|
* It can be used to generate random numbers, booleans and other different values.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* The class exposes two coexisting surfaces:
|
|
9
|
+
* - A **static API** (`Random.Integer`, `Random.Boolean`, …) that uses
|
|
10
|
+
* {@link Math.random} and is therefore non-deterministic.
|
|
11
|
+
* - An **instance API** with the same method names that uses a seeded PRNG
|
|
12
|
+
* (Mulberry32) for reproducible sequences. Instances are created via the
|
|
13
|
+
* {@link Random.FromSeed} factory; the constructor is private.
|
|
9
14
|
*/
|
|
10
15
|
export default class Random
|
|
11
16
|
{
|
|
17
|
+
static #Mulberry32(seed: number): () => number
|
|
18
|
+
{
|
|
19
|
+
let state = seed | 0;
|
|
20
|
+
|
|
21
|
+
return () =>
|
|
22
|
+
{
|
|
23
|
+
state = (state + 0x6D2B79F5) | 0;
|
|
24
|
+
|
|
25
|
+
let t = state;
|
|
26
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
27
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
28
|
+
|
|
29
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private static _Boolean(random: () => number, ratio: number): boolean
|
|
34
|
+
{
|
|
35
|
+
return (random() < ratio);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private static _Integer(random: () => number, min: number, max?: number): number
|
|
39
|
+
{
|
|
40
|
+
if (max === undefined) { return Math.floor(random() * min); }
|
|
41
|
+
|
|
42
|
+
return Math.floor(random() * (max - min) + min);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private static _Decimal(random: () => number, min?: number, max?: number): number
|
|
46
|
+
{
|
|
47
|
+
if (min === undefined) { return random(); }
|
|
48
|
+
if (max === undefined) { return (random() * min); }
|
|
49
|
+
|
|
50
|
+
return (random() * (max - min) + min);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private static _Index<T>(random: () => number, elements: readonly T[]): number
|
|
54
|
+
{
|
|
55
|
+
if (elements.length === 0) { throw new ValueException("You must provide at least one element."); }
|
|
56
|
+
|
|
57
|
+
return Random._Integer(random, elements.length);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private static _Choice<T>(random: () => number, elements: readonly T[]): T
|
|
61
|
+
{
|
|
62
|
+
return elements[Random._Index(random, elements)];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private static _Sample<T>(
|
|
66
|
+
random: () => number,
|
|
67
|
+
elements: readonly T[],
|
|
68
|
+
count: number,
|
|
69
|
+
weights?: readonly number[]
|
|
70
|
+
): T[]
|
|
71
|
+
{
|
|
72
|
+
const length = elements.length;
|
|
73
|
+
|
|
74
|
+
if (length === 0) { throw new ValueException("You must provide at least one element."); }
|
|
75
|
+
if (count < 0) { throw new ValueException("Count must be non-negative."); }
|
|
76
|
+
if (count > length) { throw new ValueException("Count cannot exceed the number of elements."); }
|
|
77
|
+
|
|
78
|
+
if (count === 0) { return []; }
|
|
79
|
+
|
|
80
|
+
if (weights === undefined)
|
|
81
|
+
{
|
|
82
|
+
const pool = Array.from(elements);
|
|
83
|
+
const result: T[] = new Array(count);
|
|
84
|
+
|
|
85
|
+
for (let index = 0; index < count; index += 1)
|
|
86
|
+
{
|
|
87
|
+
const randomIndex = Random._Integer(random, index, length);
|
|
88
|
+
|
|
89
|
+
result[index] = pool[randomIndex];
|
|
90
|
+
pool[randomIndex] = pool[index];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (weights.length !== length)
|
|
97
|
+
{
|
|
98
|
+
throw new ValueException("Weights array must have the same length as elements array.");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const keys: ({ index: number, key: number })[] = new Array(length);
|
|
102
|
+
for (let index = 0; index < length; index += 1)
|
|
103
|
+
{
|
|
104
|
+
if (weights[index] <= 0)
|
|
105
|
+
{
|
|
106
|
+
throw new ValueException(`Weight for element #${index} must be greater than zero.`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
keys[index] = { index: index, key: Math.pow(random(), 1 / weights[index]) };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
keys.sort((a, b) => b.key - a.key);
|
|
113
|
+
|
|
114
|
+
const result: T[] = new Array(count);
|
|
115
|
+
for (let index = 0; index < count; index += 1)
|
|
116
|
+
{
|
|
117
|
+
result[index] = elements[keys[index].index];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static #Split(random: () => number, total: number, parts: number): number[]
|
|
124
|
+
{
|
|
125
|
+
const cuts: number[] = new Array(parts - 1);
|
|
126
|
+
for (let index = 0; index < cuts.length; index += 1)
|
|
127
|
+
{
|
|
128
|
+
cuts[index] = random() * total;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
cuts.sort((a, b) => (a - b));
|
|
132
|
+
|
|
133
|
+
const boundaries = [0, ...cuts, total];
|
|
134
|
+
const values: number[] = new Array(parts);
|
|
135
|
+
|
|
136
|
+
for (let index = 0; index < parts; index += 1)
|
|
137
|
+
{
|
|
138
|
+
values[index] = Math.floor(boundaries[index + 1] - boundaries[index]);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let remainder = total - values.reduce((sum, val) => (sum + val), 0);
|
|
142
|
+
while (remainder > 0)
|
|
143
|
+
{
|
|
144
|
+
values[Random._Integer(random, parts)] += 1;
|
|
145
|
+
|
|
146
|
+
remainder -= 1;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return values;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private static _Split<T>(
|
|
153
|
+
random: () => number,
|
|
154
|
+
totalOrElements: number | Iterable<T>,
|
|
155
|
+
parts: number
|
|
156
|
+
): number[] | T[][]
|
|
157
|
+
{
|
|
158
|
+
if (parts < 1) { throw new ValueException("The number of splits must be greater than zero."); }
|
|
159
|
+
|
|
160
|
+
if (typeof totalOrElements === "number")
|
|
161
|
+
{
|
|
162
|
+
if (totalOrElements < 0) { throw new ValueException("The total must be a non-negative number."); }
|
|
163
|
+
|
|
164
|
+
return Random.#Split(random, totalOrElements, parts);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const elements = Array.from(totalOrElements);
|
|
168
|
+
const length = elements.length;
|
|
169
|
+
|
|
170
|
+
if (length === 0) { throw new ValueException("You must provide at least one element."); }
|
|
171
|
+
if (parts > length)
|
|
172
|
+
{
|
|
173
|
+
throw new ValueException("The number of splits cannot exceed the number of elements.");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const sizes = Random.#Split(random, length, parts);
|
|
177
|
+
const groups: T[][] = new Array(parts);
|
|
178
|
+
|
|
179
|
+
let offset = 0;
|
|
180
|
+
for (let index = 0; index < parts; index += 1)
|
|
181
|
+
{
|
|
182
|
+
groups[index] = elements.slice(offset, offset + sizes[index]);
|
|
183
|
+
|
|
184
|
+
offset += sizes[index];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return groups;
|
|
188
|
+
}
|
|
189
|
+
|
|
12
190
|
/**
|
|
13
|
-
* Generates a random boolean value.
|
|
191
|
+
* Generates a random boolean value.
|
|
192
|
+
* See also {@link Random.boolean} for the seeded & deterministic counterpart.
|
|
14
193
|
*
|
|
15
194
|
* ---
|
|
16
195
|
*
|
|
@@ -33,17 +212,18 @@ export default class Random
|
|
|
33
212
|
*/
|
|
34
213
|
public static Boolean(ratio = 0.5): boolean
|
|
35
214
|
{
|
|
36
|
-
return (Math.random
|
|
215
|
+
return Random._Boolean(Math.random, ratio);
|
|
37
216
|
}
|
|
38
217
|
|
|
39
218
|
/**
|
|
40
|
-
* Generates a random integer value between `0` (included) and `max` (excluded).
|
|
219
|
+
* Generates a random integer value between `0` (included) and `max` (excluded).
|
|
220
|
+
* See also {@link Random.integer} for the seeded & deterministic counterpart.
|
|
41
221
|
*
|
|
42
222
|
* ---
|
|
43
223
|
*
|
|
44
224
|
* @example
|
|
45
225
|
* ```ts
|
|
46
|
-
* Random.Integer(5); // 0,
|
|
226
|
+
* Random.Integer(5); // [0, 5)
|
|
47
227
|
* ```
|
|
48
228
|
*
|
|
49
229
|
* ---
|
|
@@ -55,13 +235,14 @@ export default class Random
|
|
|
55
235
|
public static Integer(max: number): number;
|
|
56
236
|
|
|
57
237
|
/**
|
|
58
|
-
* Generates a random integer value between `min` (included) and `max` (excluded).
|
|
238
|
+
* Generates a random integer value between `min` (included) and `max` (excluded).
|
|
239
|
+
* See also {@link Random.integer} for the seeded & deterministic counterpart.
|
|
59
240
|
*
|
|
60
241
|
* ---
|
|
61
242
|
*
|
|
62
243
|
* @example
|
|
63
244
|
* ```ts
|
|
64
|
-
* Random.Integer(2, 7); // 2,
|
|
245
|
+
* Random.Integer(2, 7); // [2, 7)
|
|
65
246
|
* ```
|
|
66
247
|
*
|
|
67
248
|
* ---
|
|
@@ -74,19 +255,18 @@ export default class Random
|
|
|
74
255
|
public static Integer(min: number, max: number): number;
|
|
75
256
|
public static Integer(min: number, max?: number): number
|
|
76
257
|
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return Math.floor(Math.random() * (max - min) + min);
|
|
258
|
+
return Random._Integer(Math.random, min, max);
|
|
80
259
|
}
|
|
81
260
|
|
|
82
261
|
/**
|
|
83
|
-
* Generates a random decimal value between `0` (included) and `1` (excluded).
|
|
262
|
+
* Generates a random decimal value between `0` (included) and `1` (excluded).
|
|
263
|
+
* See also {@link Random.decimal} for the seeded & deterministic counterpart.
|
|
84
264
|
*
|
|
85
265
|
* ---
|
|
86
266
|
*
|
|
87
267
|
* @example
|
|
88
268
|
* ```ts
|
|
89
|
-
* Random.Decimal(); // 0.123456789
|
|
269
|
+
* Random.Decimal(); // e.g. 0.123456789
|
|
90
270
|
* ```
|
|
91
271
|
*
|
|
92
272
|
* ---
|
|
@@ -96,13 +276,14 @@ export default class Random
|
|
|
96
276
|
public static Decimal(): number;
|
|
97
277
|
|
|
98
278
|
/**
|
|
99
|
-
* Generates a random decimal value between `0` (included) and `max` (excluded).
|
|
279
|
+
* Generates a random decimal value between `0` (included) and `max` (excluded).
|
|
280
|
+
* See also {@link Random.decimal} for the seeded & deterministic counterpart.
|
|
100
281
|
*
|
|
101
282
|
* ---
|
|
102
283
|
*
|
|
103
284
|
* @example
|
|
104
285
|
* ```ts
|
|
105
|
-
* Random.Decimal(5); // 2.3456789
|
|
286
|
+
* Random.Decimal(5); // e.g. 2.3456789
|
|
106
287
|
* ```
|
|
107
288
|
*
|
|
108
289
|
* ---
|
|
@@ -114,13 +295,14 @@ export default class Random
|
|
|
114
295
|
public static Decimal(max: number): number;
|
|
115
296
|
|
|
116
297
|
/**
|
|
117
|
-
* Generates a random decimal value between `min` (included) and `max` (excluded).
|
|
298
|
+
* Generates a random decimal value between `min` (included) and `max` (excluded).
|
|
299
|
+
* See also {@link Random.decimal} for the seeded & deterministic counterpart.
|
|
118
300
|
*
|
|
119
301
|
* ---
|
|
120
302
|
*
|
|
121
303
|
* @example
|
|
122
304
|
* ```ts
|
|
123
|
-
* Random.Decimal(2, 7); // 4.56789
|
|
305
|
+
* Random.Decimal(2, 7); // e.g. 4.56789
|
|
124
306
|
* ```
|
|
125
307
|
*
|
|
126
308
|
* ---
|
|
@@ -128,19 +310,26 @@ export default class Random
|
|
|
128
310
|
* @param min The minimum value (included).
|
|
129
311
|
* @param max The maximum value (excluded).
|
|
130
312
|
*
|
|
131
|
-
* @returns A random decimal value
|
|
313
|
+
* @returns A random decimal value.
|
|
132
314
|
*/
|
|
133
315
|
public static Decimal(min: number, max: number): number;
|
|
134
316
|
public static Decimal(min?: number, max?: number): number
|
|
135
317
|
{
|
|
136
|
-
|
|
137
|
-
if (max === undefined) { return (Math.random() * min); }
|
|
138
|
-
|
|
139
|
-
return (Math.random() * (max - min) + min);
|
|
318
|
+
return Random._Decimal(Math.random, min, max);
|
|
140
319
|
}
|
|
141
320
|
|
|
142
321
|
/**
|
|
143
|
-
* Picks a random valid index from a given array of elements.
|
|
322
|
+
* Picks a random valid index from a given array of elements.
|
|
323
|
+
* See also {@link Random.index} for the seeded & deterministic counterpart.
|
|
324
|
+
*
|
|
325
|
+
* ---
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```ts
|
|
329
|
+
* const elements = ["a", "b", "c"];
|
|
330
|
+
*
|
|
331
|
+
* Random.Index(elements); // 0, 1, or 2
|
|
332
|
+
* ```
|
|
144
333
|
*
|
|
145
334
|
* ---
|
|
146
335
|
*
|
|
@@ -155,13 +344,21 @@ export default class Random
|
|
|
155
344
|
*/
|
|
156
345
|
public static Index<T>(elements: readonly T[]): number
|
|
157
346
|
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return this.Integer(elements.length);
|
|
347
|
+
return Random._Index(Math.random, elements);
|
|
161
348
|
}
|
|
162
349
|
|
|
163
350
|
/**
|
|
164
|
-
* Picks a random element from a given array of elements.
|
|
351
|
+
* Picks a random element from a given array of elements.
|
|
352
|
+
* See also {@link Random.choice} for the seeded & deterministic counterpart.
|
|
353
|
+
*
|
|
354
|
+
* ---
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* const elements = ["a", "b", "c"];
|
|
359
|
+
*
|
|
360
|
+
* Random.Choice(elements); // "a", "b", or "c"
|
|
361
|
+
* ```
|
|
165
362
|
*
|
|
166
363
|
* ---
|
|
167
364
|
*
|
|
@@ -176,11 +373,12 @@ export default class Random
|
|
|
176
373
|
*/
|
|
177
374
|
public static Choice<T>(elements: readonly T[]): T
|
|
178
375
|
{
|
|
179
|
-
return
|
|
376
|
+
return Random._Choice(Math.random, elements);
|
|
180
377
|
}
|
|
181
378
|
|
|
182
379
|
/**
|
|
183
|
-
* Picks a random sample of elements from a given array without replacement.
|
|
380
|
+
* Picks a random sample of elements from a given array without replacement.
|
|
381
|
+
* See also {@link Random.sample} for the seeded & deterministic counterpart.
|
|
184
382
|
*
|
|
185
383
|
* Uses the Fisher-Yates shuffle algorithm for uniform sampling,
|
|
186
384
|
* which is O(count) instead of O(n log n) for a full shuffle.
|
|
@@ -189,7 +387,7 @@ export default class Random
|
|
|
189
387
|
*
|
|
190
388
|
* @example
|
|
191
389
|
* ```ts
|
|
192
|
-
* Random.Sample([1, 2, 3, 4, 5], 3); // e.g
|
|
390
|
+
* Random.Sample([1, 2, 3, 4, 5], 3); // e.g. [4, 1, 5]
|
|
193
391
|
* ```
|
|
194
392
|
*
|
|
195
393
|
* ---
|
|
@@ -211,7 +409,8 @@ export default class Random
|
|
|
211
409
|
public static Sample<T>(elements: readonly T[], count: number): T[];
|
|
212
410
|
|
|
213
411
|
/**
|
|
214
|
-
* Picks a weighted random sample of elements from a given array without replacement.
|
|
412
|
+
* Picks a weighted random sample of elements from a given array without replacement.
|
|
413
|
+
* See also {@link Random.sample} for the seeded & deterministic counterpart.
|
|
215
414
|
*
|
|
216
415
|
* Uses the Efraimidis-Spirakis algorithm for weighted sampling.
|
|
217
416
|
* Elements with higher weights have a higher probability of being selected.
|
|
@@ -221,7 +420,7 @@ export default class Random
|
|
|
221
420
|
* @example
|
|
222
421
|
* ```ts
|
|
223
422
|
* // Element "a" is 3x more likely to be picked than "b" or "c"
|
|
224
|
-
* Random.Sample(["a", "b", "c"], 2, [3, 1, 1]);
|
|
423
|
+
* Random.Sample(["a", "b", "c"], 2, [3, 1, 1]); // e.g. ["a", "c"]
|
|
225
424
|
* ```
|
|
226
425
|
*
|
|
227
426
|
* ---
|
|
@@ -249,88 +448,12 @@ export default class Random
|
|
|
249
448
|
public static Sample<T>(elements: readonly T[], count: number, weights: readonly number[]): T[];
|
|
250
449
|
public static Sample<T>(elements: readonly T[], count: number, weights?: readonly number[]): T[]
|
|
251
450
|
{
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (length === 0) { throw new ValueException("You must provide at least one element."); }
|
|
255
|
-
if (count < 0) { throw new ValueException("Count must be non-negative."); }
|
|
256
|
-
if (count > length) { throw new ValueException("Count cannot exceed the number of elements."); }
|
|
257
|
-
|
|
258
|
-
if (count === 0) { return []; }
|
|
259
|
-
|
|
260
|
-
if (weights === undefined)
|
|
261
|
-
{
|
|
262
|
-
const pool = Array.from(elements);
|
|
263
|
-
const result: T[] = new Array(count);
|
|
264
|
-
|
|
265
|
-
for (let index = 0; index < count; index += 1)
|
|
266
|
-
{
|
|
267
|
-
const randomIndex = this.Integer(index, length);
|
|
268
|
-
|
|
269
|
-
result[index] = pool[randomIndex];
|
|
270
|
-
pool[randomIndex] = pool[index];
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return result;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (weights.length !== length)
|
|
277
|
-
{
|
|
278
|
-
throw new ValueException("Weights array must have the same length as elements array.");
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const keys: ({ index: number, key: number })[] = new Array(length);
|
|
282
|
-
for (let index = 0; index < length; index += 1)
|
|
283
|
-
{
|
|
284
|
-
if (weights[index] <= 0)
|
|
285
|
-
{
|
|
286
|
-
throw new ValueException(`Weight for element #${index} must be greater than zero.`);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
keys[index] = { index: index, key: Math.pow(Math.random(), 1 / weights[index]) };
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
keys.sort((a, b) => b.key - a.key);
|
|
293
|
-
|
|
294
|
-
const result: T[] = new Array(count);
|
|
295
|
-
for (let index = 0; index < count; index += 1)
|
|
296
|
-
{
|
|
297
|
-
result[index] = elements[keys[index].index];
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return result;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
static #Split(total: number, parts: number): number[]
|
|
304
|
-
{
|
|
305
|
-
const cuts: number[] = new Array(parts - 1);
|
|
306
|
-
for (let index = 0; index < cuts.length; index += 1)
|
|
307
|
-
{
|
|
308
|
-
cuts[index] = Math.random() * total;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
cuts.sort((a, b) => (a - b));
|
|
312
|
-
|
|
313
|
-
const boundaries = [0, ...cuts, total];
|
|
314
|
-
const values: number[] = new Array(parts);
|
|
315
|
-
|
|
316
|
-
for (let index = 0; index < parts; index += 1)
|
|
317
|
-
{
|
|
318
|
-
values[index] = Math.floor(boundaries[index + 1] - boundaries[index]);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
let remainder = total - values.reduce((sum, val) => (sum + val), 0);
|
|
322
|
-
while (remainder > 0)
|
|
323
|
-
{
|
|
324
|
-
values[this.Integer(parts)] += 1;
|
|
325
|
-
|
|
326
|
-
remainder -= 1;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return values;
|
|
451
|
+
return Random._Sample(Math.random, elements, count, weights);
|
|
330
452
|
}
|
|
331
453
|
|
|
332
454
|
/**
|
|
333
|
-
* Splits a total amount into a given number of randomly balanced integer parts that sum to the total.
|
|
455
|
+
* Splits a total amount into a given number of randomly balanced integer parts that sum to the total.
|
|
456
|
+
* See also {@link Random.split} for the seeded & deterministic counterpart.
|
|
334
457
|
*
|
|
335
458
|
* Uses random cut-points to generate a uniform distribution of parts.
|
|
336
459
|
*
|
|
@@ -338,8 +461,8 @@ export default class Random
|
|
|
338
461
|
*
|
|
339
462
|
* @example
|
|
340
463
|
* ```ts
|
|
341
|
-
* Random.Split(100, 3); // [28, 41, 31]
|
|
342
|
-
* Random.Split(10, 4); // [3, 1, 4, 2]
|
|
464
|
+
* Random.Split(100, 3); // e.g. [28, 41, 31]
|
|
465
|
+
* Random.Split(10, 4); // e.g. [3, 1, 4, 2]
|
|
343
466
|
* ```
|
|
344
467
|
*
|
|
345
468
|
* ---
|
|
@@ -359,7 +482,8 @@ export default class Random
|
|
|
359
482
|
public static Split(total: number, parts: number): number[];
|
|
360
483
|
|
|
361
484
|
/**
|
|
362
|
-
* Splits an iterable of elements into a given number of randomly balanced groups.
|
|
485
|
+
* Splits an iterable of elements into a given number of randomly balanced groups.
|
|
486
|
+
* See also {@link Random.split} for the seeded & deterministic counterpart.
|
|
363
487
|
*
|
|
364
488
|
* The elements are distributed into groups whose sizes are
|
|
365
489
|
* determined by a random split of the total number of elements.
|
|
@@ -368,9 +492,9 @@ export default class Random
|
|
|
368
492
|
*
|
|
369
493
|
* @example
|
|
370
494
|
* ```ts
|
|
371
|
-
* Random.Split([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4, 5]]
|
|
372
|
-
* Random.Split([1, 2, 3, 4, 5], 2); // [[1, 2, 3, 4], [5]]
|
|
373
|
-
* Random.Split("abcdef", 3); // [["a"], ["b", "c", "d"], ["e", "f"]]
|
|
495
|
+
* Random.Split([1, 2, 3, 4, 5], 2); // e.g. [[1, 2], [3, 4, 5]]
|
|
496
|
+
* Random.Split([1, 2, 3, 4, 5], 2); // e.g. [[1, 2, 3, 4], [5]]
|
|
497
|
+
* Random.Split("abcdef", 3); // e.g. [["a"], ["b", "c", "d"], ["e", "f"]]
|
|
374
498
|
* ```
|
|
375
499
|
*
|
|
376
500
|
* ---
|
|
@@ -393,39 +517,398 @@ export default class Random
|
|
|
393
517
|
public static Split<T>(elements: Iterable<T>, groups: number): T[][];
|
|
394
518
|
public static Split<T>(totalOrElements: number | Iterable<T>, parts: number): number[] | T[][]
|
|
395
519
|
{
|
|
396
|
-
|
|
520
|
+
return Random._Split(Math.random, totalOrElements, parts);
|
|
521
|
+
}
|
|
397
522
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
523
|
+
/**
|
|
524
|
+
* Creates a new seedable {@link Random} generator instance.
|
|
525
|
+
*
|
|
526
|
+
* The returned instance exposes the same API as the static {@link Random} class,
|
|
527
|
+
* but produces deterministic sequences driven by the given seed.
|
|
528
|
+
* Two instances built with the same seed will emit the same values in the same order.
|
|
529
|
+
*
|
|
530
|
+
* Internally, values are produced by a Mulberry32 PRNG.
|
|
531
|
+
*
|
|
532
|
+
* ---
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```ts
|
|
536
|
+
* const rng = Random.FromSeed(42); // deterministic — same seed, same sequence
|
|
537
|
+
*
|
|
538
|
+
* rng.integer(100); // 60
|
|
539
|
+
* rng.decimal(); // 0.44829055899754167
|
|
540
|
+
* ```
|
|
541
|
+
*
|
|
542
|
+
* ---
|
|
543
|
+
*
|
|
544
|
+
* @param seed The 32-bit integer seed used to initialize the generator.
|
|
545
|
+
*
|
|
546
|
+
* @returns A new {@link Random} instance bound to the given seed.
|
|
547
|
+
*/
|
|
548
|
+
public static FromSeed(seed: number): Random
|
|
549
|
+
{
|
|
550
|
+
return new Random(seed);
|
|
551
|
+
}
|
|
401
552
|
|
|
402
|
-
|
|
403
|
-
}
|
|
553
|
+
private readonly _next: () => number;
|
|
404
554
|
|
|
405
|
-
|
|
406
|
-
|
|
555
|
+
private constructor(seed: number)
|
|
556
|
+
{
|
|
557
|
+
this._next = Random.#Mulberry32(seed);
|
|
558
|
+
}
|
|
407
559
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
560
|
+
/**
|
|
561
|
+
* Generates a random boolean value.
|
|
562
|
+
* See also {@link Random.Boolean} for the static & non-deterministic counterpart.
|
|
563
|
+
*
|
|
564
|
+
* ---
|
|
565
|
+
*
|
|
566
|
+
* @example
|
|
567
|
+
* ```ts
|
|
568
|
+
* const rng = Random.FromSeed(...);
|
|
569
|
+
*
|
|
570
|
+
* if (rng.boolean())
|
|
571
|
+
* {
|
|
572
|
+
* // Do something...
|
|
573
|
+
* }
|
|
574
|
+
* ```
|
|
575
|
+
*
|
|
576
|
+
* ---
|
|
577
|
+
*
|
|
578
|
+
* @param ratio
|
|
579
|
+
* The probability of generating `true`.
|
|
580
|
+
*
|
|
581
|
+
* It must be included between `0` and `1`. Default is `0.5`.
|
|
582
|
+
*
|
|
583
|
+
* @returns A random boolean value.
|
|
584
|
+
*/
|
|
585
|
+
public boolean(ratio = 0.5): boolean
|
|
586
|
+
{
|
|
587
|
+
return Random._Boolean(this._next, ratio);
|
|
588
|
+
}
|
|
413
589
|
|
|
414
|
-
|
|
415
|
-
|
|
590
|
+
/**
|
|
591
|
+
* Generates a random integer value between `0` (included) and `max` (excluded).
|
|
592
|
+
* See also {@link Random.Integer} for the static & non-deterministic counterpart.
|
|
593
|
+
*
|
|
594
|
+
* ---
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* ```ts
|
|
598
|
+
* const rng = Random.FromSeed(...);
|
|
599
|
+
*
|
|
600
|
+
* rng.integer(5); // [0, 5)
|
|
601
|
+
* ```
|
|
602
|
+
*
|
|
603
|
+
* ---
|
|
604
|
+
*
|
|
605
|
+
* @param max The maximum value (excluded).
|
|
606
|
+
*
|
|
607
|
+
* @returns A random integer value.
|
|
608
|
+
*/
|
|
609
|
+
public integer(max: number): number;
|
|
416
610
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
611
|
+
/**
|
|
612
|
+
* Generates a random integer value between `min` (included) and `max` (excluded).
|
|
613
|
+
* See also {@link Random.Integer} for the static & non-deterministic counterpart.
|
|
614
|
+
*
|
|
615
|
+
* ---
|
|
616
|
+
*
|
|
617
|
+
* @example
|
|
618
|
+
* ```ts
|
|
619
|
+
* const rng = Random.FromSeed(...);
|
|
620
|
+
*
|
|
621
|
+
* rng.integer(2, 7); // [2, 7)
|
|
622
|
+
* ```
|
|
623
|
+
*
|
|
624
|
+
* ---
|
|
625
|
+
*
|
|
626
|
+
* @param min The minimum value (included).
|
|
627
|
+
* @param max The maximum value (excluded).
|
|
628
|
+
*
|
|
629
|
+
* @returns A random integer value.
|
|
630
|
+
*/
|
|
631
|
+
public integer(min: number, max: number): number;
|
|
632
|
+
public integer(min: number, max?: number): number
|
|
633
|
+
{
|
|
634
|
+
return Random._Integer(this._next, min, max);
|
|
635
|
+
}
|
|
421
636
|
|
|
422
|
-
|
|
423
|
-
|
|
637
|
+
/**
|
|
638
|
+
* Generates a random decimal value between `0` (included) and `1` (excluded).
|
|
639
|
+
* See also {@link Random.Decimal} for the static & non-deterministic counterpart.
|
|
640
|
+
*
|
|
641
|
+
* ---
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* ```ts
|
|
645
|
+
* const rng = Random.FromSeed(...);
|
|
646
|
+
*
|
|
647
|
+
* rng.decimal(); // e.g. 0.123456789
|
|
648
|
+
* ```
|
|
649
|
+
*
|
|
650
|
+
* ---
|
|
651
|
+
*
|
|
652
|
+
* @returns A random decimal value.
|
|
653
|
+
*/
|
|
654
|
+
public decimal(): number;
|
|
424
655
|
|
|
425
|
-
|
|
656
|
+
/**
|
|
657
|
+
* Generates a random decimal value between `0` (included) and `max` (excluded).
|
|
658
|
+
* See also {@link Random.Decimal} for the static & non-deterministic counterpart.
|
|
659
|
+
*
|
|
660
|
+
* ---
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```ts
|
|
664
|
+
* const rng = Random.FromSeed(...);
|
|
665
|
+
*
|
|
666
|
+
* rng.decimal(5); // e.g. 2.3456789
|
|
667
|
+
* ```
|
|
668
|
+
*
|
|
669
|
+
* ---
|
|
670
|
+
*
|
|
671
|
+
* @param max The maximum value (excluded).
|
|
672
|
+
*
|
|
673
|
+
* @returns A random decimal value.
|
|
674
|
+
*/
|
|
675
|
+
public decimal(max: number): number;
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Generates a random decimal value between `min` (included) and `max` (excluded).
|
|
679
|
+
* See also {@link Random.Decimal} for the static & non-deterministic counterpart.
|
|
680
|
+
*
|
|
681
|
+
* ---
|
|
682
|
+
*
|
|
683
|
+
* @example
|
|
684
|
+
* ```ts
|
|
685
|
+
* const rng = Random.FromSeed(...);
|
|
686
|
+
*
|
|
687
|
+
* rng.decimal(2, 7); // e.g. 4.56789
|
|
688
|
+
* ```
|
|
689
|
+
*
|
|
690
|
+
* ---
|
|
691
|
+
*
|
|
692
|
+
* @param min The minimum value (included).
|
|
693
|
+
* @param max The maximum value (excluded).
|
|
694
|
+
*
|
|
695
|
+
* @returns A random decimal value.
|
|
696
|
+
*/
|
|
697
|
+
public decimal(min: number, max: number): number;
|
|
698
|
+
public decimal(min?: number, max?: number): number
|
|
699
|
+
{
|
|
700
|
+
return Random._Decimal(this._next, min, max);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Picks a random valid index from a given array of elements.
|
|
705
|
+
* See also {@link Random.Index} for the static & non-deterministic counterpart.
|
|
706
|
+
*
|
|
707
|
+
* ---
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```ts
|
|
711
|
+
* const rng = Random.FromSeed(...);
|
|
712
|
+
*
|
|
713
|
+
* rng.index(["a", "b", "c"]); // 0, 1, or 2
|
|
714
|
+
* ```
|
|
715
|
+
*
|
|
716
|
+
* ---
|
|
717
|
+
*
|
|
718
|
+
* @template T The type of the elements in the array.
|
|
719
|
+
*
|
|
720
|
+
* @param elements
|
|
721
|
+
* The array of elements to pick from.
|
|
722
|
+
*
|
|
723
|
+
* It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
|
|
724
|
+
*
|
|
725
|
+
* @returns A valid random index from the given array.
|
|
726
|
+
*/
|
|
727
|
+
public index<T>(elements: readonly T[]): number
|
|
728
|
+
{
|
|
729
|
+
return Random._Index(this._next, elements);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Picks a random element from a given array of elements.
|
|
734
|
+
* See also {@link Random.Choice} for the static & non-deterministic counterpart.
|
|
735
|
+
*
|
|
736
|
+
* ---
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```ts
|
|
740
|
+
* const rng = Random.FromSeed(...);
|
|
741
|
+
*
|
|
742
|
+
* rng.choice(["a", "b", "c"]); // "a", "b", or "c"
|
|
743
|
+
* ```
|
|
744
|
+
*
|
|
745
|
+
* ---
|
|
746
|
+
*
|
|
747
|
+
* @template T The type of the elements in the array.
|
|
748
|
+
*
|
|
749
|
+
* @param elements
|
|
750
|
+
* The array of elements to pick from.
|
|
751
|
+
*
|
|
752
|
+
* It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
|
|
753
|
+
*
|
|
754
|
+
* @returns A random element from the given array.
|
|
755
|
+
*/
|
|
756
|
+
public choice<T>(elements: readonly T[]): T
|
|
757
|
+
{
|
|
758
|
+
return Random._Choice(this._next, elements);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Picks a random sample of elements from a given array without replacement.
|
|
763
|
+
* See also {@link Random.Sample} for the static & non-deterministic counterpart.
|
|
764
|
+
*
|
|
765
|
+
* Uses the Fisher-Yates shuffle algorithm for uniform sampling,
|
|
766
|
+
* which is O(count) instead of O(n log n) for a full shuffle.
|
|
767
|
+
*
|
|
768
|
+
* ---
|
|
769
|
+
*
|
|
770
|
+
* @example
|
|
771
|
+
* ```ts
|
|
772
|
+
* const rng = Random.FromSeed(...);
|
|
773
|
+
*
|
|
774
|
+
* rng.sample([1, 2, 3, 4, 5], 3); // e.g. [4, 1, 5]
|
|
775
|
+
* ```
|
|
776
|
+
*
|
|
777
|
+
* ---
|
|
778
|
+
*
|
|
779
|
+
* @template T The type of the elements in the array.
|
|
780
|
+
*
|
|
781
|
+
* @param elements
|
|
782
|
+
* The array of elements to sample from.
|
|
783
|
+
*
|
|
784
|
+
* It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
|
|
785
|
+
*
|
|
786
|
+
* @param count
|
|
787
|
+
* The number of elements to sample.
|
|
788
|
+
*
|
|
789
|
+
* It must be between `0` and `elements.length`. Otherwise, a {@link ValueException} will be thrown.
|
|
790
|
+
*
|
|
791
|
+
* @returns An array containing the randomly sampled elements.
|
|
792
|
+
*/
|
|
793
|
+
public sample<T>(elements: readonly T[], count: number): T[];
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Picks a weighted random sample of elements from a given array without replacement.
|
|
797
|
+
* See also {@link Random.Sample} for the static & non-deterministic counterpart.
|
|
798
|
+
*
|
|
799
|
+
* Uses the Efraimidis-Spirakis algorithm for weighted sampling.
|
|
800
|
+
* Elements with higher weights have a higher probability of being selected.
|
|
801
|
+
*
|
|
802
|
+
* ---
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* ```ts
|
|
806
|
+
* const rng = Random.FromSeed(...);
|
|
807
|
+
*
|
|
808
|
+
* // Element "a" is 3x more likely to be picked than "b" or "c"
|
|
809
|
+
* rng.sample(["a", "b", "c"], 2, [3, 1, 1]); // e.g. ["a", "c"]
|
|
810
|
+
* ```
|
|
811
|
+
*
|
|
812
|
+
* ---
|
|
813
|
+
*
|
|
814
|
+
* @template T The type of the elements in the array.
|
|
815
|
+
*
|
|
816
|
+
* @param elements
|
|
817
|
+
* The array of elements to sample from.
|
|
818
|
+
*
|
|
819
|
+
* It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
|
|
820
|
+
*
|
|
821
|
+
* @param count
|
|
822
|
+
* The number of elements to sample.
|
|
823
|
+
*
|
|
824
|
+
* It must be between `0` and `elements.length`. Otherwise, a {@link ValueException} will be thrown.
|
|
825
|
+
*
|
|
826
|
+
* @param weights
|
|
827
|
+
* The weights associated with each element.
|
|
828
|
+
*
|
|
829
|
+
* It must have the same length as the elements array.
|
|
830
|
+
* All weights must be greater than zero. Otherwise, a {@link ValueException} will be thrown.
|
|
831
|
+
*
|
|
832
|
+
* @returns An array containing the randomly sampled elements.
|
|
833
|
+
*/
|
|
834
|
+
public sample<T>(elements: readonly T[], count: number, weights: readonly number[]): T[];
|
|
835
|
+
public sample<T>(elements: readonly T[], count: number, weights?: readonly number[]): T[]
|
|
836
|
+
{
|
|
837
|
+
return Random._Sample(this._next, elements, count, weights);
|
|
426
838
|
}
|
|
427
839
|
|
|
428
|
-
|
|
840
|
+
/**
|
|
841
|
+
* Splits a total amount into a given number of randomly balanced integer parts that sum to the total.
|
|
842
|
+
* See also {@link Random.Split} for the static & non-deterministic counterpart.
|
|
843
|
+
*
|
|
844
|
+
* Uses random cut-points to generate a uniform distribution of parts.
|
|
845
|
+
*
|
|
846
|
+
* ---
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```ts
|
|
850
|
+
* const rng = Random.FromSeed(...);
|
|
851
|
+
*
|
|
852
|
+
* rng.split(100, 3); // e.g. [28, 41, 31]
|
|
853
|
+
* rng.split(10, 4); // e.g. [3, 1, 4, 2]
|
|
854
|
+
* ```
|
|
855
|
+
*
|
|
856
|
+
* ---
|
|
857
|
+
*
|
|
858
|
+
* @param total
|
|
859
|
+
* The total amount to split.
|
|
860
|
+
*
|
|
861
|
+
* It must be non-negative. Otherwise, a {@link ValueException} will be thrown.
|
|
862
|
+
*
|
|
863
|
+
* @param parts
|
|
864
|
+
* The number of parts to split the total into.
|
|
865
|
+
*
|
|
866
|
+
* It must be at least `1`. Otherwise, a {@link ValueException} will be thrown.
|
|
867
|
+
*
|
|
868
|
+
* @returns An array of integers that sum to the given total.
|
|
869
|
+
*/
|
|
870
|
+
public split(total: number, parts: number): number[];
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Splits an iterable of elements into a given number of randomly balanced groups.
|
|
874
|
+
* See also {@link Random.Split} for the static & non-deterministic counterpart.
|
|
875
|
+
*
|
|
876
|
+
* The elements are distributed into groups whose sizes are
|
|
877
|
+
* determined by a random split of the total number of elements.
|
|
878
|
+
*
|
|
879
|
+
* ---
|
|
880
|
+
*
|
|
881
|
+
* @example
|
|
882
|
+
* ```ts
|
|
883
|
+
* const rng = Random.FromSeed(...);
|
|
884
|
+
*
|
|
885
|
+
* rng.split([1, 2, 3, 4, 5], 2); // e.g. [[1, 2], [3, 4, 5]]
|
|
886
|
+
* rng.split([1, 2, 3, 4, 5], 2); // e.g. [[1, 2, 3, 4], [5]]
|
|
887
|
+
* rng.split("abcdef", 3); // e.g. [["a"], ["b", "c", "d"], ["e", "f"]]
|
|
888
|
+
* ```
|
|
889
|
+
*
|
|
890
|
+
* ---
|
|
891
|
+
*
|
|
892
|
+
* @template T The type of the elements in the iterable.
|
|
893
|
+
*
|
|
894
|
+
* @param elements
|
|
895
|
+
* The iterable of elements to split into groups.
|
|
896
|
+
*
|
|
897
|
+
* It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
|
|
898
|
+
*
|
|
899
|
+
* @param groups
|
|
900
|
+
* The number of groups to split the elements into.
|
|
901
|
+
*
|
|
902
|
+
* It must be between `1` and the number of elements.
|
|
903
|
+
* Otherwise, a {@link ValueException} will be thrown.
|
|
904
|
+
*
|
|
905
|
+
* @returns An array of arrays, each containing a subset of the original elements.
|
|
906
|
+
*/
|
|
907
|
+
public split<T>(elements: Iterable<T>, groups: number): T[][];
|
|
908
|
+
public split<T>(totalOrElements: number | Iterable<T>, parts: number): number[] | T[][]
|
|
909
|
+
{
|
|
910
|
+
return Random._Split(this._next, totalOrElements, parts);
|
|
911
|
+
}
|
|
429
912
|
|
|
430
913
|
public readonly [Symbol.toStringTag]: string = "Random";
|
|
431
914
|
}
|