| ... | @@ -337,6 +337,42 @@ pub const Random = struct { | ... | @@ -337,6 +337,42 @@ pub const Random = struct { |
| 337 | mem.swap(T, &buf[i], &buf[j]); | 337 | mem.swap(T, &buf[i], &buf[j]); |
| 338 | } | 338 | } |
| 339 | } | 339 | } |
| | 340 | |
| | 341 | /// Randomly selects an index into `proportions`, where the likelihood of each |
| | 342 | /// index is weighted by that proportion. |
| | 343 | /// |
| | 344 | /// This is useful for selecting an item from a slice where weights are not equal. |
| | 345 | /// `T` must be a numeric type capable of holding the sum of `proportions`. |
| | 346 | pub fn weightedIndex(r: std.rand.Random, comptime T: type, proportions: []T) usize { |
| | 347 | // This implementation works by summing the proportions and picking a random |
| | 348 | // point in [0, sum). We then loop over the proportions, accumulating |
| | 349 | // until our accumulator is greater than the random point. |
| | 350 | |
| | 351 | var sum: T = 0; |
| | 352 | for (proportions) |v| { |
| | 353 | sum += v; |
| | 354 | } |
| | 355 | |
| | 356 | const point = if (comptime std.meta.trait.isSignedInt(T)) |
| | 357 | r.intRangeLessThan(T, 0, sum) |
| | 358 | else if (comptime std.meta.trait.isUnsignedInt(T)) |
| | 359 | r.uintLessThan(T, sum) |
| | 360 | else if (comptime std.meta.trait.isFloat(T)) |
| | 361 | // take care that imprecision doesn't lead to a value slightly greater than sum |
| | 362 | std.math.min(r.float(T) * sum, sum - std.math.epsilon(T)) |
| | 363 | else |
| | 364 | @compileError("weightedIndex does not support proportions of type " ++ @typeName(T)); |
| | 365 | |
| | 366 | std.debug.assert(point < sum); |
| | 367 | |
| | 368 | var accumulator: T = 0; |
| | 369 | for (proportions) |p, index| { |
| | 370 | accumulator += p; |
| | 371 | if (point < accumulator) return index; |
| | 372 | } |
| | 373 | |
| | 374 | unreachable; |
| | 375 | } |
| 340 | }; | 376 | }; |
| 341 | | 377 | |
| 342 | /// Convert a random integer 0 <= random_int <= maxValue(T), | 378 | /// Convert a random integer 0 <= random_int <= maxValue(T), |