【问题标题】:Select a random item from a weighted list从加权列表中选择一个随机项目
【发布时间】:2011-11-14 01:48:02
【问题描述】:

我正在尝试编写一个程序来从US Census last name list 中选择一个随机名称。列表格式为

Name           Weight Cumulative line
-----          -----  -----      -
SMITH          1.006  1.006      1
JOHNSON        0.810  1.816      2
WILLIAMS       0.699  2.515      3
JONES          0.621  3.136      4
BROWN          0.621  3.757      5
DAVIS          0.480  4.237      6

假设我将数据加载到类似的结构中

Class Name
{
    public string Name {get; set;}
    public decimal Weight {get; set;}
    public decimal Cumulative {get; set;}
}

什么数据结构最适合保存名称列表,以及从列表中选择随机名称但名称分布与现实世界相同的最佳方法。

如果数据结构有所不同,我只会使用前 10,000 行。

我曾尝试查看有关加权随机性的其他一些问题,但我在将理论转化为代码时遇到了一些麻烦。我对数学理论不太了解,所以我不知道这是否是“有或没有替换”随机选择,我希望同一个名字能够出现不止一次,这意味着。

【问题讨论】:

  • 将累积值存储在平衡二叉树中,节点中包含名称。选择一个小于累积值之和的随机整数,并在 bin 树中搜索(小于)。
  • @belisarius .NET 中是否有任何二叉树结构,或者我必须编写一个?
  • @Scott:你可以只使用一个数组来做这个——BinarySearch 可以正常工作,只要它被排序......
  • @Scott 我不会说.Net,但我想应该有......这就是我没有写答案的原因
  • @Scott:没有内置的,但是有不错的选择,比如:itu.dk/research/c5

标签: c# random distribution weighted


【解决方案1】:

处理此问题的“最简单”方法是将其保存在列表中。

然后你可以使用:

Name GetRandomName(Random random, List<Name> names)
{
    double value = random.NextDouble() * names[names.Count-1].Culmitive;
    return names.Last(name => name.Culmitive <= value);
}

如果速度是一个问题,您可以存储一个单独的数组,该数组仅包含 Culmitive 值。有了这个,您可以使用Array.BinarySearch 快速找到合适的索引:

Name GetRandomName(Random random, List<Name> names, double[] culmitiveValues)
{
    double value = random.NextDouble() * names[names.Count-1].Culmitive;
    int index = Array.BinarySearch(culmitiveValues, value);
    if (index >= 0)
        index = ~index;

    return names[index];
}

另一个可能是最有效的选择是使用类似于C5 Generic Collection Librarytree classes 之一。然后您可以使用RangeFrom 来查找合适的名称。这样做的好处是不需要单独的集合

【讨论】:

  • 您的第一次植入将足够快,可以满足我的需要,谢谢!
  • 我们找到了同样的解决方案。此外,我们围绕 NextDouble 实施了一个效率包装器,以将信息分散到多个 GetRandomName 选择中(不需要 32 位信息来从 6 个选择中选择)。
  • 看这个,我觉得二分搜索答案需要在 if 语句上使用不同的符号。如果指数为零或高于零,请使用该答案。如果它低于零,则执行按位补码 (~) 以获得大于给定搜索值的第一个元素(如果有)(根据 Array.BinarySearch 文档)。
【解决方案2】:

我会说一个数组(如果你愿意,可以是向量)最好保存它们。至于加权平均,求和,在零和和之间取一个随机数,取累积值较小的姓氏。 (例如这里,

附:这是累积的。

【讨论】:

    【解决方案3】:

    只是为了好玩,绝不是最佳选择

    List<Name> Names = //Load your structure into this
    
    List<String> NameBank = new List<String>();
    foreach(Name name in Names)
       for(int i = 0; i <= (int)(name.Weight*1000); i++)
         NameBank.Add(name.Name)
    

    然后:

    String output = NameBank[rand(NameBank.Count)];
    

    【讨论】:

      【解决方案4】:

      我已经创建了a C# library for randomly selected weighted items

      • 它实现了树选择和 walker 别名方法算法,为所有用例提供最佳性能。
      • 它经过单元测试和优化。
      • 它支持 LINQ。
      • 它是免费和开源的,在 MIT 许可下获得许可。

      一些示例代码:

      IWeightedRandomizer<string> randomizer = new DynamicWeightedRandomizer<string>();
      randomizer["Joe"] = 1;
      randomizer["Ryan"] = 2;
      randomizer["Jason"] = 2;
      
      string name1 = randomizer.RandomWithReplacement();
      //name1 has a 20% chance of being "Joe", 40% of "Ryan", 40% of "Jason"
      
      string name2 = randomizer.RandomWithRemoval();
      //Same as above, except whichever one was chosen has been removed from the list.
      

      【讨论】:

        猜你喜欢
        • 2012-09-11
        • 2011-05-29
        • 1970-01-01
        • 2011-02-28
        • 2010-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多