-8

I have a function that generates random numbers given a range. I want to make sure I don't re-generate the same pair of numbers.

function generateRandomInt(max) {
      return Math.floor(Math.random() * Math.floor(max));
}

let randomInt1 = generateRandomInt(10) + 1;
let randomInt2 = generateRandomInt(10) + 1;

let numberStore = randomInt1 + "" + randomInt2; 

console.log(randomInt1);
console.log(randomInt2);
console.log(parseInt(numberStore));

numberStore contains the result of randomInt1 and randomInt2. I want to avoid having a pair of numbers that were already generated.

https://codepen.io/anon/pen/wRJrrW

Bharata
  • 13,509
  • 6
  • 36
  • 50
zadubz
  • 1,281
  • 2
  • 21
  • 36
  • So when you generate the second one, see if it is equal to the first, if it is, keep generating.... Store a list of generated pairs and see if it exists. – epascarello Dec 21 '18 at 14:56
  • Is 2, 3 same as 3, 2 – epascarello Dec 21 '18 at 15:00
  • 2, 3 is not the same 3, 2 they are unique pairs – zadubz Dec 21 '18 at 15:02
  • 5
    I believe this is an XY question ( https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem ). It'd be helpful to know what you are actually trying to do rather than attempting to answer the question of how to implement your solution. – imjosh Dec 26 '18 at 19:02
  • If you're going to keep this question open with a bounty, _please_ edit it to clarify exactly what you want. Are you generating a single pair of numbers, and want to make sure they're not both the same? Or are you generating many pairs, and don't want the same pair to be generated twice? And if so, do you know in advance how many pairs you will be generating, and will that number be more than 50? – Ilmari Karonen Dec 28 '18 at 10:20
  • How random do the numbers need to be? You could do something like store integers 1 to 500 in a list, then just shuffle/swap the elements of the list, by generating random from/to elements to swap - over say 500 iterations.. Then just pop the first n off the front before reshuffle, next selection of integers. – JGFMK Dec 28 '18 at 15:15
  • I tried editing the question to add a bit more details but the edit was removed :( – zadubz Dec 29 '18 at 13:57
  • OK, so just to confirm, what you want is to generate all distinct pairs of integers between 1 and 10 in a random order, right? – Ilmari Karonen Dec 29 '18 at 20:33
  • Assuming there are all possibilities of combinations already used by your given `max` range (easily reached with low numbers) - is it valid for your task to increase the `max` value? – Jankapunkt Dec 30 '18 at 20:09
  • Poor question, no clarification what is asked, poor reaction on comments, bounty not selected, thus half of the 300 reputation lost, no accepted answer. This way people lose motivation to anwer questions. That's really bad at all. – Pinke Helga Jan 03 '19 at 06:36
  • You can see that I have never downvoted your question – I have deleted my upvote from your question now. – Bharata Jan 08 '19 at 21:37

13 Answers13

3

You could use the Set object. When using the method add(), values cannot be duplicated.

Here is an example:

function random(x, max = 10) {  // x = how many random numbers and max = the max number
    const set = new Set();
    for (let i = 0; i <= x; i++) { // Generate numbers x times
      secondLoop:
      while(true) { // Run the loop until there is a match
        let random = Math.floor(Math.random() * Math.floor(max));        
        set.add(random); // Set the value          
        if (set.size === max) { // We have all the values. Let's break the loop       
          break secondLoop;          
        }
      }
    }
  return set;
}

console.log(random(10));

console.log(random(10)) returns everything that you need. You could use random(10).values(), random(10).delete() or whatever you like.

Reza Saadati
  • 5,018
  • 4
  • 27
  • 64
  • 1
    Could you elaborate or provide a working example how this would work with a pair of values? Why did you decide to use a infinite while loop with a break in an if instead of putting the continue condition in the `while()`? – Rob Monhemius Dec 31 '18 at 15:34
  • Or maybe just single `for-loop` until size of set is reached ^^ just to little bit mangle with your thought and hopefully get some good practices in – Jimi Pajala Jan 01 '19 at 06:47
2

All you need to do is keep track of what was used. Simplest thing to do is you an object to see. So combine the first number to the second number with a separator.

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function pairGenerator(min, max) {

  var usedPairs = {} // holds the keys for what we used alreay
 
  return function() {

    const generate = function() {
      const a = getRandomInt(min, max)
      const b = getRandomInt(min, max)
      const key = a + "," + b // generate the key
      if (usedPairs[key]) { // see if it is used
        return generate() // if it is, try again
      } else {
        usedPairs[key] = 1 // mark it was not used
        return [a, b] // return the two numbers
      }
    }

    return generate()
  }
}

const myGenerator = pairGenerator(0, 10);
for (let i = 0; i < 10; i++) {
  console.log(myGenerator())
}
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • this creates a set of 10 pairs, can it go further and do all possible pairs within that range? – zadubz Dec 21 '18 at 15:43
  • yeah, keep looping.... but the code is not checking to see if all possibilities are taken. – epascarello Dec 21 '18 at 16:12
  • But if you think you will use all possible combinations, than there are other ways to do it. – epascarello Dec 21 '18 at 16:14
  • Yep, need to go through all possible combinations – zadubz Dec 21 '18 at 16:15
  • 1
    Might be better to generate all possibilities up front and shuffle sort. That is sort of out of scope of your original question. And I do not have time to do it. – epascarello Dec 21 '18 at 16:18
  • Dupe: https://stackoverflow.com/q/9960908/215552 (for after you get the bounty for answering this duplicate). – Heretic Monkey Dec 26 '18 at 01:43
  • @HereticMonkey: I don't see how that's a duplicate. There's no randomization involved in the question you linked to. (That said, this question is so unclear that it's hard to tell what the OP wants. But I'm pretty sure it somehow involves randomness.) – Ilmari Karonen Dec 28 '18 at 10:16
2

Citate from bounty description:
The current answers do not contain enough detail.

I think you do not understand how it works in current answers. Because of this I would like to show you two more simple solutions.

And at first I would like to write about the random function. You can use your function in my solutions, but your function will never get the max number. I recommend to use the correct random function from MDN. In this case we will get random numbers which follows a uniform distribution.

Solution with

We take an associative array in Javascript literal notation var numberStore = {}; and add to it the values with keys: numberStore[key] = value;. We could also do it on this way: numberStore.key = value;. And if you want read the numbers then we can do it like value = numberStore[key]; or value = numberStore.key;.

More information about it you will find here: Working with Objects.

//get random numbers which follows a uniform distribution
function getRandom(min, max)
{
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

var numberStore = {}, //associative array or JS-object
    length = 10;

for(var i = 0; i < length; i++)
{
    // do not forget to think about the range of numbers.
    // Because if you want generate 1000 pairs then it should
    // be a range with more than 33 numbers and not with 10.
    // In other case you will get a loop, because 33 x 33 = 999
    var val1 = getRandom(0, length),
        val2 = getRandom(0, length),
        key = val1 + '_' + val2; // generate the key

    if(numberStore[key] != undefined) //check if we have it already
    {
        i--;
        continue; //go to next loop step with i = i - 1
        //The continue statement terminates execution of the statements in the current iteration of the current or labeled loop, and continues execution of the loop with the next iteration.
        //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
    }
    //if we do not have it then we store it as new array:
    else
        numberStore[key] = [val1, val2]; //new array with our values
}

//we use JSON.stringify to beautify (prettify) the code output.
console.log(JSON.stringify(numberStore, null, 4));

console.log('---------------------');

//if you want to access the stored numbers, then you can ďo it like follows:
for(var key in numberStore)
{
    var val = numberStore[key];
    console.log(''+ val); //convert array to string
}

Solution with classic

We can store it in one classic array too like follows:

function getRandom(min, max)
{
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

var numberStore = [],
    length = 10;

for(var i = 0; i < length; i++)
{
    var val1 = getRandom(0, length),
        val2 = getRandom(0, length),
        key = val1 + '_' + val2; // generate the key

    if(numberStore.indexOf(key) > -1) //check if we have it already
    {
        i--;
        continue
    }
    else
        numberStore.push(key)
}

console.log(JSON.stringify(numberStore, null, 4));

console.log('---------------------');

for(var i = 0; i < numberStore.length; i++)
{
    var arrVals = numberStore[i].split('_');
    console.log(arrVals[0] + ', ' + arrVals[1]);
}
Bharata
  • 13,509
  • 6
  • 36
  • 50
2

I tried to solve the problem with a more OOP approach. Probably not the most standard approach.

  • A value is represented by a number and retrieved.
  • When a value is retrieved a replacement value will be assigned in the processedIndexes object. The index will equal the removed value.
  • The value of the newly added object property will equal the value or (referencing value) of the last index.
  • The last index will be removed; totalIndexes will be decreased by one.
  • With the decode_value method the number is 'decoded' by the desirable output.

Read the code for more info.

class RandomNumberGenerator {
  integerRange1 = null;
  integerRange2 = null;
  totalIndexes = null;
  processedIndexes = {};

  constructor(integerRange1, integerRange2) {
    this.integerRange1 = integerRange1;
    this.integerRange2 = integerRange2;
    this.totalIndexes = integerRange2 * integerRange1;
  }

  get_randomValue() {
    // if all possible values are occupied throw an error and return null
    if (this.totalIndexes === 0) {
      console.error('ERROR: all indexes have already been generated');
      return null;
    }
    // calculate availableIndexes
    let availableIndexes = this.totalIndexes;
    // pick a random value
    let newIndex = Math.random() * availableIndexes;
    // round down because of 0 is the first item
    newIndex = Math.floor(newIndex);
    // 
    let newValue = this.retreive_value(newIndex);
    // decode the value to useful output
    newValue = this.decode_value(newValue);
    return newValue;
  }

  retreive_value(newIndex) {
    let value = null;
    // check if the value has already been assigned previously, if so return the new referencing value
    value = (typeof this.processedIndexes[newIndex] === 'number') ? this.processedIndexes[newIndex] : newIndex;
    // the length of the array is reduced by one
    this.totalIndexes--;
    if (typeof this.processedIndexes[this.totalIndexes] === 'number') {
      // replace the retreived value with the highest possible index we are about to remove
      this.processedIndexes[newIndex] = this.processedIndexes[this.totalIndexes];
      // remove the last index from the object, since it it no longer relevant
      delete this.processedIndexes[this.totalIndexes];
    } else {
      this.processedIndexes[newIndex] = this.totalIndexes;
    }
    // return value
    return value;
  }

  decode_value(value) {
    // this is some logic that translates the number to desireable output
    let integer1 = null;
    let integer2 = null;

    // count the amount of times 
    integer2 = Math.floor(value / this.integerRange1) + 1;
    // remaining values
    integer1 = value % this.integerRange1 + 1;

    return [integer1, integer2]
  }
}

let rng = new RandomNumberGenerator(10, 10);
for (let i = 0; i < 10; i++) {
  let values = rng.get_randomValue();
  console.log(values[0], values[1]);
}

PS: Happy new year

Rob Monhemius
  • 4,822
  • 2
  • 17
  • 49
2

This is actually a pretty cool problem.

Ok, let's start from the beginning, let's see if I understood the problem correctly. What you want is a "function" that returns unique pairs of random numbers for a given range.

So, what happens when we run out of unique pairs? What do we do then?

I'm asking that because I think that what you actually want here is a generator function.

Something that could be used like this:

const pairsGen = randomPairsMaker(10);
console.log(pairsGen.next().value) // [9, 5] 
console.log(pairsGen.next().value) // [3, 9]
console.log(pairsGen.next().value) // [9, 3]
console.log(pairsGen.next().value) // [4, 4]
// ...
console.log(pairsGen.next().done) // Eventually this will become true

Ok, so let's write that generator:

function* randomPairsMaker(range) {
  // Let's build a circularly linked data structure where we will
  // keep all the available permutations. So, that every time that 
  // we remove one we can do it in a somewhat performant manner:
  let current = {};
  const fakeInit = current;
  for (let a = 0; a < range; a++) {
    for (let b = 0; b < range; b++) {
      current.next = {pair: [a, b]};
      current = current.next;
    }  
  }
  current.next = fakeInit.next;

  // And now let's just yield the results
  for (let nAvailable = range * range; nAvailable > 0; nAvailable--) {
    const advance = Math.floor(Math.random() * nAvailable);
    for (let i = 0; i < advance; i++) current = current.next;
    yield current.next.pair;
    current.next = current.next.next;
  }
}
Josep
  • 12,926
  • 2
  • 42
  • 45
1

I don't exactly get what you really want, but I will try to answer:

1) Let assume you want to store unique pairs

function generateRandomInt(max) {
          return Math.floor(Math.random() * Math.floor(max));
    }

    const store = new Set()

    let randomInt1 = generateRandomInt(10) + 1;
    let randomInt2 = generateRandomInt(10) + 1;
    let numberStore = randomInt1 + "," + randomInt2; 

store.add(numberStore)
store.add(numberStore) // duplicate won't be added

https://jsfiddle.net/zhmtd9sg/1/

2) Let assume you want to generate random pairs from unique set

let store = []

const arr = Array.from(Array(100), (_,i) => i+1) // numbers from 1 to 100

arr.forEach(firstNum => {
    arr.forEach(secondNum => {
    arr.push([firstNum, secondNum])
  })
})

let randomStoreIndex = Math.floor(Math.random()*store.length)
let randomUniquePair = store[randomStoreIndex]
store.splice(randomStoreIndex, 1)

console.log(randomUniquePair)
console.log(store)

https://jsfiddle.net/o7skenzb/

ulou
  • 5,542
  • 5
  • 37
  • 47
1

there are a few problems with your description and "fixing" each one leads to a different solution.

I want to make sure I don't re-generate the same pair of numbers

here, you wish to constrain the behaviour of a process but the process itself doesn't exist. your code is not "generating a pair of random numbers". instead, it is "generating 2 random numbers and then pairing them blindly". let's fix this bit first:

function generateRandomInt(max) {
  return Math.floor(Math.random() * Math.floor(max));
}

function generateRandomIntPair(max) {
  let randomInt1 = generateRandomInt(max) + 1;
  let randomInt2 = generateRandomInt(max) + 1;
  let pair = [randomInt1, randomInt2];
  return pair;
}

let randomIntPairgenerateRandomIntPair(10);

I want to avoid having a pair of numbers that were already generated.

What should the expected behaviour be when a duplicate pair is generated? fail silently? throw error? generate a new unique random pair?

errors and silent fails are bad but trivial

on the other hand, to generate a new unique random pair you need to know 2 things:

  1. for a given max, how many unique random pairs are possible
  2. how many pairs have already been generated

when all possible pairs have been made, a naive approach may go into an infinite loop

fixing the problems:

  1. total pairs = max * max (since you only care about permutations instead of combinations and repeating values is allowed
  2. total generated pairs can be managed by a simple count variable

The problem with maintaining such state in a global variable is the memory consumption

function generateRandomInt(max) {
    return Math.floor(Math.random() * Math.floor(max))
}

const totalPairsPossible = {}
const uniquePairs = {}

function generateRandomIntPair(max) {
    const N = max * max
    totalPairsPossible[max] = N

    let randomInt1 = generateRandomInt(max) + 1
    let randomInt2 = generateRandomInt(max) + 1

    let newPair = [randomInt1, randomInt2]

    uniquePairs[max] = uniquePairs[max] || []

    if (uniquePairs[max].length === totalPairsPossible[max]) {
        throw new Error('All unique pairs generated')
    }

    let isNewPairUnique = uniquePairs[max].every(
        pair => (pair[0] !== newPair[0] || pair[1] !== newPair[1])
    )

    if (isNewPairUnique) {
        uniquePairs[max].push(newPair)
        return newPair
    } else {
        return generateRandomIntPair(max)
    }
}
zhirzh
  • 3,273
  • 3
  • 25
  • 30
1

With ES6 generators you are able to generate all possible pairs in an iterator-fashioned way.

Since you are generating integers, you can use a double-array matrix in order to store the already generated pairs as indexes.

The generator will create new unused pairs until all possible combinations have been generate. it will then return { value: undefined, done: true }).

Code example:

function* pairGen (maxRange) {

  // create an array with the inclusive range as size
  const store = new Array(maxRange + 1)

  // inclusive int gen with min=0
  function int (max) {
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - 0 + 1)) + 0;
  }

  // update the store with a given pair
  function update(x, y) {
    store[x] = store[x] || new Array(maxRange + 1)
    store[x][y] = true
  }

  // check if there is any pair available (=falsey)
  function available() {
    for (let entry of store) {
      if (typeof entry === 'undefined') return true
      for (let value of entry) {
        if (!value) return true
      }
    }
    return false
  }

  let int1, int2
  while (available()) {
    int1 = int(maxRange)
    int2 = int(maxRange)

    // only yield if the values are still available
    if (!store[int1] || !store[int1][int2]) {
      update(int1, int2)
      yield [int1, int2]
    }
  }
}

