Home > Software design >  How to choose randomly unique number when the button is clicked
How to choose randomly unique number when the button is clicked

Time:06-20

I am trying to choose random unique numbers everytime when I click button. For this my function is:

const chooseNumber = () => {
    var r = Math.floor(Math.random() * 75)   1;
    console.log(r)
    while(selectedNumbers.indexOf(r) === -1) {
      selectedNumbers.push(r);
    }
    console.log(selectedNumbers);
  };

But the problem is if the random number is already on my list, I need to click the button again to generate new number and it goes until it find the number which is not on the list. But I want to generate number which is not on the list directly so I dont need to click the button everytime. Thanks for you helps.

CodePudding user response:

You are in a right track, except the while loop should be for random number generator, not pushing number into an array:

const selectedNumbers = [];
const chooseNumber = () => {
    let r;
    do
    {
      r = Math.floor(Math.random() * 75)   1;
    }
    while(selectedNumbers.indexOf(r) > -1)
    selectedNumbers.push(r);
    console.log(r, "[" selectedNumbers "]");
  };
<button onclick="chooseNumber()">Generate</button>

Note, that this might eventually lead to a freeze, since there is no fail safe check if array is full, so to battle that we should also check length of the array:

const selectedNumbers = [];
const maxNumber = 75;
const chooseNumber = () => {
  let r;
  do
  {
    r = ~~(Math.random() * maxNumber)   1;
  }
  while(selectedNumbers.indexOf(r) > -1 && selectedNumbers.length < maxNumber)
  if (selectedNumbers.length < maxNumber)
    selectedNumbers.push(r);
  else
    console.log("array is full");

  console.log(r, "[" selectedNumbers "]");
};


for(let i = 0; i < 76; i  )
{
  chooseNumber();
}
<button onclick="chooseNumber()">Generate</button>

CodePudding user response:

Your while loop is unnecessary. You could use an "if" statement instead.

To avoid clicking again on your button, you can do a recursive function like:

const chooseNumber = () => {
    var r = Math.floor(Math.random() * 75)   1;
    console.log(r)
    if(selectedNumbers.indexOf(r) === -1) {
      selectedNumbers.push(r);
      console.log(selectedNumbers);
    } else {
      chooseNumber();
    }
};

CodePudding user response:

Don't rely on a loop to generate a unique (unseen) integer in a limited range.

First, once all of the values in the range have been exhausted there will be no possibilities left, so you'll be left in an endless loop on the next invocation.

Second, it's wasteful of the processor because you are generating useless values on each invocation.

Instead, generate all of the values in range in advance (once), then shuffle them and get the last one from the array on each invocation (and throw an error when none remain):

/**
 * Durstenfeld shuffle
 *
 * - https://stackoverflow.com/a/12646864/438273
 * - https://en.wikipedia.org/wiki/Fisher–Yates_shuffle#The_modern_algorithm
 */
function shuffleArray (array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i   1));
    [array[i], array[j]] = [array[j], array[i]];
  }
}

/** Get shuffled array of all integers in range */
function generateShuffledIntegerArray (min, max) {
  const arr = [];
  for (let i = min; i <= max; i  = 1) arr.push(i);
  shuffleArray(arr);
  return arr;
}

const getUniqueInt = (() => {
  const numbers = generateShuffledIntegerArray(1, 75);

  return () => {
    const n = numbers.pop();
    if (typeof n === 'number') return n;
    throw new Error('No unique numbers remaining');
  };
})();

// Will log 75 integers, then throw on the 76th invocation:
for (let i = 1; i <= 76; i  = 1) {
  const n = getUniqueInt();
  console.log(`${i}:`, n);
}

Code in TypeScript Playground

  • Related