为什么这个Javascript循环是无限的?

时间:2012-04-21 19:13:52

标签: javascript while-loop qunit

我不明白为什么这个while循环是无限的:

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("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;
        }
    });

解决方案:

对不起,错误是在另一个测试中,就像@Jon回答描述的那样,当anyNumber等于1时,无限循环发生:

    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);
        }
    });

1 个答案:

答案 0 :(得分:7)

无尽循环#1

如果limit == 1,循环将是无穷无尽的。考虑:

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

Math.random返回[0,1]范围内的数字。乘以limit不会改变这一点,添加一个会将其带到[1,2],因此根据定义Math.floor将返回1

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

这里我们有相同的逻辑,所以:

  • 第一次调用该方法时,它将返回1(它不会进入循环,因为prevRandomNumber为-1)
  • 第二次及以后的方法永远不会返回,因为prevRandomNumber已经是1

如果limit是大于1的数字,我无法看到循环是无限的。

无尽循环#2

如果limit不是数字,请考虑会发生什么。

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

此处,Math.random() * limit将为NaN,表达式的最终结果也将如此。因此actualRandomNumber 始终等于NaN。该方法将在第一次调用时返回Nan,并且不会第二次返回,原因与上述相同。

解决方案

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

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

第一行修复无限循环#2,第二行修复循环#1,在将结果放平之前加1限制。因此,如果limit为1,您将拥有

Math.floor( /* something in [0, 1) */ * 2 )
容易看到的

返回0或1(而不是总是 1)。