With this you can generate all pairs until no pair is available anymore.

Usage:

let gen = pairGen(2);
let value = gen.next().value
while (value) {
  console.log(value)
  value = gen.next().value
}

Possible output:

[0, 2]
[1, 2]
[0, 0]
[2, 1]
[1, 1]
[0, 1]
[2, 2]
[2, 0]
[1, 0]

Pros

  • All required variables are in block scope of the generator.
  • No infinite loops.
  • Generates all possible pairs.

Cons

  • Availability check involves a double for-loop and can become very imperformant with larger ranges and increasing amount of generated pairs.
  • while loop yields only a new pair if it has not been generated, making the loop sometimes run a lot again. This is because there is no strategy to generate the numbers but a random approach that could lead to lots of already generated pairs with growing store.
Jankapunkt
  • 8,128
  • 4
  • 30
  • 59
1

Modern JavaScript provides the classes Set and Map. A Set manages unique key entries. A Map is an extended Set which associates an additional object per unique key. Other than JavaScript's standard objects the Set and Maps classes do have a dynamic size property containing the number of entries.

Even if objects and arrays are supported as keys, neither the Set nor the Map does provide a deep inspection for comparision. Identical references are considered as equality criteria instead. To get a distinct content criteria, you need to convert the content into a unique value, e.g. a joined string with an unambiguous delimiter or in the most generic way the JSON representation.

