Calculation of the next item in percentage distribution

I am working on a project that includes forwarding phone calls to several destinations.

For example, I want:

  • 10% of calls to go to destination
  • 20% of calls to destination B
  • 30% of calls to destination C
  • 40% of calls to destination D

The number of destinations and their percentages must be customizable.




I was thinking about how to do this while playing with spreadsheets and some code, and I came up with the following:

For each destination, take a random number, multiply it by a percentage and select the destination with the highest number. Like this:

Item: RANDOM * PERCENTAGE = RESULT A: 48 * 10 = 480 B: 33 * 20 = 660 C: 81 * 30 = 2430 <--- Highest number, select C D: 5 * 40 = 200 

I thought I could handle it, since D would be explicitly selected the most, followed by C, then B and least A.

But that will not work. If I do this 5,000 times and calculate the actual percentage of the time that each destination was selected, I get the following:

  • 1% of calls to go to destination A
  • 12% of calls to destination B
  • 31% of calls to destination C
  • 56% of calls to destination D



Here is the code I used for testing:

 // Initialise item weighting percentages Dictionary<string, int> weighting = new Dictionary<string, int>(); weighting["A"] = 10; //10% weighting["B"] = 20; //20% weighting["C"] = 30; //30% weighting["D"] = 40; //40% (total = 100%) // Initialise data set used for each iteration Dictionary<string, int> data = new Dictionary<string, int>(); // Initialise counts of the selected items Dictionary<string, int> count = new Dictionary<string, int>(); count["A"] = 0; count["B"] = 0; count["C"] = 0; count["D"] = 0; Random rand = new Random(); // Loop 5000 times for (int i = 0; i < 5000; i++) { // For each item, get a random number between 0 and 99 // and multiply it by the percentage to get a // weighted random number. data["A"] = rand.Next(100) * weighting["A"]; data["B"] = rand.Next(100) * weighting["B"]; data["C"] = rand.Next(100) * weighting["C"]; data["D"] = rand.Next(100) * weighting["D"]; // Find which item came out on top and increment the count string sel = data.First(x => x.Value == data.Max(y => y.Value)).Key; count[sel]++; // Log, so you can see whats going on... if (i < 15) Console.WriteLine("A:{0:00000} B:{1:00000} C:{2:00000} D:{3:00000} SELECTED:{4}", data["A"], data["B"], data["C"], data["D"], sel); else if (i == 15) Console.WriteLine("..."); } // Output the results, showing the percentage of the number // occurrances of each item. Console.WriteLine(); Console.WriteLine("Results: "); Console.WriteLine(" A = {0}%", 100 * ((double)count["A"] / (double)count.Sum(z => z.Value))); Console.WriteLine(" B = {0}%", 100 * ((double)count["B"] / (double)count.Sum(z => z.Value))); Console.WriteLine(" C = {0}%", 100 * ((double)count["C"] / (double)count.Sum(z => z.Value))); Console.WriteLine(" D = {0}%", 100 * ((double)count["D"] / (double)count.Sum(z => z.Value))); 

results :

 A:00780 B:00300 C:01740 D:03680 SELECTED:D A:00600 B:00660 C:00060 D:03400 SELECTED:D A:00900 B:01880 C:00510 D:00720 SELECTED:B A:00260 B:01380 C:00540 D:01520 SELECTED:D A:00220 B:01960 C:00210 D:02080 SELECTED:D A:00020 B:01400 C:01530 D:00120 SELECTED:C A:00980 B:00400 C:01560 D:03280 SELECTED:D A:00330 B:00300 C:01500 D:03680 SELECTED:D A:00590 B:00460 C:02730 D:02400 SELECTED:C A:00580 B:01900 C:02040 D:01320 SELECTED:C A:00620 B:01320 C:00750 D:01760 SELECTED:D A:00320 B:01040 C:01350 D:03640 SELECTED:D A:00340 B:01520 C:02010 D:03880 SELECTED:D A:00850 B:01420 C:00480 D:03400 SELECTED:D A:00560 B:00680 C:00030 D:00000 SELECTED:B ... Results: A = 1.44% B = 11.54% C = 30.6% D = 56.42% 

Can anyone suggest a way to fix this so that real interest comes out as configured?




And for bonus points, someone can suggest a way to do something similar, but not use random numbers, so the sequence of the selected destinations will be clearly defined. Using the above example, output this sequence each time:

 ABCDBCDCDD ABCDBCDCDD ABCDBCDCDD ABCDBCDCDD ... 

(note that the sequence is evenly distributed)

Thank. Ben

+3
c # algorithm random sequence
03 Sep '10 at 8:28
source share
4 answers

Well, I have done this many times in simulations, so here is the main method I use (without correct error checking):

You need to imagine that a line is drawn through a page going from 0 to 100. Now what we do is divide this line proportionally between your destinations. Then we use random numbers to select a point on this line. The destination that has this area of ​​the line is selected.

EDIT: Attempting a line chart

 |-----------------------------------------------------| Line 1 to 100 |-----|----------|---------------|--------------------| Line split proportionally 0 A 10 B 30 C 60 D 100 

We can do this as follows.

Suppose your target percentages are in an array and not in separate variables.

 int totalPercentages = 0; int destinationsIndex = -1; int randomNumberBetween0and100 = GetRandomNumber(); for(int i = 0; i < destinationPercentageArrays.Length; i++) { totalPercentages += destinationPercentageArrays[i]; if (totalPercentages > randomNumberBetween0and100) { destinationIndex = i; break; } } if (destinationIndex == -1) { throw new Exception("Something went badly wrong."); } 

The destinationIndex variable now points to the selected destination.

+5
Sep 03 2018-10-09T00:
source share

To distribute the percentage that you gave, do the following:

Create a random number from 1 to 100 (inclusive)

 If < 10 A If > 10 < 30 B If > 30 < 60 C If > 60 D 



Regarding the question of how to have a specific list, just put the recipients in order in an array and list them one at a time. When you're done, start over at the beginning.

 string[] destinations = new string[] { "A", "B", "C", "D", ... } int counter = 0; //when need routing RouteTo(destinations[counter]); counter++; if (counter == destinations.Length) { counter = 0; } 
+2
Sep 03 '10 at 8:39
source share

Another option is to fill out a list of size 100 using the for loop and insert each value times its weight. Then randomly select a list item.

Example, short list (10 items)

  • 5x A
  • 4x B
  • 1x C

List = {A, A, A, A, A, B, B, B, B, C}

Random value from 0 to 9.

+1
03 Sep '10 at 8:38
source share

This will create a random list of 100 characters in length, i.e. ABCDBCDCDD ...

  static void Main() { var weighting = new Dictionary<char, int>(); weighting['A'] = 10; //10% weighting['B'] = 20; //20% weighting['C'] = 30; //30% weighting['D'] = 40; //40% (total = 100%) var test = CreateOrder(weighting); } static IEnumerable<char> CreateOrder(Dictionary<char, int> weighting) { var list = new List<KeyValuePair<int, char>>(); var random = new Random(); foreach (var i in weighting) { for (int j = 0; j < i.Value; j++) { list.Add(new KeyValuePair<int, char>(random.Next(), i.Key)); } } return list.OrderBy(u=>u.Key).Select(u => u.Value); } 
0
03 Sep '10 at 9:11
source share



All Articles