如何动态更改此随机数生成器的曲线?

时间:2017-10-28 03:37:37

标签: javascript algorithm

此函数生成一个随机数,但概率曲线有利于较低范围:

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);
}

我希望它在因子决定曲线的地方工作,因此因子1表示沿最小和最大范围的所有数字都是同等可能的,但0.5表示下降,其中2是1和3的可能性的一半是2的可能性的一半,依此类推。我在动态地搞清楚它时遇到了很多麻烦。

1 个答案:

答案 0 :(得分:1)

可以通过简单的算术功能来解决这个问题。该函数将用于将均匀分布的随机数映射到具有特殊分布的期望范围。

如果我们以0.5为例,每个后继者的概率为一半,我们得到一个类似的事件:

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

表示min = 0,max = 2,与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

注意什么?在max = 2的情况下,该集合由7(2^3 - 1)个元素组成,并且在max = 3的情况下,它是15(2^4 - 1)。因此,对于任意最大值,我们需要获得具有2^(max+1) - 1元素的事件集。

所以我们现在需要做的是

  • 1 .. 2^(max+1)-1(等于distrib)
  • 范围内创建一个随机数
  • 将此数字映射到相应的结果,如集合
  • 中所示

第一项任务很简单,只需致电getRandomNumber(1,2^(max+1)-1)。 第二个是通过计算此随机数的基数2的对数并将差值从最大值建立到最大值来完成的:

// 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;

如果min != 0怎么办? 这很容易处理:我们只是从max中减去它并将其添加到最终结果中。

除了0.5以外的分布是什么? 我们在公式中看到的臭名昭着的2只是1/0.5。对于任意值,我们将{2}替换为1/factor(来自您主题的因子,范围从0独占到1) 集合中的事件数由(1/factor)^0 + (1/factor)^1 + (1/factor)^2 + ...给出,等于((1/factor)^(max+1) - 1) / (1/factor - 1)

适用于任意min,max和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;

请注意,Java中不存在log2logb,但您可以使用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);
});
&#13;
<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>
&#13;
&#13;
&#13;