Assuming you really want pairs to be unique, not just the concatenated strings, and you might want to handle the two random numbers seperately, the Map class is what you want since we can store the raw array without having to rebuild it from JSON later.

// It's always a good habit to encapsulate the script scope and enable strict mode
(()=>{
  'use strict';

  function generateRandomInt(max, min)
  {
    return Math.floor(Math.random() * Math.floor(max+min+1) + min);
  }

  function generateRandomUniquePairs(count, max, min)
  {
    // ensure proper integer arguments
    count = undefined === count ? 0 : Math.floor(count);
    max   = undefined === max   ? 0 : Math.floor(max  );
    min   = undefined === min   ? 0 : Math.ceil (min  );

    // throw error on inappropriate input arguments
    if(isNaN(count))
      throw new Error('Error: `count` must be convertible to integer.');

    if(isNaN(max))
      throw new Error('Error: `max` must be convertible to integer.');

    if(isNaN(min))
      throw new Error('Error: `min` must be convertible to integer.');

    if(Math.pow(1+max-min, 2) < count)
      throw new Error( 'Error: There is no unique set of ' + count +
                       ' pairs within a range of ' + (1+max-min) + ' values ('+min+'..'+max+'). ' +
                       '`count` must be equal or less than the range squared.');


    // basic algorithm
    // Map holds unique keys associated to any object and provides a dynamic size property
    let pairs = new Map();

    // generate `count` distinct entries
    while(pairs.size < count)
    {
      // Here you could additionally `sort()` the pair for the key only
      // or both the key and value if you consider [1,2] and [2,1] to be equal

      let pair = [generateRandomInt(max, min), generateRandomInt(max, min)];

      // use JSON string representation as unambiguous key
      pairs.set(JSON.stringify(pair), pair);
    }

    // convert the result to a simple array
    return Array.from(pairs.values());
  }


  // ***** TEST CASE *****
  let pairs = generateRandomUniquePairs(50, 9);

  // output as array of arrays
  console.log(pairs);

  // convert items to concatenated strings
  pairs.forEach( (v,i) => pairs[i] = v.join('') );

  // ordered output as array of strings as given in the example of your question
  console.log(pairs.sort());


})();
Pinke Helga
  • 6,378
  • 2
  • 22
  • 42
