【问题标题】:Selecting Random Item from List given probability of each item给定每个项目的概率,从列表中选择随机项目
【发布时间】:2016-10-31 09:20:24
【问题描述】:

抱歉标题措辞不当......

我有一个名为 NGram 的对象

class NGram
{
     //other properties
     double Probability {get; set;} //Value between 1 and 0 
}

现在假设我有一个这些对象的列表...

List<NGrams> grams = GetNGrams();
Debug.Assert(grams.Sum(x => x.Probability) == 1);

如何在考虑概率分布的同时从该列表中选择一个随机项目。

例如,假设grams[0].Probability == 0.5 那么应该有50% 的机会选择grams[0]

我想我可能需要rand.NextDouble() 之类的东西,但我不知所措。

【问题讨论】:

  • 您想根据他们的概率值进行选择吗?

标签: c# random probability n-gram


【解决方案1】:

对列表进行排序,按概率排序,升序。

对列表中所有元素的概率字段求和。我们称其为 P

获取[0,P]之间的随机数,我们称之为r

迭代列表,同时将概率总和的累积值保持到您正在迭代的当前元素 (pe)。当找到第一个 pe >= r

的元素时结束搜索

数组中所有元素总和为 1 的情况现在只是一种特殊情况 :)

【讨论】:

  • 谢谢。我已经按照你说的实现了,但是我注意到了这个问题:假设我生成了一个随机数 0.955。列表中没有一个项目的概率为 0.955,因此在这种情况下,概率值 >= r 永远不会为真
  • 忘了说你应该积累概率。在迭代时,让我回顾一下
  • 好的,已经用正确的算法编辑了。使用累积概率检查随机值时,您不会遇到您指出的问题
【解决方案2】:

在伪代码中

r = Get a random number between 0 and 1
sum = 0
i = 0
Loop  
    sum = sum + grams[i].Probability  
    If sum >= r Then  
        Exit Loop
    End
    i = i + 1  
End
i is the index of the random item in the list

这个想法是对项目的概率求和,直到总和大于或等于一个随机数。由于概率总和为 1,并且随机数在 0 .. 1 范围内,因此无论如何您都会找到一个项目。概率较大的项目更有可能被选中。

∑P= 0 0.08     0.3 0.43 0.53          0.88  1
    +--+--------+----+---+-------------+----+
    |  |        |    |   |             |    |
    +--+--------+----+---+-------------+----+ 
i =  0      1      2   3       4         5  

您可以想象每个项目的长度等于其分配的概率。该算法就像向长度为 1 的尺子随机投掷飞镖,所有概率都沿尺子堆叠。物品被击中的概率与其大小成正比(即其分配的概率)。

【讨论】:

  • 假设你有 {0.7, 0.15, 0.15} 这种数据,这个算法行得通吗!?
【解决方案3】:

试试这个:

List<NGram> grams = new List<NGram>()
{
    new NGram() { Probability = 0.5 },
    new NGram() { Probability = 0.35 },
    new NGram() { Probability = 0.15 }
};

var rnd = new Random();

var result =
    grams
        .Aggregate(
            new { sum = 0.0, target = rnd.NextDouble(), gram = (NGram)null },
            (a, g) =>
                a.gram == null && a.sum + g.Probability >= a.target
                    ? new { sum = a.sum + g.Probability, a.target, gram = g }
                    : new { sum = a.sum + g.Probability, a.target, a.gram });

它给了我这样的结果:

【讨论】:

    【解决方案4】:

    这是一种更通用的方法(意味着您不需要断言概率加到 1):

    static Random rand = new Random();
    
    public NGram GetRandom(IEnumerable<NGram> pool)
    {
         // get universal probability 
         double u = pool.Sum (p => p.Probability);
    
         // pick a random number between 0 and u
         double r = rand.NextDouble() * u;
    
         double sum = 0;
         foreach(NGram n in pool)
         {
             // loop until the random number is less than our cumulative probability
             if(r <= (sum = sum + n.Probability))
             {
                return n;
             }
         }
         // should never get here
         return null;
    }
    

    【讨论】:

    • 啊哈。我错过了总和的累积线。现在一切正常。谢谢你所有的答案,但我先用了这个 :) William
    • 假设你有 {0.7, 0.15, 0.15} 这种数据,这个算法行得通吗!?
    • @FatihTürker 试试看!从表面上看,我不明白为什么它不起作用。
    猜你喜欢
    • 2011-02-15
    • 1970-01-01
    • 2017-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-11
    • 1970-01-01
    • 2021-11-17
    相关资源
    最近更新 更多