Acaz Souza
Acaz Souza

Reputation: 8631

Why this while Javascript loop is infinite?

I don't understand why this while loop is infinite:

window.prevRandomNumber = -1;
function getRandomNumber(limit) {
    if (!limit)
        limit = 9;

    var actualRandomNumber = Math.floor((Math.random() * limit) + 1);

    while (window.prevRandomNumber == actualRandomNumber) {
        actualRandomNumber = Math.floor((Math.random() * limit) + 1)
    }

    window.prevRandomNumber = actualRandomNumber;

    return actualRandomNumber;
}

QUnit test for this:

    test("getRandomNumber() should never return the same number once and again", function () {
        //http://www.askageek.com/2006/01/31/javascript-random-function-that-does-not-return-two-consecutive-identical-results/

        var prevNumber, actualNumber, assertResult;

        for (var i = 0; i <= 200; i++) {
            actualNumber = getRandomNumber();
            assertResult = prevNumber != actualNumber;

            equal(assertResult, true);

            if (!assertResult)
                break;

            prevNumber = actualNumber;
        }
    });

Solution:

Sorry, the mistake was in another test, like describe by @Jon answer, when anyNumber equal a 1, infinite loop happens:

    test("getRandomNumber(anyNumber) should return a number between 1..anyNumber", function () {

        var anyNumber, result;

        for (var i = 0; i <= 100; i++) {
            anyNumber = Math.floor((Math.random() * 9) + 1);
            result = getRandomNumber(anyNumber);

            equal((0 < result && result < (anyNumber + 1)), true);
        }
    });

Upvotes: 1

Views: 284

Answers (1)

Jon
Jon

Reputation: 437336

Endless loop #1

The loop will be endless if limit == 1. Consider:

var actualRandomNumber = Math.floor((Math.random() * limit) + 1);

Math.random returns a number in the range [0, 1). Multiplying by limit does not change that, adding one brings it to [1, 2) so by definition Math.floor on that will return 1.

while (window.prevRandomNumber == actualRandomNumber) {
    actualRandomNumber = Math.floor((Math.random() * limit) + 1)
}

Here we have the same logic, so:

  • the first time the method is called it will return 1 (it will not go into a loop because prevRandomNumber is -1)
  • the second and subsequent times the method will never return because prevRandomNumber will be already 1

I can't see how the loop would be infinite if limit is a number greater than 1.

Endless loop #2

Consider what happens if limit is not a number.

var actualRandomNumber = Math.floor((Math.random() * limit) + 1);

Here, Math.random() * limit is going to be NaN, and so will the end result of the expression. So actualRandomNumber will always be equal to NaN. The method will return Nan the first time it's invoked and will not return the second time, for the same reason as above.

Solution

function getRandomNumber(limit) {
    limit = Number(limit) || 9;

    var actualRandomNumber = Math.floor(Math.random() * (limit + 1));

The first line fixes endless loop #2, and the second fixes loop #1 by adding 1 to limit before flooring the result. Therefore, if limit is 1 you will have

Math.floor( /* something in [0, 1) */ * 2 )

which is easy to see returns either 0 or 1 (and not always 1).

Upvotes: 7

Related Questions