Random number, which is not equal to the previous number

I need to get random number, but it should not be equal to the previous number. Here is my piece of the code. But it doesn't work.

function getNumber(){
  var min = 0;
  var max = 4;
  var i;
  i = Math.floor(Math.random() * (max - min)) + min;
  if (i=== i) {
    i = Math.floor(Math.random() * (max - min)) + min;
  }
  return i;
};

console.log(getNumber());

Solution 1:

This answer presents three attempts

  1. A simple version with a property of the function getNumber, last, which stores the last random value.

  2. A version which uses a closure over the min and max values with raising an exception if max is smaller than min.

  3. A version which combines the closure and the idea of keeping all random values and use it as it seems appropriate.


One

You could use a property of getNumber to store the last number and use a do ... while loop.

function getNumber() {
    var min = 0,
        max = 4,
        random;

    do {
        random = Math.floor(Math.random() * (max - min)) + min;
    } while (random === getNumber.last);
    getNumber.last = random;
    return random;
};

var i;
for (i = 0; i < 100; i++) {
    console.log(getNumber());
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

Two

Another proposal with a closure over the interval and the last random value.

function setRandomInterval(min, max) {
    var last;
    if (min >= max) {
        throw 'Selected interval [' + min + ', ' + max + ') does not work for random numbers.';
    }
    return function () {
        var random;
        do {
            random = Math.floor(Math.random() * (max - min)) + min;
        } while (random === last);
        last = random;
        return random;
    };
}

var i,
    getRandom = setRandomInterval(0, 4);

for (i = 0; i < 100; i++) {
    console.log(getRandom());
}

setRandomInterval(4, 4); // throw error
.as-console-wrapper { max-height: 100% !important; top: 0; }

Three

This proposal uses the idea to minimise the call of a new random number. It works with two variables, value for the continuing same random value and count for saving the count of the same value.

The function looks first if the saved count is given and if the value is not equal with the last value. If that happens, the saved value is returned and count is decremented.

Otherwise a new random numner is generated and checked as above (first proposal). If the number is equal to the last value, the count is incremented and it goes on with generating a new random value.

As result, almost all previous generated random values are used.

function setRandomInterval(min, max) {
    var last,      // keeping the last random value
        value,     // value which is repeated selected
        count = 0, // count of repeated value
        getR = function () { return Math.floor(Math.random() * (max - min)) + min; };

    if (min >= max) {
        throw 'Selected interval [' + min + ', ' + max + ') does not work for random numbers.';
    }
    return function () {
        var random;
        if (count && value !== last) {
            --count;
            return last = value;
        }
        random = getR();
        while (random === last) {
            value = random;
            ++count;
            random = getR();
        }
        return last = random;
    };
}

var i,
    getRandom = setRandomInterval(0, 4);

for (i = 0; i < 100; i++) {
    console.log(getRandom());
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

Solution 2:

The following method generates a new random number in the [min, max] range and makes sure that this number differs from the previous one, without looping and without recursive calls (Math.random() is called only once):

  • If a previous number exists, decrease max by one
  • Generate a new random number in the range
  • If the new number is equal to or greater than the previous one, add one
    (An alternative: If the new number is equal to the previous one, set it to max + 1)

In order to keep the previous number in a closure, getNumber can be created in an IIFE:

// getNumber generates a different random number in the inclusive range [0, 4]
var getNumber = (function() {
  var previous = NaN;
  return function() {
    var min = 0;
    var max = 4 + (!isNaN(previous) ? -1 : 0);
    var value = Math.floor(Math.random() * (max - min + 1)) + min;
    if (value >= previous) {
      value += 1;
    }
    previous = value;
    return value;
  };
})();

// Test: generate 100 numbers
for (var i = 0; i < 100; i++) {
  console.log(getNumber());
}
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

The [min, max] range is made inclusive by adding 1 to max - min in the following statement:

var value = Math.floor(Math.random() * (max - min + 1)) + min;

This is not a requirement in the question but it feels more natural to me to use an inclusive range.

Solution 3:

First of all function should compare with previous value, now We have only i variable which is compared to itself. To be sure that we not have previous value we need to do loop inside ( recursive in my solution ), because single if statement not give us sure that second random will be not the same ( exists chance on that ). Your number set is very small so chance for collision is high and it is possible that loop needs few executions.

function getNumber(prev){
  var min = 0;
  var max = 4;
  var next;
  
  next = Math.floor(Math.random() * (max - min)) + min;
  
  if (next===prev) {
    console.log("--run recursion. Our next is ="+next); //log only for test case
    next = getNumber(prev); //recursive
  }
  
  return next;
};

//test 100 times
var num=0;
for ( var i=0; i<100; i++){
  num=getNumber(num);
  console.log(num);
}

As You can see in tests we never have two the same values next to each other. I also added some console.log to show how many times recursion needs to run to find next number which is different then previous one.

Solution 4:

A general solution

Keep track of the last generated number. When generating a new number, check that it differs from the last one. If not, keep generating new numbers until it is different, then output it.

Working demo

var getNumber = (function(){
  var min = 0;
  var max = 4;
  var last = -1;
  return function(){
    var current;
    do{
      // draw a random number from the range [min, max]
      current = Math.floor(Math.random() * (max + 1 - min)) + min;
    } while(current === last)
    return (last = current);
  }
})();

// generate a sequence of 100 numbers,
// see that they all differ from the last

for(var test = [], i = 0; i < 100; i++){
  test[i] = getNumber();
}
console.log(test);

Comment about computational efficiency

As discussed in comments and other answers, a potential drawback of the approach above is that it may require several attempts at generating a random number if the generated number equals the previous. Note that the probability of needing many attempts is quite low (it follows a rapidly declining geometric distribution). For practical purposes, this is not likely to have any noticeable impact.

However, it is possible to avoid making several attempts at generating a new random number by directly drawing a random number from the set of numbers in the range [min, max] excluding the previously drawn number: This is well demonstrated in the answer by @ConnorsFan, where only one random number is generated at each function call, while randomness is still preserved.

Solution 5:

You'll need a variable with a greater scope than the variables local to your getNumber function. Try:

var j;
function getNumber(){
  var min = 0;
  var max = 4;
  var i = Math.floor(Math.random() * (max - min)) + min;
  if (j === i) {
    i = getNumber();
  }
  j = i;
  return i;
};