3

Okay, so my problem is that I have three random number generators and I want the value of all three randomly generated numbers (generated from 1-5) to add up to 9. For example, I randomly generate a 4, a 3 and a 2. And on another click of the button, a 5, a 3 and a 1. I've tried for a while to do this but just can not figure it out.

        function statGen() {
            var x = document.getElementById("number");
            x.innerHTML = Math.floor((Math.random() * 5) + 1);
            var a = document.getElementById("agl");
            a.innerHTML = Math.floor((Math.random() * 4) + 1);
            var l = document.getElementById("lck");
            l.innerHTML = Math.floor((Math.random() * 3) + 1);
        }
 <button id="statbutton" onclick="statGen()">Get Numbers</button>       
        <p id="main">Main:</p>
        <p id="number"></p>
        <br/>
        <p id="Agl">Agl:</p>
        <p id="agl"></p>
        <br/>
        <p id="Lck">Lck:</p>
        <p id="lck"></p>

I have tried to run it through a loop multiple times until it was the number 9, but none of those worked.

CrossKing
  • 31
  • 1
  • 3
  • You need to define what you mean by "random" here. One interpretation would be "of all permutations of 3 numbers 1-5 which add up to 9, choose one at random". That's a lot different than choosing one random number, then a second from the remainder, then a third. – Mark Peters Apr 10 '15 at 02:47
  • The last number can't be random in your code. – Ja͢ck Apr 10 '15 at 02:47
  • this sujet already exist with a good answer http://stackoverflow.com/questions/1527803/generating-random-numbers-in-javascript-in-a-specific-range – Martin Dubois Apr 10 '15 at 02:48
  • @MartinDubois that discusses a different topic. – Ja͢ck Apr 10 '15 at 02:48
  • Since your total always has to equal 9, only two numbers can be found with a randomizer and then the 3rd would be the difference between the sum of the first two and 9. For the 1st number, the max value would be 5. Easy enough. The 2nd number has a conditional if the 1st number is greater than 3. So you would generate a random number with a max that is the lesser of 5 and the difference between 8 and the 1st number. Then the 3rd would be the difference between the sum of the 1st and 2nd numbers and 9. Happy coding! – Jason Cust Apr 10 '15 at 02:59

5 Answers5

1

All of the answers so far do not have good randomness properties. By choosing the first number, then the second, then the third, you have a large bias towards the extremes.

Let me illustrate with a dice example. Say you want to roll three dice where they add up to 8. If you roll them one at a time, there's a 1/6 chance that the permutation you end up with will be [6, 1, 1]. That's because after you've rolled the 6 (at 1/6 probability), the only acceptable remaining numbers are 1 and 1.

In reality, [6, 1, 1] is not nearly 1/6th of all acceptable rolls. For example, starting with a 3 gives [3, 1, 4], [3, 2, 3], [3, 3, 2], [3, 4, 1]. So having a 3 as the first number should be four times as likely as having a 6. But that's not the case with these other approaches!

So there's a few ways to get "good" randomness. The simplest is actually to just roll all three "dice", and if the results don't meet your constraint, roll them all again.

function roll() {
   return Math.floor(Math.random() * 5 + 1); //1-5 uniformly
}

function chooseNumbers() {
   var x = 0, y = 0, z = 0;

   while (x + y + z !== 9) {
      x = roll();
      y = roll();
      z = roll();
   }
   return [x, y, z];
}

This will be extremely fast for most purposes, but you do end up throwing out a decent amount of rolls. Another option is to enumerate (using code, or manually) all of the permutations you could have, and then choose an index using a uniform random number.

var permutations = [
   [5, 3, 1],
   [5, 2, 2],
   [5, 1, 3],
   [4, 4, 1],
   //...
   [1, 3, 5]
];

