如何根据百分比随机选择

本文关键字:随机 选择 百分比 何根 | 更新日期: 2023-09-27 18:24:07

我有一堆项目,它们超出了1-10大小的范围。

我想让物品的大小由百分比或物体达到该大小的可能性决定。。

例如:

大小的项目机会1=50%机会

大小的项目机会5=20%机会

大小的项目机会10=5%机会

当然,我知道我需要使用Random生成器。

但只是想知道你们中的一些人会如何在C#中理解这一逻辑?

如何根据百分比随机选择

首先:提供的概率加起来不到100%

50% + 20% + 5% = 75%

因此,您必须检查这些值。您可能想要生成这些每美分:

// Simplest, but not thread safe
private static Random s_Random = new Random();
...
int perCent = s_Random.Next(0, 100);
if (perCent < 50)               //  0 .. 49
{
    // return Item of size 1
}
else if (perCent < 50 + 20)     // 50 .. 69
{
    // return Item of size 5
}
else if (perCent < 50 + 20 + 5) // 70 .. 74 
{
    // return Item of size 10
} 
...

使用我的方法。它简单易懂。我不计算0…1范围内的部分,我只使用"概率池"(听起来很酷,是吗?)我列出了我想从中选择的所有元素。每个元素都有自己的机会。将最常见元素的几率设置为100是有用的,因此大多数稀有元素将为60或50。

在圆形图中,您可以看到池中每个元素的重量

在这里你可以看到轮盘累积概率的实现

`
// Some c`lass or struct for represent items you want to roulette
public class Item
{
    public string name; // not only string, any type of data
    public int chance;  // chance of getting this Item
}
public class ProportionalWheelSelection
{
    public static Random rnd = new Random();
    // Static method for using from anywhere. You can make its overload for accepting not only List, but arrays also: 
    // public static Item SelectItem (Item[] items)...
    public static Item SelectItem(List<Item> items)
    {
        // Calculate the summa of all portions.
        int poolSize = 0;
        for (int i = 0; i < items.Count; i++)
        {
            poolSize += items[i].chance;
        }
        // Get a random integer from 0 to PoolSize.
        int randomNumber = rnd.Next(0, poolSize) + 1;
        // Detect the item, which corresponds to current random number.
        int accumulatedProbability = 0;
        for (int i = 0; i < items.Count; i++)
        {
            accumulatedProbability += items[i].chance;
            if (randomNumber <= accumulatedProbability)
                return items[i];
        }
        return null;    // this code will never come while you use this programm right :)
    }
}
// Example of using somewhere in your program:
        static void Main(string[] args)
        {
            List<Item> items = new List<Item>();
            items.Add(new Item() { name = "Anna", chance = 100});
            items.Add(new Item() { name = "Alex", chance = 125});
            items.Add(new Item() { name = "Dog", chance = 50});
            items.Add(new Item() { name = "Cat", chance = 35});
            Item newItem = ProportionalWheelSelection.SelectItem(items);
        }

我是这样做的,也许对其他人有用。

public class Product
    {
        public int Id { get; set; }
        public int Name { get; set; }
        public int Percent { get; set; }
    }
    public class ChoiceItemModel
    {
        public int Id { get; set; }
        public int Percent { get; set; }
        public int Min { get; set; }
        public int Max { get; set; }
    }
public int ChoiceProduct(List<Product> products)
        {
            var chioiceItems = new List<ChoiceItemModel>();
            var percent = 0;
            foreach (var product in products.OrderByDescending(p => p.Percent).ToList())
            {
                chioiceItems.Add(new ChoiceItemModel()
                {
                    Id = product.Id,
                    Percent = product.Percent,
                    Min = percent,
                    Max = product.Percent + percent
                });
                percent = product.Percent + percent; //max
            }
            var random = new Random();
            var probability = random.Next(1, 100);
            var found = chioiceItems.FirstOrDefault(p => probability > p.Min && probability <= p.Max);
            if (found != null)
                return found.Id;
        }

我结束使用Oleksandr Martish的这一类,它针对泛型类型和Unity进行了修改,以防有人看到它的用处:)

public class ProportionalRandomSelector<T> {
    private readonly Dictionary<T, int> percentageItemsDict;
    public ProportionalRandomSelector() => percentageItemsDict = new();
    public void AddPercentageItem(T item, int percentage) => percentageItemsDict.Add(item, percentage);
    public T SelectItem() {
        
        // Calculate the summa of all portions.
        int poolSize = 0;
        foreach (int i in percentageItemsDict.Values) {
            poolSize += i;
        }
        // Get a random integer from 1 to PoolSize.
        int randomNumber = Random.Range(1, poolSize);
        // Detect the item, which corresponds to current random number.
        int accumulatedProbability = 0;
        foreach (KeyValuePair<T, int> pair in percentageItemsDict) {
            accumulatedProbability += pair.Value;
            if (randomNumber <= accumulatedProbability)
                return pair.Key;
        }
        
        return default;  // this code will never come while you use this programm right :)
    
    }
}
//Example of use. You can use any type for the item too, and don't need an internal struct for the use.
public class Behaviour : MonoBehaviour {
    
    ProportionalRandomSelector<string> randomSelector = new();
    randomSelector.AddPercentageItem("Option1", 20);
    randomSelector.AddPercentageItem("Option2", 30);
    randomSelector.AddPercentageItem("Option3", 30);
    randomSelector.AddPercentageItem("Option4", 15);
    randomSelector.AddPercentageItem("Option5", 5);
    string result = randomSelector.SelectItem();
    
}