简体   繁体   中英

How can I change the curve of this random number generator dynamically?

This function generates a random number but with a probability curve that favors the lower range:

function getRandomLowNumber(min=1,max=100,factor=1){
  let num = getRandomDecimal(min,max);
  let rollDiff = num - min;
  let percent = (rollDiff) / (max - min);
  percent = 1 - (1 - percent) / factor;
  return Math.round(rollDiff * percent + min);
}

I want it to work where the factor decides the curve, so a factor of 1 means all numbers along the min and max range are equally probable, but 0.5 means a drop-off where 2 is half as likely as 1 and 3 is half as likely as 2, and so on. I'm having a lot of trouble figuring it out dynamically though.

It's possible to solve this with a simple arithmetical function. The function will be used to map an equally distributed random number to the desired range with the special distribution.

If we take your example with 0.5, which for each successor shall half it's probability, we get an event set like that:

#1 2 3 4 5 6 7
 0 0 0 0 1 1 2

for min=0, max=2 and like that for max=3:

#1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
 0 0 0 0 0 0 0 0 1 1 1 1 2 2 3

Notice something? In case of max=2 the set consists of 7 ( 2^3 - 1 ) elements, and in case of max=3 it's 15 ( 2^4 - 1 ). So for arbitrary max values we'd need to get event sets with 2^(max+1) - 1 elements.

So what we need to do now is

  • create a random number in the range 1 .. 2^(max+1)-1 (equal distrib)
  • map this number to the corresponding result as shown in the set

The first task is trivial, just call getRandomNumber(1,2^(max+1)-1) . The second one is done by computing the logarithm to base 2 of this random number and building the floor from the difference to max:

// for max==3 you get:
// rndnum :  1 2 3 4 5 6 7 8 9'0'1'2'3'4'5
// expflr :  0 1 1 2 2 2 2 3 3 3 3 3 3 3 3
// rndres :  3 2 2 1 1 1 1 0 0 0 0 0 0 0 0
int rndnum = getRandomNumber(1, Math.pow(2, max+1) - 1);
int expflr = Math.floor(log2(rndnum));
int rndres = max - expflr;

What if min != 0 ?
This is easy to handle: We just subtract it from max and add it to the final result.

What with distributions other than 0.5?
The notorious 2 that we saw in the formula is nothing but 1/0.5 . For an arbitrary value we replace the 2 with 1/factor (factor from your topic, ranges from 0 exclusive to 1).
The number of events in the set is given by (1/factor)^0 + (1/factor)^1 + (1/factor)^2 + ... which is equal to ((1/factor)^(max+1) - 1) / (1/factor - 1) .

Final version that works with arbitrary min, max and factor:

double base = 1.0 / factor;
int evtcnt = Math.floor(Math.pow(base, max-min+1) - 1) / (base-1));
int rndnum = getRandomNumber(1, evtcnt);
int expflr = Math.floor(logb((rndnum-1) * (base-1) + 1, base));
int rndres = max - expflr;

