/// <summary> /// Selects a single item based in the weights /// </summary> /// <returns>The value of the item</returns> public T Select() { if (_items.Count == 0) { throw new InvalidOperationException("There was no items to select from"); } Build(); return(WeightedHelper <T> .SelectItem(_items, _cumulativeWeights, GenerateRandomWeight(_items)).Value); }
/// <summary> /// Selects multiple items based in the weights /// </summary> /// <param name="count">The number of items to select</param> /// <returns>A list containing the values of the items selected</returns> public IEnumerable <T> Select(int count) { if (count <= 0) { throw new InvalidOperationException("Count must be > 0."); } if (_items.Count == 0) { throw new InvalidOperationException("There were no items to select from."); } if (!_options.HasFlag(SelectorOptions.AllowDuplicates) && _items.Count < count) { throw new InvalidOperationException("There aren't enough items in the collection to take " + count); } Build(); if (_options.HasFlag(SelectorOptions.AllowDuplicates)) { for (var i = 0; i < count; i++) { yield return(WeightedHelper <T> .SelectItem(_items, _cumulativeWeights, GenerateRandomWeight(_items)).Value); } } // The code bellow can perform under O(log(n)) even when removing items from the list // A shallow copy of the lists containing the items and the cumulative weights, since we will be removing items of the list we want to conserve the original ones var items = new List <WeightedItem <T> >(_items); var weights = new List <int>(_cumulativeWeightsList); for (var i = 0; i < count; i++) { int index = WeightedHelper <T> .SelectItemIndex(items, weights, GenerateRandomWeight(items)); if (items.Count > 0) { items.RemoveAt(index); } if (weights.Count > 0) { weights.RemoveAt(index); } yield return(items[index].Value); } }
/// <summary> /// Calculates the cumulative weights if it's needed /// </summary> public void Build() { if (!_forceRecalculation) { return; } _forceRecalculation = false; (_cumulativeWeights, _totalCumulativeWeight) = WeightedHelper <T> .CalculateCumulativeWeights(_items, _integerFactor); // If the selector don't allow duplicates then it have to create a copy of the weights in list form if (!_options.HasFlag(SelectorOptions.AllowDuplicates)) { _cumulativeWeightsList = _cumulativeWeights.ToList(); } }