Generate A Weighted Random Number

前端 未结 11 2206
予麋鹿
予麋鹿 2020-11-22 12:40

I\'m trying to devise a (good) way to choose a random number from a range of possible numbers where each number in the range is given a weight. To put it simply: given the

11条回答
  •  名媛妹妹
    2020-11-22 12:49

    This one is in Mathematica, but it's easy to copy to another language, I use it in my games and it can handle decimal weights:

    weights = {0.5,1,2}; // The weights
    weights = N@weights/Total@weights // Normalize weights so that the list's sum is always 1.
    min = 0; // First min value should be 0
    max = weights[[1]]; // First max value should be the first element of the newly created weights list. Note that in Mathematica the first element has index of 1, not 0.
    random = RandomReal[]; // Generate a random float from 0 to 1;
    For[i = 1, i <= Length@weights, i++,
        If[random >= min && random < max,
            Print["Chosen index number: " <> ToString@i]
        ];
        min += weights[[i]];
        If[i == Length@weights,
            max = 1,
            max += weights[[i + 1]]
        ]
    ]
    

    (Now I'm talking with a lists first element's index equals 0) The idea behind this is that having a normalized list weights there is a chance of weights[n] to return the index n, so the distances between the min and max at step n should be weights[n]. The total distance from the minimum min (which we put it to be 0) and the maximum max is the sum of the list weights.

    The good thing behind this is that you don't append to any array or nest for loops, and that increases heavily the execution time.

    Here is the code in C# without needing to normalize the weights list and deleting some code:

    int WeightedRandom(List weights) {
        float total = 0f;
        foreach (float weight in weights) {
            total += weight;
        }
    
        float max = weights [0],
        random = Random.Range(0f, total);
    
        for (int index = 0; index < weights.Count; index++) {
            if (random < max) {
                return index;
            } else if (index == weights.Count - 1) {
                return weights.Count-1;
            }
            max += weights[index+1];
        }
        return -1;
    }
    

提交回复
热议问题