什么是最简洁的方法来在c#中按重量选择随机元素?

Eri*_*Yin 11 c# linq weighted-average weighted

让我们假设:

List<element> 哪个元素是:

public class Element(){
   int Weight {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

我想要实现的是,按重量随机选择一个元素.例如:

Element_1.Weight = 100;
Element_2.Weight = 50;
Element_3.Weight = 200;
Run Code Online (Sandbox Code Playgroud)

所以

  • Element_1选择的机会是100 /(100 + 50 + 200)= 28.57%
  • Element_2选择的机会是50 /(100 + 50 + 200)= 14.29%
  • Element_3选择的机会是200 /(100 + 50 + 200)= 57.14%

我知道我可以创建一个循环,计算总数等...

我想要了解的是,Linq在一行(或尽可能短)中做到这一点的最佳方法,谢谢.

UPDATE

我在下面找到了答案.我学到的第一件事是:Linq不是魔术,它比设计良好的循环慢.

所以我的问题就是按重量找到一个随机元素,(尽可能短的东西:)

dob*_*lak 6

如果您想要通用版本(对于与(单例)随机化助手一起使用很有用,请考虑是否需要恒定种子)

用法:

randomizer.GetRandomItem(items, x => x.Weight)
Run Code Online (Sandbox Code Playgroud)

代码:

public T GetRandomItem<T>(IEnumerable<T> itemsEnumerable, Func<T, int> weightKey)
{
    var items = itemsEnumerable.ToList();

    var totalWeight = items.Sum(x => weightKey(x));
    var randomWeightedIndex = _random.Next(totalWeight);
    var itemWeightedIndex = 0;
    foreach(var item in items)
    {
        itemWeightedIndex += weightKey(item);
        if(randomWeightedIndex < itemWeightedIndex)
            return item;
    }
    throw new ArgumentException("Collection count and weights must be greater than 0");
}
Run Code Online (Sandbox Code Playgroud)

  • 我确信这没有什么区别。 (2认同)

Nuf*_*fin 5

// assuming rnd is an already instantiated instance of the Random class
var max = list.Sum(y => y.Weight);
var rand = rnd.Next(max);
var res = list
    .FirstOrDefault(x => rand >= (max -= x.Weight));
Run Code Online (Sandbox Code Playgroud)

  • 分解出`list.Max(...)`。您当前的代码是`O(n^2)`。如果你把它排除在外,它只是`O(n*log(n))` (2认同)
  • `minWeight` 是为了确保随着重量的增加,更频繁地选择项目。并且随机顺序 (@Abel) 用于从结果集中选择 _a 随机项目_,因为仅选择第一个永远不会从示例中产生 `Weight=50` 项目,即使 `minWeight` 将是 `&lt;50` (2认同)