function chooseNumbers() {
   return permutations[Math.floor(Math.random() * permutations.length)];
}
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • Yep. I didn't think about the "randomness" of the result at all. I was totally focused on just meeting the basic requirement. – Seamus Apr 10 '15 at 03:47
  • I put in all of the permutations by hand but I can't get them to display. I used function getStats() { var permutations = document.getElementById("number"); permutations.innerHTML = [Math.floor(Math.random() * permutations.length)]; } But it won't work. It says "NaN" where the numbers should appear. I'm new to this and this is blowing my mind... Thank you for the method to getting it. – CrossKing Apr 10 '15 at 16:20
  • @CrossKing: My method was to give you the array of 3 random numbers. You still need to correctly show it on your page. First of all, you are using "permutations" as the variable containing your DOM element "number". Call that something different, because it's conflicting with your array variable. – Mark Peters Apr 10 '15 at 16:48
  • @MarkPeters Okay, I think I [found a way](http://stackoverflow.com/a/29552555/2300664) to improve upon your method in some cases. – Seamus Apr 10 '15 at 19:18
0

last random number r3 depends on r1 and r2 so it's considered to be random.

    function statGen() {
        var x = document.getElementById("number");
        var r1=Math.floor((Math.random() * 9) + 1);
        var r2=Math.floor((Math.random() * (8-r1)) + 1);
        var r3=9-r1-r2;
        x.innerHTML = r1;
        var a = document.getElementById("agl");
        a.innerHTML = r2;
        var l = document.getElementById("lck");
        l.innerHTML = r3;
    }
Omar Elawady
  • 3,300
  • 1
  • 13
  • 17
0

Something like this? (This is pseudocode):

var first = Math.floor((Math.random() * 5) + 1);

var second = Math.floor((Math.random() * 8-first) + 1);

var third = Math.floor((Math.random() * 9 - (first+second);

If you need 3 numbers guaranteed, the sum of the first two should be 8 or less, unless you accept 0 as the 3rd possible number.

Shazam
  • 301
  • 1
  • 2
  • 16
  • 1
    I don't really get the calculation for `third`. Shouldn't it just be `9 - first - second`? – Mark Peters Apr 10 '15 at 02:56
  • Yes, it should, i was tired when I wrote it and copy/pasted the 3 lines. I edited it to reflect the better 3rd calculation. – Shazam Apr 10 '15 at 20:17
0

Mark Peters outlines two methods in his answer. The first method is sort of a brute force method: generate groups of three random numbers until you find one whose sum is 9. The second method is to tabulate all 28 possible permutations, and then randomly select one.

The first method might be slower, and the second will consume more memory. As long as you only need to execute this once, and the required sum of the three numbers is small (like 9) it doesn't make much difference which one you use.

However, if the required sum is large, you might need a different approach. Suppose, the required sum was 1 million. The brute force approach could be quite slow. Tabulating all of the permutations might consume too much memory (or disk space, depending on the implementation).

The total number of permutations is the sum of integers 1 through the maximum possible value. That sum can be quickly calculated by using this equation from Mathematics Stack Exchange:

Take the average of the first number and the last number, and multiply by the number of numbers.

So, in this case, the total number of permutations would be almost 1,000,000 x 500,000; about 500,000,000,000 permutations.

So instead of computing all the possible permutations, we can generate a random number n and calculate only the nth permutation given a required sum:

function getPermutation( n, requiredSum) {
  var remainder = n;
  var step = requiredSum - 2;

  var firstNumber = 1;

  while(remainder > step){
    firstNumber++;
    remainder -= step;
    step--;
  }

  var secondNumber = remainder;

  var thirdNumber = requiredSum - firstNumber - secondNumber;

  return [ firstNumber, secondNumber, thirdNumber ];

}

Example here:

$(document).ready( function(){
  $("button").click( function() {
    $("div").text( JSON.stringify( get3Numbers(1000000) ) );
  });
});

function get3Numbers(requiredSum){
  var maximum = requiredSum - 2;

  var permutations = maximum * ( (maximum + 1.0) / 2.0);

  var randomNumber = Math.floor((Math.random() * permutations) + 1);

  var threeNumbers = getPermutation(randomNumber, requiredSum);

  var sumOfNumbers = threeNumbers[0] + threeNumbers[1] + threeNumbers[2];

  return {
    threeNumbers: threeNumbers,
    total:  sumOfNumbers
  };
}

function getPermutation( n, requiredSum) {
  var remainder = n;
  var step = requiredSum - 2;

  var firstNumber = 1;

  while(remainder > step){
    firstNumber++;
    remainder -= step;
    step--;
  }

  var secondNumber = remainder;

  var thirdNumber = requiredSum - firstNumber - secondNumber;

  return [ firstNumber, secondNumber, thirdNumber ];

}
div {
    border: 1px solid silver;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>&nbsp;</div>
<button>Get three numbers!</button>
Community
  • 1
  • 1
Seamus
  • 4,539
  • 2
  • 32
  • 42
-1

Here's one way to do it.

function statGen() {
    var x = document.getElementById("number");
    var firstNumber = Math.floor((Math.random() * 5) + 1);
    x.innerHTML = firstNumber;
    var a = document.getElementById("agl");
    if (firstNumber == 5) {
        var secondNumber = Math.floor((Math.random() * 4) + 1);
    } else {
        var secondNumber = Math.floor((Math.random() * 5) + 1);
    }
    a.innerHTML = secondNumber;
    var l = document.getElementById("lck");
    l.innerHTML = 9-(firstNumber + secondNumber);
 }
 <button id="statbutton" onclick="statGen()">Get Numbers</button>       
        <p id="main">Main:</p>
        <p id="number"></p>
        <br/>
        <p id="Agl">Agl:</p>
        <p id="agl"></p>
        <br/>
        <p id="Lck">Lck:</p>
        <p id="lck"></p>
Katherine
  • 639
  • 5
  • 16
  • Why limit the second number to 4? – Mark Peters Apr 10 '15 at 02:51
  • Both numbers need to be limited to 4 so that the third number can be `>0`. If not, then there is a possibility of `5 and 4` , which would force the third number to be `0`, or even worse, `5 and 5`, which would go over the limit. – Katherine Apr 10 '15 at 02:51
  • Huh? 5 + 3 + 1 = 9. 2 + 5 + 2 = 9. 5 > 4. – Mark Peters Apr 10 '15 at 02:52
  • Then you should at worst be limiting the second number based on the result of the first. By never letting the number go over 4 just because it *might* not be acceptable throws out a huge amount of the possibilities, including one explicitly identified as an example in the question. – Mark Peters Apr 10 '15 at 02:54
  • Good point. If you need each number to be from 1-5, you would have to dynamically change the requirement each time based on the previous number. E.g. if `firstNumber` was 5, then `secondNumber` has to be 4 or less, etc. – Katherine Apr 10 '15 at 02:55