-
-
Save sbrl/9090a8c646b8d34b6e0170ddfd197d09 to your computer and use it in GitHub Desktop.
[WeightedRandom.cs] A weighted random number generator in C♯. #microlibrary #algorithm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace SBRL.Algorithms | |
{ | |
/// <summary> | |
/// Picks random items from a list, according to weights assigned to them. | |
/// </summary> | |
/// <remarks> | |
/// Higher weights mean that an item is picked more frequently than the other items. | |
/// </remarks> | |
/// <license>Mozilla Public License version 2.0</license> | |
/// <origin>https://gist.github.com/sbrl/9090a8c646b8d34b6e0170ddfd197d09</origin> | |
/// <author>Starbeamrainbowlabs (https://starbeamrainbowlabs.com/)</author> | |
/// <changelog> | |
/// v0.1 - 20th May 2017: | |
/// - Creation! :D | |
/// v0.2 - 17th Februrary 2018: | |
/// - Add Count property | |
/// - Add SetContents and ClearContents methods | |
/// - Add empty constructor | |
/// - Next() will now throw an InvalidOperationException if the generator's internal weights list is empty | |
/// </changelog> | |
public class WeightedRandom<ItemType> | |
{ | |
private Random rand = new Random(); | |
protected Dictionary<ItemType, double> weights = new Dictionary<ItemType, double>(); | |
public int Count { | |
get { | |
return weights.Count; | |
} | |
} | |
/// <summary> | |
/// Creates a new weighted random number generator. | |
/// </summary> | |
/// <param name="items">The dictionary of weights and their corresponding items.</param> | |
public WeightedRandom(IDictionary<ItemType, double> items) | |
{ | |
SetContents(items); | |
} | |
/// <summary> | |
/// Createse a new empty weighted random number generator. | |
/// Remember to populate it before using! | |
/// </summary> | |
public WeightedRandom() | |
{ | |
} | |
public void SetContents(IDictionary<ItemType, double> items) | |
{ | |
if (items.Count == 0) | |
throw new ArgumentException("Error: The items dictionary provided is empty!"); | |
double totalWeight = items.Values.Aggregate((double a, double b) => a + b); | |
foreach (KeyValuePair<ItemType, double> itemData in items) { | |
weights.Add(itemData.Key, itemData.Value / totalWeight); | |
} | |
} | |
public void ClearContents() | |
{ | |
weights.Clear(); | |
} | |
/// <summary> | |
/// Picks a new random item from the list provided at initialisation, based | |
/// on the weights assigned to them. | |
/// </summary> | |
/// <returns>A random item, picked according to the assigned weights.</returns> | |
public ItemType Next() | |
{ | |
if (weights.Count == 0) | |
throw new InvalidOperationException("Error: No weights specified! Add some with SetContents() before generating random numbers."); | |
double target = rand.NextDouble(); | |
double lower = 0; | |
double higher = 0; | |
foreach (KeyValuePair<ItemType, double> weightData in weights) | |
{ | |
higher += weightData.Value; | |
if (target >= lower && target <= higher) | |
return weightData.Key; | |
lower += weightData.Value; | |
} | |
throw new Exception($"Error: Unable to find the weight that matches {target}"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment