using System.Collections.Generic;
public sealed class Picker<T>
private const int MaxRetries = 2;
private const int CleanupTriggerDenominator = 4;
public int Samples { get; set; }
public int Scanned { get; set; }
private readonly Random random = new();
private List<T> items = new();
private List<double> weights = new();
private List<double> weightCumulations = new();
private int pickedCount = 0;
private List<bool> picked = new();
public Picker(IEnumerable<(T Item, double Weight)> pairs)
foreach(var (item, weight) in pairs)
weightCumulations.Add(cumulation);
var i = GetRandomIndex();
if (++count == MaxRetries || pickedCount > items.Count / CleanupTriggerDenominator) {
private int GetRandomIndex()
var r = random.NextDouble() * weightCumulations[^1];
var i = weightCumulations.BinarySearch(r);
var newItems = new List<T>();
var newWeights = new List<double>();
var newWeightCumulations = new List<double>();
var newPicked = new List<bool>();
for(var i=0; i<items.Count; i++) {
newWeightCumulations.Add(cumulation);
weightCumulations = newWeightCumulations;
public static void Main()
var picker = new Picker<int>(Enumerable.Range(0, 100).Select(x => (x, 1.0)));
for(var i=0; i < 100; i++) {
Console.WriteLine("GetRandomIndex: {0}", picker.Samples);
Console.WriteLine("Scanned: {0}", picker.Scanned);