我有许多尺寸在 1-10 范围内的物品。
我想根据该物品成为该尺寸的百分比或概率来确定物品的尺寸。
例如:
物品成为 1 尺寸的几率 = 50%
概率
物品成为 5 尺寸的几率 = 20%
概率
物品成为 10 尺寸的几率 = 5%
概率
当然,我知道我需要使用一个Random
生成器。
但是,我想知道一些人用C#如何处理这个逻辑?
我有许多尺寸在 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
}
...
`
// 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;
}
我最终使用了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();
}