javascriptprobabilitynon-uniform-distribution

Creating a Javascript function that returns random integers but with a specified distribution/"weight"


I have an array of values:

var my_arr = [/*all kinds of stuff*/]

I have a function that generates a random number, which I use as the index of an element in my_arr...

var RandomFromRange = function (min,max)
{
    return Math.floor(Math.random()*(max-min+1)+min);
};

...so I could do stuff like this:

my_arr[RandomFromRange(0,my_arr.length)];

What I want to do is to designate certain elements within my_arr as having "priority", so that RandomFromRange returns 5, say, 25% of the time, returns 4, 14% of the time, and returns any other number...

(100 - 25 - 14)/(my_arr.length - 2)

...% of the time.

As I was doing my research, I came across several posts that describe similar problems, but their answers are not in Javascript, and I, alas, don't have enough math to understand their general principles. Any advice would be appreciated.


Solution

  • This may not be as exact as what you are looking for, but this certainly works. Basically this code returns a random number specified from the min and max like yours, but only after addressing the priority numbers based on the chance given.

    First we gotta prioritize your priority numbers in the code. If there is no hit on your priority numbers, that is when we proceed to the normal RNG.

    //priority = list of numbers as priority,
    //chance = the percentage
    //min and max are your parameters
    
    var randomFromRange = function (min,max,priority,chance)
    {
      var val = null; //initialize value to return
    	
    	for(var i = 0; i < priority.length; i++){ //loop through priority numbers
    		
    		var roll = Math.floor(Math.random()*100); //roll the dice (outputs 0-100)
    		
    		if(chance > roll){ ///check if the chance is greater than the roll output, if true, there's a hit. Less chance value means less likely that the chance value is greater than the roll output
    			val = priority[i]; //make the current number in the priority loop the value to return;
    			break; //if there's a hit, stop the loop.
    		}
    		else{
    			continue; //else, keep looping through the priority list
    		}
    	}
    	
      //if there is no hit to any priority numbers, return a number from the min and max range
    	if(val == null){
    		val = Math.floor(Math.random()*(max-min+1)+min);
    	}
    	
      //return the value and do whatever you want with it
    	return val;
    };
    
    document.getElementsByTagName('body')[0].onclick = function (){
    	console.log(randomFromRange(0,10,[20,30],50));
    }
    <!DOCTYPE html>
    <html>
    <body style='height: 1000px; width: 100%;'></body>
    <script></script>
    </html>

    This code applies a single chance on all array of priority numbers. If you want individual chances for each number in the priority list, we gotta modify the structure and change parameters to a single array of objects that holds something like

    var priorityList = [{num: 4, chance: 25},
                        {num: 5, chance: 12}]
    

    etc