代码之家  ›  专栏  ›  技术社区  ›  Zied CHAARI

以概率从列表中选取随机元素

  •  5
  • Zied CHAARI  · 技术社区  · 7 年前

    我有一个包含四项(a、B、C、D)的列表。每个项目都有被选择的可能性。例如,A有74%的机会被选中,B有15%,C有7%,D有4%。

    我想创建一个函数,根据概率随机选择一个项目。

    需要帮忙吗?

    3 回复  |  直到 7 年前
        1
  •  5
  •   Aleks Andreev Md. Suman Kabir    7 年前

    为项目定义一个类,如下所示:

    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) 复杂性您可以使用二进制搜索对其进行优化(因为 转换 集合已排序)

        2
  •  1
  •   Kevin    7 年前

    我很抱歉这样回答这个问题——我有点把它看作是一种“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);
    
        3
  •  -2
  •   Todor Balabanov    7 年前
    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)] );
        }
    }