1

Using a combination of function generators and Set.

Working Example:

function randomNumber(max) {
  return Math.floor(Math.random() * Math.floor(max));
}

function* uniquePair(max) {

  const pairs    = new Set();
  const maxPairs = max * max;

  while (true) {
    const res = [randomNumber(max), randomNumber(max)];
    const resStr = res.join(",");
    switch (true) {
      case !pairs.has(resStr):
        pairs.add(resStr);
        yield res;
        break;
      case pairs.size === maxPairs:
        //done
        return;
      default:
        console.log(resStr + " Exists...");
    }
  }
}


const gen10 = uniquePair(10);

const si = setInterval(function() {
  const next = gen10.next();
  if (next.done === true) {
    clearInterval(si);
    console.log("Done");
  } else {
    console.log(next.value);
  }
}, 500);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How does this work?

const gen = uniquePair(10);

This will create a new generator for the given max range.


function* uniquePair(max) {

  const pairs    = new Set();
  const maxPairs = max * max;

  /** more code **/

}

The first two lines are only created once. The pairs to memorise the unique combinations already created (empty at the start) and maxPairs to know the max possible unique combinations.


function* uniquePair(max) {

  /** more code **/

  while (true) {
    const res = [randomNumber(max), randomNumber(max)];
    const resStr = res.join(",");

    /** more code **/
  }
}

