using System; using System.Collections.Generic; using System.Linq; namespace MLEM.Extensions { /// /// A set of extensions for dealing with /// public static class RandomExtensions { /// /// Gets a random entry from the given collection with uniform chance. /// /// The random /// The entries to choose from /// The entries' type /// A random entry public static T GetRandomEntry(this Random random, ICollection entries) { return RandomExtensions.GetRandomEntry(entries, random.NextSingle()); } /// /// Returns a random entry from the given collection based on the specified weight function. /// A higher weight for an entry increases its likeliness of being picked. /// /// The random /// The entries to choose from /// A function that applies weight to each entry /// The entries' type /// A random entry, based on the entries' weight /// If the weight function returns different weights for the same entry public static T GetRandomWeightedEntry(this Random random, ICollection entries, Func weightFunc) { return RandomExtensions.GetRandomWeightedEntry(entries, weightFunc, random.NextSingle()); } /// public static T GetRandomWeightedEntry(this Random random, ICollection entries, Func weightFunc) { return RandomExtensions.GetRandomWeightedEntry(entries, weightFunc, random.NextSingle()); } /// /// Returns a random floating-point number that is greater than or equal to 0, and less than . /// /// The random. /// The (exclusive) maximum value. /// A single-precision floating point number that is greater than or equal to 0, and less than . public static float NextSingle(this Random random, float maxValue) { return maxValue * random.NextSingle(); } /// /// Returns a random floating-point number that is greater than or equal to , and less than . /// /// The random. /// The (inclusive) minimum value. /// The (exclusive) maximum value. /// A single-precision floating point number that is greater than or equal to , and less than . public static float NextSingle(this Random random, float minValue, float maxValue) { return (maxValue - minValue) * random.NextSingle() + minValue; } #if !NET6_0_OR_GREATER /// /// Returns a random floating-point number that is greater than or equal to 0, and less than 1. /// /// The random. /// A single-precision floating point number that is greater than or equal to 0, and less than 1. public static float NextSingle(this Random random) { return (float) random.NextDouble(); } #endif internal static T GetRandomEntry(ICollection entries, float randomValue) { // ElementAt internally optimizes for IList access so we don't have to here return entries.ElementAt((int) (randomValue * entries.Count)); } internal static T GetRandomWeightedEntry(ICollection entries, Func weightFunc, float randomValue) { var goalWeight = randomValue * entries.Sum(weightFunc); var currWeight = 0; foreach (var entry in entries) { currWeight += weightFunc(entry); if (currWeight > goalWeight) return entry; } throw new IndexOutOfRangeException(); } internal static T GetRandomWeightedEntry(ICollection entries, Func weightFunc, float randomValue) { var goalWeight = randomValue * entries.Sum(weightFunc); var currWeight = 0F; foreach (var entry in entries) { currWeight += weightFunc(entry); if (currWeight > goalWeight) return entry; } throw new IndexOutOfRangeException(); } } }