0

I would like to generate 5 different sets of 2 different numbers (ranging from 0 to 9).

Here are my codes but it seem like execute non-stop. Can anyone please help? Thank you in advance.

let numPos = [];
let numPosTemp = [];
let r, j, k;

// generate 5 sets
while (numPos.length < 5) {

   // generate 2 diff num pos
   while (numPosTemp.length < 2) {
      r = Math.floor(Math.random() * 10);
      if (numPosTemp.indexOf(r) === -1) {
         numPosTemp.push(r);
      }
   }

   // check match any child array
   k = 0;
   for (j = 0; j < numPos.length; j++) {
      if (numPosTemp.every(function (element) {
         return numPos[j].includes(element);
      }) == true) {
      k++;
      }
   }

   // will only add into numPos if totally no match
   if (k == 0) {
      numPos.push(numPosTemp);
   }
}

if there is another way better or more efficient please do comment also.. thank you~!

updated: Sample output: [[0, 1], [2, 3], [6, 9], [4, 2], [7, 8]]

1 Answers1

1

You have an infinite loop because numPosTemp after the first iteration of your while-loop will remain with two elements for the next iteration of your loop. As a result, your inner while loop doesn't run, as numPostTemp is already greater/equal to two. Your if-statement check in your for loop is also true, as you're checking if the array you just pushed into numPos exists in your numPos. This results in k increasing, and so your final if-statement check is false. As nothing changed for this iteration of your loop, you end up in an infinite loop, as numPos can't increase. To fix this, move numPosTemp into your while loop.

Moreover, your .every() method is checking that all elements from your current numPosTemp exists within your numPos array. If one element doesn't exist, the every method will return valse and your numPosTemp array will still be added, meaning [1, 2], [1, 3] can occur. To fix this (so 1 doesn't double-up), change .every() to .some()

let numPos = [];
let r, j, k;

// generate 5 sets
while (numPos.length < 5) {
   let numPosTemp = [];
   // generate 2 diff num pos
   while (numPosTemp.length < 2) {
      r = Math.floor(Math.random() * 10);
      if (numPosTemp.indexOf(r) === -1) {
         numPosTemp.push(r);
      }
   }

   // check match any child array
   k = 0;
   for (j = 0; j < numPos.length; j++) {
      if (numPosTemp.some(function (element) {
         return numPos[j].includes(element);
      }) == true) {
      k++;
      }
   }

   // will only add into numPos if totally no match
   if (k == 0) {
      numPos.push(numPosTemp);
   }
}
console.log(numPos);

A more efficient solution would be to generate an array of 10 numbers from 0 to 9. You can then shuffle this array to randomize it, and then chunk the array into twos:

function shuffle(array) {
  // From: https://stackoverflow.com/a/12646864/5648954
  for (var i = array.length - 1; i > 0; i--) {
    var j = Math.floor(Math.random() * (i + 1));
    var temp = array[i];
    array[i] = array[j];
    array[j] = temp;
  }
  return array;
}

function chunk(arr, n) {
  return arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : [];
}

const groups = 5; // 5 groups
const nums = Array.from({length: groups * 2}, (_, i) => i); // [0, 1, 2, ... 9]

const res = chunk(shuffle(nums), 2);
console.log(res);

If you want numbers to be able to double up, you can maintain a Set of strings. The strings represent string versions of your inner arrays [x, y] -> "x,y". For each number pair you want to create, you can check if the formatted string version of your numbers is in the set. Using a set has an efficient look-up compared to an array. If the formatted string appears in the set, then you can generate your random numebrs until you have two numbers not in your set. Once you find two numbers, you can add these to your set and use them a a pair of numbers in your end result:

const groups = 5; // 5 groups
const seen = new Set();
const format = (...args) => args.join();
const nums = Array.from({length: groups}, () => {
  let x, y;
  do {
    x = Math.floor(Math.random() * groups*2); 
    y = Math.floor(Math.random() * groups*2);
  } while(x !== y && seen.has(format(x,y)));
  seen.add(format(x, y));
  return [x, y];
});

console.log(nums);
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
  • 1
    i seeeeeeeee.. thank you so much for the solution and explanations.. however i do need to check every elements.. but thanks for the explanation again.. i will keep in mind for other usages.. so for the proposed solution.. is that possible to make a light changes so that [1, 2] and [1, 3] can occur?? – xiaobai5883 Jun 07 '21 at 11:27
  • @xiaobai5883 I added a third possible way that allows for double-ups of numbers, but will keep each all 5 sets unique and each set have different numbers – Nick Parsons Jun 07 '21 at 12:05