Here we create an infinite loop. Each loop we create a random combination of two values. We also create the string representation of those two values (ex: [1,0] -> "1,0").


function* uniquePair(max) {

  /** more code **/

  while (true) {
    /** more code **/
    switch (true) {
      case !pairs.has(resStr):
        pairs.add(resStr);
        yield res;
        break;
      /** more code **/
    }
  }
}

For each iteration of the while loop, we check to see if the string representation of our two values exists within our set. If it doesn't, we add that string representation to the set and yield the array.

yield is where we temporarily "leave" our generator and send back the result, which is then accessible in:

const next = gen.next();
//next.value -> [1,0] , next.done -> false

function* uniquePair(max) {

  /** more code **/

  while (true) {
    /** more code **/
    switch (true) {
      /** more code **/
      case pairs.size === maxPairs:
        //done
        return;
      /** more code **/
    }
  }
}

If the string representation of the values already exists within our set, we check to see if the set's size is equal to the max number of pairs. If it is, we can assume there are no more results possible and we simply return

At this point the generator is done and will no longer return any values:

const next = gen.next();
//next.value -> undefined , next.done -> true

function* uniquePair(max) {

  /** more code **/

  while (true) {
    /** more code **/
    switch (true) {
      /** more code **/
      default:
        console.log(resStr + " Exists...");
    }
  }
}

If none of the previous cases match then we can assume that 1. The current combination already exists and 2. There are still combinations left. We then do the next loop iteration and come up with a new combination and start again till a new unique combination is found.

Multiple instances runnable:

This code also allows the ability to run multiple instances of a generator each having different max numbers.

const gen2 = uniquePair(2);
const gen4 = uniquePair(4);
const gen6 = uniquePair(6);

let cycle = 1;
const si = setInterval(function(){

  console.log(`Cycle: ${cycle}`);

  const next2 = gen2.next();
  const next4 = gen4.next();
  const next6 = gen6.next();
  
  if(next2.done === false){
    console.log(`2: [${next2.value.join(",")}]`);
  } else {
    console.log("2: DONE");
  }
  
  if(next4.done === false){
    console.log(`4: [${next4.value.join(",")}]`);
  } else {
    console.log("4: DONE");
  }
  
  if(next6.done === false){
    console.log(`6: [${next6.value.join(",")}]`);
  } else {
    console.log("6: DONE");
  }
  
  console.log("-------");
  cycle++;
  if(cycle === 40) clearInterval(si);

}, 1000);



function randomNumber(max){return Math.floor(Math.random()*Math.floor(max))}
function*uniquePair(max){const pairs=new Set();const maxPairs=max*max;while(!0){const res=[randomNumber(max),randomNumber(max)];const resStr=res.join(",");switch(!0){case!pairs.has(resStr):pairs.add(resStr);yield res;break;case pairs.size===maxPairs:return;}}}
.as-console-wrapper { max-height: 100% !important; top: 0; }

Min Max Solution:

function randomNumber(max,min) {
  return Math.floor(Math.random() * (max - min) + min);
}