Note that log2 and logb do not exist in Java, but you can define them with log10(val) / log10(2.0) resp log10(val) / log10(base) .

 function logb(val, base) { return Math.log10(val) / Math.log10(base); } function getRandomNumber(min,max){ return Math.floor(Math.random()*(max-min+1)+min); } function getRandomLowNumber(min, max, factor) { var base = 1.0 / factor; var evtcnt = Math.floor(Math.pow(base, max-min+1) - 1) / (base-1); var rndnum = getRandomNumber(1, evtcnt); var expflr = Math.floor(logb((rndnum-1) * (base-1) + 1, base)); var rndres = max - expflr; return rndres; } function runit() { var min = document.getElementById('input-min').value; var max = document.getElementById('input-max').value; var factor = document.getElementById('input-factor').value; var times = document.getElementById('input-times').value; var list = {}; for (let i = 0; i < times; i++) { var number = getRandomLowNumber(min, max, factor); if (typeof list[number] == 'number') { list[number]++; } else { list[number] = 1; } } console.log('Min: ', min); console.log('Max: ', max); console.log('Factor: ', factor); console.log('Iterations: ', times); console.log('List: ', list); } function runClippy() { var name = 'Clippy'; if (clippy.load._data[name]) { return; } clippy.load(name, function(agent) { var animations = agent.animations(); $('.js-states').text(animations.join(' ')); agent.show(); agent.moveTo(400, 30); agent.speak("Hello, I see you're trying to run this sample. My name is " + name + " and yacc sent me here to help."); agent.moveTo(200, 100); agent.speak("There are four input fields to put parameters."); agent.moveTo(300, 50); agent.gestureAt(-100,50); agent.speak("The first two specify the minimum and maximum random value."); agent.moveTo(90,50); agent.gestureAt(0,-50); agent.speak("I'll put starting values for you here."); agent._addToQueue(function(complete) { $('.input-min').val("1"); $('.input-max').val("100"); complete(); }); agent.gestureAt(-100,50); agent.speak("The next field specifies the factor that will decrease the probability for each successor. It should range between 0 (exclusively) and 1. Let's try a value here."); agent._addToQueue(function(complete) { $('.input-factor').val("0.5"); complete(); }); agent.moveTo(550, 70); agent.gestureAt(-100,50); agent.speak("The final input field is used to specify the amount of random numbers to generate. I'll fill it in for you."); agent._addToQueue(function(complete) { $('.input-times').val("100"); complete(); }); agent.speak("Now, did you notice the big button at the bottom of the form? You can push it to start the calculation."); agent.moveTo(50, 120); agent.gestureAt(-100,50); agent.moveTo(90,50); agent.gestureAt(0,-50); agent.speak("Be careful with the amount of calculations. If the task takes too long, it might be aborted."); agent.moveTo(630, 200); agent.speak("So, now you can start on your own calculation of randoms. Be sure to fill in the fields properly, so that min <= max, or 0 < factor <= 1. Our lab is so busy at the moment that we spared a few safety belts."); agent._addToQueue(function(complete) { $('.wmd-input').val("# What are you trying to achieve?"); complete(); }); agent.moveTo(400, 30); agent.gestureAt(-100, 50); agent.speak("Please describe in short what you are trying to achieve"); agent._addToQueue(function(complete) { $('.wmd-input').val("# What are you trying to achieve?\\n\\n# What is the problem you're facing?"); complete(); }); agent.moveTo(400, 70); agent.gestureAt(-100, 50); agent.speak("Please describe the error you're getting, and/or post the error message you're getting"); agent._addToQueue(function(complete) { $('.wmd-input').val("# What are you trying to achieve?\\n\\n# What is the problem you're facing?\\n\\n#Show the code causing the problem"); complete(); }); agent.moveTo(400, 90); agent.gestureAt(-100, 50); agent.speak("Please post the code that causes your problem. Try to post it without clutter or unrelated code."); agent.speak("People who answer should be able to use your code to reproduce the error. Please lookup MVCE in the stack overflow help ."); agent.moveTo(630, 200); }); } $(document).ready(function(){ // $('.wmd-input').one('focus', function() {runClippy();}); $('.input-min').one('focus', runClippy); $('.input-max').one('focus', runClippy); $('.input-factor').one('focus', runClippy); $('.input-times').one('focus', runClippy); });
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel='stylesheet prefetch' href='https://cdn.rawgit.com/smore-inc/clippy.js/master/build/clippy.css'> <!--link rel="stylesheet prefetch" href="https://cdn.sstatic.net/Sites/stackoverflow/all.css?v=b0fb54f66683"--> <script src='https://cdn.rawgit.com/smore-inc/clippy.js/master/build/clippy.min.js'></script> <div> Min: <input value="0" id="input-min" class="input-min processed"> Max: <input value="100" id="input-max" class="input-max processed"><br> Factor: <input value="0.5" id="input-factor" class="input-factor processed"><br> #-Runs: <input value="1000000" id="input-times" class="input-times processed"><br> <button style="float: center;" onclick="runit()">--- Run that algorithm ---</button> </div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM