以概率从列表中选择随机元素
Pick random element from list with probability
我有一个包含四个项目(A、B、C、D)的列表。每个项目都有被选中的概率。假设 A 有 74% 的机会被选中,B 15%,C 7%,D 4%。
我想创建一个根据概率随机选择项目的函数。
有什么帮助吗?
像这样为您的项目定义 class:
class Items<T>
{
public double Probability { get; set; }
public T Item { get; set; }
}
然后初始化它
var initial = new List<Items<string>>
{
new Items<string> {Probability = 74 / 100.0, Item = "A"},
new Items<string> {Probability = 15 / 100.0, Item = "B"},
new Items<string> {Probability = 7 / 100.0, Item = "C"},
new Items<string> {Probability = 4 / 100.0, Item = "D"},
};
然后你需要将它转换为从 0 到 1 的概率总和
var converted = new List<Items<string>>(initial.Count);
var sum = 0.0;
foreach (var item in initial.Take(initial.Count - 1))
{
sum += item.Probability;
converted.Add(new Items<string> {Probability = sum, Item = item.Item});
}
converted.Add(new Items<string> {Probability = 1.0, Item = initial.Last().Item});
现在您可以从 converted
集合中选择一项,概率为:
var rnd = new Random();
while (true)
{
var probability = rnd.NextDouble();
var selected = converted.SkipWhile(i => i.Probability < probability).First();
Console.WriteLine($"Selected item = {selected.Item}");
}
注意:我的实现有 O(n)
复杂性。您可以使用二进制搜索对其进行优化(因为 converted
集合中的值已排序)
using System;
public class Test{
private static String[] values = {"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","B","B","B","B","B","B","B","B","B","B","B","B","B","B","C","C","C","C","C","C","C","D","D","D","D",};
private static Random PRNG = new Random();
public static void Main(){
Console.WriteLine( values[PRNG.Next(values.Length)] );
}
}
很抱歉以这样的方式回答这个问题 - 我有点将其视为一种 "Euler.Net" 谜题,以及一种玩泛型的方式。
无论如何,这是我的做法:
public class WeightedItem<T>
{
private T value;
private int weight;
private int cumulativeSum;
private static Random rndInst = new Random();
public WeightedItem(T value, int weight)
{
this.value = value;
this.weight = weight;
}
public static T Choose(List<WeightedItem<T>> items)
{
int cumulSum = 0;
int cnt = items.Count();
for (int slot = 0; slot < cnt; slot++)
{
cumulSum += items[slot].weight;
items[slot].cumulativeSum = cumulSum;
}
double divSpot = rndInst.NextDouble() * cumulSum;
WeightedItem<T> chosen = items.FirstOrDefault(i => i.cumulativeSum >= divSpot);
if (chosen == null) throw new Exception("No item chosen - there seems to be a problem with the probability distribution.");
return chosen.value;
}
}
用法:
WeightedItem<string> alice = new WeightedItem<string>("alice", 1);
WeightedItem<string> bob = new WeightedItem<string>("bob", 1);
WeightedItem<string> charlie = new WeightedItem<string>("charlie", 1);
WeightedItem<string> diana = new WeightedItem<string>("diana", 4);
WeightedItem<string> elaine = new WeightedItem<string>("elaine", 1);
List<WeightedItem<string>> myList = new List<WeightedItem<string>> { alice, bob, charlie, diana, elaine };
string chosen = WeightedItem<string>.Choose(myList);
我有一个包含四个项目(A、B、C、D)的列表。每个项目都有被选中的概率。假设 A 有 74% 的机会被选中,B 15%,C 7%,D 4%。
我想创建一个根据概率随机选择项目的函数。
有什么帮助吗?
像这样为您的项目定义 class:
class Items<T>
{
public double Probability { get; set; }
public T Item { get; set; }
}
然后初始化它
var initial = new List<Items<string>>
{
new Items<string> {Probability = 74 / 100.0, Item = "A"},
new Items<string> {Probability = 15 / 100.0, Item = "B"},
new Items<string> {Probability = 7 / 100.0, Item = "C"},
new Items<string> {Probability = 4 / 100.0, Item = "D"},
};
然后你需要将它转换为从 0 到 1 的概率总和
var converted = new List<Items<string>>(initial.Count);
var sum = 0.0;
foreach (var item in initial.Take(initial.Count - 1))
{
sum += item.Probability;
converted.Add(new Items<string> {Probability = sum, Item = item.Item});
}
converted.Add(new Items<string> {Probability = 1.0, Item = initial.Last().Item});
现在您可以从 converted
集合中选择一项,概率为:
var rnd = new Random();
while (true)
{
var probability = rnd.NextDouble();
var selected = converted.SkipWhile(i => i.Probability < probability).First();
Console.WriteLine($"Selected item = {selected.Item}");
}
注意:我的实现有 O(n)
复杂性。您可以使用二进制搜索对其进行优化(因为 converted
集合中的值已排序)
using System;
public class Test{
private static String[] values = {"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","B","B","B","B","B","B","B","B","B","B","B","B","B","B","C","C","C","C","C","C","C","D","D","D","D",};
private static Random PRNG = new Random();
public static void Main(){
Console.WriteLine( values[PRNG.Next(values.Length)] );
}
}
很抱歉以这样的方式回答这个问题 - 我有点将其视为一种 "Euler.Net" 谜题,以及一种玩泛型的方式。
无论如何,这是我的做法:
public class WeightedItem<T>
{
private T value;
private int weight;
private int cumulativeSum;
private static Random rndInst = new Random();
public WeightedItem(T value, int weight)
{
this.value = value;
this.weight = weight;
}
public static T Choose(List<WeightedItem<T>> items)
{
int cumulSum = 0;
int cnt = items.Count();
for (int slot = 0; slot < cnt; slot++)
{
cumulSum += items[slot].weight;
items[slot].cumulativeSum = cumulSum;
}
double divSpot = rndInst.NextDouble() * cumulSum;
WeightedItem<T> chosen = items.FirstOrDefault(i => i.cumulativeSum >= divSpot);
if (chosen == null) throw new Exception("No item chosen - there seems to be a problem with the probability distribution.");
return chosen.value;
}
}
用法:
WeightedItem<string> alice = new WeightedItem<string>("alice", 1);
WeightedItem<string> bob = new WeightedItem<string>("bob", 1);
WeightedItem<string> charlie = new WeightedItem<string>("charlie", 1);
WeightedItem<string> diana = new WeightedItem<string>("diana", 4);
WeightedItem<string> elaine = new WeightedItem<string>("elaine", 1);
List<WeightedItem<string>> myList = new List<WeightedItem<string>> { alice, bob, charlie, diana, elaine };
string chosen = WeightedItem<string>.Choose(myList);