function* uniquePair(max, min = 0) {

  const pairs = new Set();
  const maxPairs = Math.pow(max-min, 2);
  console.log(maxPairs);

  while (true) {
    const res = [randomNumber(max, min), randomNumber(max, min)];
    const resStr = res.join(",");
    switch (true) {
      case !pairs.has(resStr):
        pairs.add(resStr);
        yield res;
        break;
      case pairs.size === maxPairs:
        //done
        return;
      default:
        console.log(resStr + " Exists...");
    }
  }
}


const gen10 = uniquePair(10,5);

const si = setInterval(function() {
  const next = gen10.next();
  if (next.done === true) {
    clearInterval(si);
    console.log("Done");
  } else {
    console.log(next.value);
  }
}, 500);

Solution where all possibilities are pre-calculated:

The main issue with the main example I have is that, the more combinations requested the harder it'll be for the algo to find a unique pair. This is of course negligible if:

  1. There are less than a 100 pairs
  2. Only 60-70% of the possible matches are ever requested

If both the above points are false then this solution would be optimal:

function randomNumber(max) {
  return Math.floor(Math.random() * Math.floor(max));
}

function generateAllPossibilities(max){
  const res = [];
  for(let i = 0; i < max; i++){
    for(let j = 0; j < max; j++){
      res.push([i,j]);
    }
  }
  return res;
}

function* uniquePair(max) {

  const pairs    = generateAllPossibilities(max);
  
  while (true) {
    const len = pairs.length;
    if(len === 0) return;
    yield pairs.splice(randomNumber(len), 1).shift();
  }
}


const gen10 = uniquePair(10);

const si = setInterval(function() {
  const next = gen10.next();
  if (next.done === true) {
    clearInterval(si);
    console.log("Done");
  } else {
    console.log(next.value);
  }
}, 200);
.as-console-wrapper { max-height: 100% !important; top: 0; }
kemicofa ghost
  • 16,349
  • 8
  • 82
  • 131
0

make numberStore an object having key as (input1+input2) and value randomInt1 + "" + randomInt2; and before adding new key to the numberStore object check if the key is already present in object or not

function generateRandomInt(max) {
      return Math.floor(Math.random() * Math.floor(max));
    }

let randomInt1 = generateRandomInt(10) + 1;
let randomInt2 = generateRandomInt(10) + 1;
let numberStore={};
numberStore.values=[];
var key={[randomInt1]:randomInt1,[randomInt2]:randomInt2};

  if(!numberStore[key[randomInt1]] && !numberStore[key[randomInt2]]){
     numberStore.values.push(randomInt1 + "" + randomInt2); 
     }

console.log(randomInt1);
console.log(randomInt2);
console.log(numberStore);
0

It sounds like you're asking, if you generate these number pairs over and over, how can you avoid collisions (meaning, returning the same number pair more than once).

There are traditionally two ways to do this:

1) Keep track of every number you've ever generated, and make sure the number you generated isn't already in the list. If it is, generate another one. Loop until you've produced a number you haven't seen before.

2) Make the number space of the random number really, really big, so that a collision is very very unlikely.

Approach #1 includes things like keeping a list of random numbers in memory, all the way up to, e.g., generating a number and inserting it into a MySQL table with a unique index (if you get a collision, you can re-generate the number and try again, etc.).

Approach #2 usually involves selecting a better (cryptographic) random number generator with a large output space, or using a GUID generator, etc.

Elliot Nelson
  • 11,371
  • 3
  • 30
  • 44
0

You already have an answer for generating random pairs. This could be done by saving the generated pairs in a Set.

Problem with this approach is that the algorithm might keep generating a pair that is already created. Hence, there cannot be a known amortized analysis for this approach.

The solution to generate guaranteed new pairs with good performance

Produce all pairs by looping and store them in an array pairArray. Now, generate a random number i<= size of array. Your new pair will be equal to pairArray[i].

To prevent this element from re-appearing in the result you should exclude this from the pairArray. Code: pairArray.splice(i,1)

I am sure you can easily implement this since you look well versed with the language. Also, splicing takes a performance hit. You are welcome to chose another data structure that fits to your need.

Community
  • 1
  • 1
YetAnotherBot
  • 1,937
  • 2
  • 25
  • 32