C# weighted random numbers

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

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

Tags:

C#

Random

Unity3D