Last active
August 3, 2016 13:38
-
-
Save RobThree/2626a5c6599fc60c6e3025a4e46bacd0 to your computer and use it in GitHub Desktop.
"Weighted random" object picker (based on http://www.perlmonks.org/?node_id=242751)
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; | |
class Program | |
{ | |
// Demo | |
static void Main(string[] args) | |
{ | |
// Define some weighted object (in this case: strings, but could be anything (e.g. "Customer") and set their weights | |
var myobjects = new[] | |
{ | |
new WeightedObject<string>("A", 2), | |
new WeightedObject<string>("B", 3), | |
new WeightedObject<string>("C", 5), | |
new WeightedObject<string>("D", 10) | |
}; | |
// For <iterations> iterations pick a random object from the above array; keep track of the results in a list | |
var iterations = 10000; | |
var results = new List<WeightedObject<string>>(); | |
for (int i = 0; i < iterations; i++) | |
results.Add(WeightedObjectPicker.PickWeightedObject(myobjects)); | |
// Print the number of times each object was picked and also present this as a percentage | |
Console.WriteLine( | |
string.Join("\r\n", results.GroupBy(o => o.Object).OrderBy(g => g.Key).Select(g => $"{g.Key}\t{g.Count()}\t{(double)g.Count() / iterations:P2}%")) | |
); | |
} | |
} | |
// Picks 'weighted random' objects from an array of objects | |
public class WeightedObjectPicker | |
{ | |
// Initialize RNG | |
private static readonly Random rng = new Random(); | |
public static WeightedObject<T> PickWeightedObject<T>(WeightedObject<T>[] objects) | |
{ | |
// Initialize indexer to point to first object in array, get total weight of objects, pick random number below total weight | |
int i = 0, totalweight = objects.Sum(o => o.Weight), r = rng.Next(totalweight); | |
// Note: we may want to check totalweight to be greater than 0 | |
// While the random number >= 0 | |
while (r >= 0) | |
r -= objects[i++].Weight; // Subtract indexed object's weight, increase indexer | |
// Decrease indexer by one (we incremented it one time too many) and return indexed object | |
return objects[--i]; | |
} | |
} | |
// Represents a weighted object of type T | |
public class WeightedObject<T> | |
{ | |
// The object's weight | |
public int Weight { get; private set; } | |
// The object | |
public T Object { get; private set; } | |
// Initialize new weighted object of T with it's weight | |
public WeightedObject(T obj, int weight) | |
{ | |
if (weight < 0) | |
throw new ArgumentOutOfRangeException("Weight must be >= 0"); | |
this.Object = obj; | |
this.Weight = weight; | |
} | |
} |
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
A 1023 10,23 %% | |
B 1530 15,30 %% | |
C 2497 24,97 %% | |
D 4950 49,50 %% |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment