From 7e64b8a990eb3c5c64ffedd96983677bc36906a5 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 7 Aug 2023 19:00:34 +0200 Subject: [PATCH] Added GetRandomEntry and GetRandomWeightedEntry to SingleRandom --- CHANGELOG.md | 1 + MLEM/Extensions/RandomExtensions.cs | 50 +++++++++++++++++------------ MLEM/Misc/SingleRandom.cs | 36 ++++++++++++++++++++- 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f21f58a..fea90c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Jump to version: Additions - Added GraphicsExtensions.WithRenderTargets, a multi-target version of WithRenderTarget - Added Zero, One, Linear and Clamp to Easings +- Added GetRandomEntry and GetRandomWeightedEntry to SingleRandom Fixes - Fixed TextInput not working correctly when using surrogate pairs diff --git a/MLEM/Extensions/RandomExtensions.cs b/MLEM/Extensions/RandomExtensions.cs index 250e50c..b171c8c 100644 --- a/MLEM/Extensions/RandomExtensions.cs +++ b/MLEM/Extensions/RandomExtensions.cs @@ -16,8 +16,7 @@ namespace MLEM.Extensions { /// The entries' type /// A random entry public static T GetRandomEntry(this Random random, ICollection entries) { - // ElementAt internally optimizes for IList access so we don't have to here - return entries.ElementAt(random.Next(entries.Count)); + return RandomExtensions.GetRandomEntry(entries, random.NextSingle()); } /// @@ -31,28 +30,12 @@ namespace MLEM.Extensions { /// 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) { - var totalWeight = entries.Sum(weightFunc); - var goalWeight = random.Next(totalWeight); - var currWeight = 0; - foreach (var entry in entries) { - currWeight += weightFunc(entry); - if (currWeight > goalWeight) - return entry; - } - throw new IndexOutOfRangeException(); + return RandomExtensions.GetRandomWeightedEntry(entries, weightFunc, random.NextSingle()); } /// public static T GetRandomWeightedEntry(this Random random, ICollection entries, Func weightFunc) { - var totalWeight = entries.Sum(weightFunc); - var goalWeight = random.NextDouble() * totalWeight; - var currWeight = 0F; - foreach (var entry in entries) { - currWeight += weightFunc(entry); - if (currWeight > goalWeight) - return entry; - } - throw new IndexOutOfRangeException(); + return RandomExtensions.GetRandomWeightedEntry(entries, weightFunc, random.NextSingle()); } /// @@ -87,5 +70,32 @@ namespace MLEM.Extensions { } #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(); + } + } } diff --git a/MLEM/Misc/SingleRandom.cs b/MLEM/Misc/SingleRandom.cs index 73f7299..dc3d861 100644 --- a/MLEM/Misc/SingleRandom.cs +++ b/MLEM/Misc/SingleRandom.cs @@ -1,4 +1,8 @@ -namespace MLEM.Misc { +using System; +using System.Collections.Generic; +using MLEM.Extensions; + +namespace MLEM.Misc { /// /// The SingleRandom class allows generating single, one-off pseudorandom numbers based on a seed or a . /// The types of numbers that can be generated are and , both of which can be generated with specific minimum and maximum values if desired. @@ -138,5 +142,35 @@ return (maxValue - minValue) * SingleRandom.Single(source) + minValue; } + /// + /// Gets a random entry from the given collection with uniform chance. + /// + /// The entries to choose from + /// The to use. + /// The entries' type + /// A random entry + public static T GetRandomEntry(ICollection entries, SeedSource source) { + return RandomExtensions.GetRandomEntry(entries, SingleRandom.Single(source)); + } + + /// + /// 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 entries to choose from + /// A function that applies weight to each entry + /// The to use. + /// 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(ICollection entries, Func weightFunc, SeedSource source) { + return RandomExtensions.GetRandomWeightedEntry(entries, weightFunc, SingleRandom.Single(source)); + } + + /// + public static T GetRandomWeightedEntry(ICollection entries, Func weightFunc, SeedSource source) { + return RandomExtensions.GetRandomWeightedEntry(entries, weightFunc, SingleRandom.Single(source)); + } + } }