Three years ago I wrote some code to randomly distribute a set of test cases based on weight. I'm pretty sure I found an example online somewhere and copied it. I have written unit tests based on every outcome I can think of and no matter what I throw at this code it always works. I even took test cases from our production environment and every one of them work properly. Here's what's going on.
Test cases are assigned a percentages (represented by decimal places 0.0-1.0). The sum of the percentages must add up to 99-100% (99 allows for 33% splits). For example:
javascript var items = [ { name: "case 1", percentage: 0.20 }, { name: "case 2", percentage: 0.80 } ]
Test case one has a weight of 20% and so on. The code works like this.
- Create a random decimal between 0.00 and 1.00.
-
Loop the test cases and choose the first one that has a percentage greater than that of the random number i.e. if the random number is 0.12 test case one will be chosen. If the random number is 0.30 then test case two will be chosen. Based on this alone the distribution will be like this:
- Case 1: 20%
- Case 2: 60%
This is because 0.0 to 0.19 is 20% of the time. 20% of the time the first item will be chosen. Subtract that from the second number which is 0.80 to get 0.59. Random numbers from 0.20 to 0.79 will select case number two. This sums up to 59% of the time (0.20 to 0.79). When the random number is greater than 0.79 none of the cases will be chosen because they all fail to match step two of the algorithm. So far everything is working as expected. This will not suffice so there is another block of code. The magic part that has us all baffled.
- If no items were chosen based on the random number (i.e if the random number was greater than all percentages) then randomly sort the list and choose the first item in the list. This is done by sorting the list by random UUIDs not impacted by the items themselves. The psuedo code is this
if !selected { selected = items.sortBy(x => uuid())[0] }
On paper this says "take the percentages which do not distribute and evenly distribute them across the list". This amount can be determined by subtracting the highest percentage in the list from 1.00. For example, the above list of items has a maximum percentage of 0.79 (test case two). If the random number is greater than 0.79 then it will not select any items in the list. Subtract that from 1 to get 0.21 or 21%. So, the 21% of the time the random number is greater than 0.79 an item will be randomly selected. Knowing this, the last 21% should be divided into the two groups (0.105 for each group) giving an outcome like this:
- Case 1: 30.50%
- Case 2: 69.50%
But this is not what's happening. The items are coming out like I want them to:
- Case 1: 20.00%
- Case 2: 80.00%
By commenting out the block of code which randomly distributes the remaining number we can show that the outcome is as expected where the final 21% does not get distributed, test case 1 gets 20%, and test case 2 gets 59%. We have changed the amount of test cases.
It is not important that the percentages of the outcome are precise to the decimal. The test cases we are running only require a "close enough" approach. If the outcome is case 1: 0.2044, case 2: 0.7956 then it's okay.
Aucun commentaire:
Enregistrer un commentaire