基于百分比随机选择的方法如何实现

14

我有许多尺寸在 1-10 范围内的物品。

我想根据该物品成为该尺寸的百分比或概率来确定物品的尺寸。

例如:

物品成为 1 尺寸的几率 = 50% 概率

物品成为 5 尺寸的几率 = 20% 概率

物品成为 10 尺寸的几率 = 5% 概率

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

但是,我想知道一些人用C#如何处理这个逻辑?


为什么这个问题被关闭并标记为“如何生成随机整数”的重复? - Anders Forsgren
1
将列表填充为50个1,20个5和5个10(以及其他值,直到列表中有100个项目)。然后获取0到99之间的随机数,并将其用作索引进入列表以获取相应的值。 - Corak
1
不,OP正在询问如何获得具有给定分布的随机数。投票以重新开放。 - David Hedlund
2
不,OP正在询问如何进行比例/轮盘选择,就像Corak的回答一样(这应该是一个答案!)。 - Anders Forsgren
4
这个问题当然也被问过了。可能是Distributed probability random number generator的重复。 - David Hedlund
1
“50% + 20% + 5% = 75%”不等于“100%”,那么完整的可能性集合是什么? - Dmitry Bychenko
4个回答

22

首先:提供的概率不加起来等于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
} 
...

太棒了。非常感谢。另一件事是提供的项目数量可以在5-50之间变化。那么有没有办法在1-50之间分配权重。例如 - 项目#1的最大尺寸为50。那么我如何使每个尺寸都有一个权重?因为在50内有大小为10、大小为2、大小为20等。我希望这是有意义的。 - coder4life22

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);
        }

0

我用这种方法完成了,或许对别人有用。

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;
        }

0

我最终使用了this类,该类是由Oleksandr Martysh创建的,经过泛型修改,并专门用于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();
    
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接