Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Random weighted choice

Consider the class below that represents a Broker:

public class Broker {     public string Name = string.Empty;     public int Weight = 0;      public Broker(string n, int w)     {         this.Name = n;         this.Weight = w;     } } 

I'd like to randomly select a Broker from an array, taking into account their weights.

What do you think of the code below?

class Program     {         private static Random _rnd = new Random();          public static Broker GetBroker(List<Broker> brokers, int totalWeight)         {             // totalWeight is the sum of all brokers' weight              int randomNumber = _rnd.Next(0, totalWeight);              Broker selectedBroker = null;             foreach (Broker broker in brokers)             {                 if (randomNumber <= broker.Weight)                 {                     selectedBroker = broker;                     break;                 }                  randomNumber = randomNumber - broker.Weight;             }              return selectedBroker;         }           static void Main(string[] args)         {             List<Broker> brokers = new List<Broker>();             brokers.Add(new Broker("A", 10));             brokers.Add(new Broker("B", 20));             brokers.Add(new Broker("C", 20));             brokers.Add(new Broker("D", 10));              // total the weigth             int totalWeight = 0;             foreach (Broker broker in brokers)             {                 totalWeight += broker.Weight;             }              while (true)             {                 Dictionary<string, int> result = new Dictionary<string, int>();                  Broker selectedBroker = null;                  for (int i = 0; i < 1000; i++)                 {                     selectedBroker = GetBroker(brokers, totalWeight);                     if (selectedBroker != null)                     {                         if (result.ContainsKey(selectedBroker.Name))                         {                             result[selectedBroker.Name] = result[selectedBroker.Name] + 1;                         }                         else                         {                             result.Add(selectedBroker.Name, 1);                         }                     }                 }                   Console.WriteLine("A\t\t" + result["A"]);                 Console.WriteLine("B\t\t" + result["B"]);                 Console.WriteLine("C\t\t" + result["C"]);                 Console.WriteLine("D\t\t" + result["D"]);                  result.Clear();                 Console.WriteLine();                 Console.ReadLine();             }         }     } 

I'm not so confident. When I run this, Broker A always gets more hits than Broker D, and they have the same weight.

Is there a more accurate algorithm?

Thanks!

like image 381
LeoD Avatar asked Sep 11 '08 14:09

LeoD


People also ask

What is random choice method?

Python Random choice() Method The choice() method returns a randomly selected element from the specified sequence. The sequence can be a string, a range, a list, a tuple or any other kind of sequence.

What is weighted random number?

1) calculate the sum of all the weights. 2) pick a random number that is 0 or greater and is less than the sum of the weights. 3) go through the items one at a time, subtracting their weight from your random number, until you get the item where the random number is less than that item's weight.


1 Answers

Your algorithm is nearly correct. However, the test should be < instead of <=:

if (randomNumber < broker.Weight) 

This is because 0 is inclusive in the random number while totalWeight is exclusive. In other words, a broker with weight 0 would still have a small chance of being selected – not at all what you want. This accounts for broker A having more hits than broker D.

Other than that, your algorithm is fine and in fact the canonical way of solving this problem.

like image 133
Konrad Rudolph Avatar answered Oct 15 '22 04:10

Konrad Rudolph