0

Full code looks like this, ideally we have 4 div boxes that need to be randomly filled with random numbers ansValue, one of them (rightAnsValue with its rightAnsId) is already done and works fine, I've managed to make it unique in comparison to others (code without commented section). But met a problem with making others unique, I keep having some identical values in my boxes. In comments is one way I tried to solve this, but pretty sure there is a much simpler and smarter solution that actually works. I would appreciate if you could help to find an understandable solution to this problem. (P.S. I've seen similar questions but they are either too dificult or done without JS.)

function createAnswers(){
    for(ansId=1; ansId<5; ansId++){
        if(ansId!=rightAnsId){
            for(i=1; i<10; i++){
                digitArray[i-1] = i;
            }
            genNewRandNum();

            // ansArray.length = 3;
            // ansArray.push(ansValue);
            // for(k=0; k<3; k++){
            //     if(ansArray[k] == ansArray[k+1] || ansArray[k] == ansArray[k+2]){
            //         genNewRandNum();
            //         ansArray[k] = ansValue;
            //     }else if(ansArray[k+1] == ansArray[k+2]){
            //         genNewRandNum();
            //         ansArray[k+1] = ansValue;
            //     }else{
            //         break;
            //     }
            // }

            if(ansValue!=rightAnsValue){
                document.getElementById("box" + ansId).innerHTML = ansValue;
            }else{
                genNewRandNum();
                document.getElementById("box" + ansId).innerHTML = ansValue;
            }
        }
    }
}

The way I generate new numbers:

function genNewRandNum(){
    rand1 = digitArray[Math.floor(Math.random() * digitArray.length)];
    rand2 = digitArray[Math.floor(Math.random() * digitArray.length)];
    ansValue = rand1 * rand2;
}
  • So you have four boxes, and you want to output a unique and random number between 1 and 9 in each of the box. Is that what you are looking for? – Karthikeyan Jan 06 '19 at 20:01
  • I guess, one of them being the right answer to a question. UPD: it should be ansValue in a box which is a random number itself done in the genNewRandNum() function – Дмитрий Давлетшин Jan 06 '19 at 20:03
  • It seems like you are showing a product of two random numbers? I think we need more context here? Where are `rand1` and `rand2` used? Are they displayed somewhere? What is the purpose of showing these products? Is it a guessing game? What needs to be guessed? – trincot Jan 06 '19 at 20:27
  • From this code I cannot make out what is the right answer value. But if you know the right answer, then you can update the code in my answer to pass the right answer to it and then return only the values other than right answer.Now you will have an array of 3 random unique values plus 1 right answer. You can randomly shuffle these 4 values into 1 array and assign it to the boxes – Karthikeyan Jan 06 '19 at 20:29
  • The reason why ansValue is counted in the external function is to make code easier to read. I create a random question, smth like: "5x9?", numbers between 1 and 9 are used (the reason I make ansValue out of these numbers). There are 3 boxes with random answers (ansValue), the 4th being the right one (has the rightAnsValue). My problem is about making them all different. Sorry if made anyone confused. – Дмитрий Давлетшин Jan 06 '19 at 20:48

2 Answers2

0

Replace your genNewRandNum() with below code. I have used IIFE to create a closure variable alreadyGeneratedNumbers thats available inside the function generateRandomNumber() thats returned.

So everytime genNewRandNum() is executed, it checks against alreadyGeneratedNumbers to make sure it always returns a unique between 1 and 9.

var genNewRandNum = (function(){
  var alreadyGeneratedNumbers = {};
  return function generateRandomNumber() {
    var min = Math.ceil(1),
      max = Math.floor(9);
      randomNumber = Math.floor(Math.random() * (max - min + 1)) + min;
    
    if(alreadyGeneratedNumbers[randomNumber]) {
      return generateRandomNumber();
    } else {
      alreadyGeneratedNumbers[randomNumber] = randomNumber;
      return randomNumber;
    }

  }
})();



console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());

Note: If you call genNewRandNum() for the 10th time it will throw error. So if you have a use case where you would need to reset it after all numbers from 1 to 9 are returned, then you need to add code to handle that

Karthikeyan
  • 302
  • 1
  • 11
0

The easiest way to brute-force this is to use accept/reject sampling. You can do something like so:

uniqueRandomNumbers = function(n, nextRandom)
{
  var nums = {}; var m = 0;

  while(m < n)
  {
    var r = nextRandom();

    if(! nums.hasOwnProperty(r))
    {
      nums[r] = true; m++;
    }
  }

  return Object.keys(nums);
}

Here I'm using the fact that js objects are implemented as hashmaps to get a hashset. (This has the downside of converting the numbers to strings, but if you're not planning on imediately doing arithmetic with them this is not a problem.)

In order to get four unique integers between 0 and 9 you can then do something like:

uniqueRandomNumbers(4, function() { return Math.floor(Math.random() * 10); })

If you want something a little better than brute force (which probably isn't relevant to your use case but could help someone googling this), one option is to go through each element and either take or leave it with an appropriate probability. This approach is outlined in the answers to this question.

Daniel McLaury
  • 4,047
  • 1
  • 15
  • 37