问题
I need help with the programming of a game.
You open a chest and with a given probability you find an item.
Item / Chance
A / 10%
B / 30%
C / 60%
Random random = new Random();
int x = random.Next(1, 101);
if (x < 11) // Numbers 1..10 ( A -> 10% )
{
do_something1(); d
}
else if (x < 41) // Numbers 11..40 ( B -> 30 % )
{
do_something2();
}
else if (x < 101) // Numbers 41..100 ( C -> 60 % )
{
do_something3();
}
Does this example really make sense, in terms of probability? Do you have another solution?
Thank you in advance!
回答1:
I agree with @Timothy, I'd go for a more maintainable solution, where you're not relying on magic numbers to split your probabilities. Also, it's personal preference, but I'd also call it ratio rather than percent, otherwise "100" becomes another magic number, and you limit yourself to a minimum probability of 1%. This way you can split it 1:10:200 or however you please:
public static readonly int RATIO_CHANCE_A = 10;
public static readonly int RATIO_CHANCE_B = 30;
// ...
public static readonly int RATIO_CHANCE_N = 60;
public static readonly int RATIO_TOTAL = RATIO_CHANCE_A
+ RATIO_CHANCE_B
// ...
+ RATIO_CHANCE_N;
Random random = new Random();
int x = random.Next(0, RATIO_TOTAL);
if ((x -= RATIO_CHANCE_A) < 0) // Test for A
{
do_something1();
}
else if ((x -= RATIO_CHANCE_B) < 0) // Test for B
{
do_something2();
}
// ... etc
else // No need for final if statement
{
do_somethingN();
}
EDIT: More generalised solution
回答2:
I realize this is a tad late, but here's an example of doing it without consts, laborious if/else and/or switch statements ;
public class WeightedChanceParam
{
public Action Func { get; }
public double Ratio { get; }
public WeightedChanceParam(Action func, double ratio)
{
Func = func;
Ratio = ratio;
}
}
public class WeightedChanceExecutor
{
public WeightedChanceParam[] Parameters { get; }
private Random r;
public double RatioSum
{
get { return Parameters.Sum(p => p.Ratio); }
}
public WeightedChanceExecutor(params WeightedChanceParam[] parameters)
{
Parameters = parameters;
r = new Random();
}
public void Execute()
{
double numericValue = r.NextDouble() * RatioSum;
foreach (var parameter in Parameters)
{
numericValue -= parameter.Ratio;
if (!(numericValue <= 0))
continue;
parameter.Func();
return;
}
}
}
usage example :
WeightedChanceExecutor weightedChanceExecutor = new WeightedChanceExecutor(
new WeightedChanceParam(() =>
{
Console.Out.WriteLine("A");
}, 25), //25% chance (since 25 + 25 + 50 = 100)
new WeightedChanceParam(() =>
{
Console.Out.WriteLine("B");
}, 50), //50% chance
new WeightedChanceParam(() =>
{
Console.Out.WriteLine("C");
}, 25) //25% chance
);
//25% chance of writing "A", 25% chance of writing "C", 50% chance of writing "B"
weightedChanceExecutor.Execute();
回答3:
So to conclude the solutions here is a solution for any number of chances without a lot of if-else statements but a switch-case instead:
int[] chances = { 1, 23, 14, 49, 61 };
int totalRatio = 0;
foreach(int c in chances)
totalRatio += c;
Random random = new Random();
int x = random.Next(0, totalRatio);
int iteration = 0; // so you know what to do next
foreach(int c in chances)
{
iteration++;
if((x -= c) < 0)
break;
}
switch(iteration)
{
case 1:
case 2:
//...
default:
}
回答4:
When I combine all your answers, then this should work here as well, right?
double number;
Random x = new Random();
number = x.NextDouble();
double RATIO_CHANCE_A = 0.10;
double RATIO_CHANCE_B = 0.30;
double RATIO_CHANCE_C = 0.60;
double RATIO_TOTAL = RATIO_CHANCE_A + RATIO_CHANCE_B + RATIO_CHANCE_C;
if ( number < RATIO_CHANCE_A ) // A -> 10%
{
do_something1();
}
else if ( number < RATIO_CHANCE_B + RATIO_CHANCE_A ) // B -> 30%
{
do_something2();
}
else if ( number < RATIO_TOTAL ) // C -> 60%
{
do_something3();
}
来源:https://stackoverflow.com/questions/46563490/c-sharp-weighted-random-numbers