1

对于我正在处理的一个项目,我需要一个 Javascript 函数,它会在给定范围内返回一个随机数,而不会重复自身,直到整个范围“耗尽”。由于周围没有这样的东西,我设法自己创造了它。

该函数还需要id传递一个。这样,如果您需要多个随机数,每个随机数都有自己的历史记录,则id可以跟踪它们。

该功能有效,但是我需要一些建议;

  1. 这是实现我想要实现的“正确”方式吗?
  2. inArray()使用非常大的范围 ( maxNum) 值执行的速度有多快?我有一种感觉,大数字会减慢函数的速度,因为它会随机化数字,直到它生成一个仍然“有效”的数字(即不在历史数组中)。但我想不出另一种方法来做到这一点..

剧本:

var UniqueRandom = {
    NumHistory: [],
    generate: function (maxNum, id) {
        if (!this.NumHistory[id]) this.NumHistory[id] = [];
        if (maxNum >= 1) {
            var current = Math.round(Math.random() * (maxNum - 1)), x = 0;
            if (maxNum > 1 && this.NumHistory[id].length > 0) {
                if (this.NumHistory[id].length !== maxNum) {
                    while ($.inArray(current, this.NumHistory[id]) !== -1) {
                        current = Math.round(Math.random() * (maxNum - 1));
                        x = x + 1;
                    }
                    this.NumHistory[id].push(current);
                } else {
                    //reset
                    this.NumHistory[id] = [current];
                }
            } else {
                //first time only
                this.NumHistory[id].push(current);
            }
            return current;
        } else {
            return maxNum;
        }
    },
    clear: function (id) {
        this.NumHistory[id] = [];
    }
};

用法将是:(100 是范围(0-100)和 the_id 是..好吧,id)

UniqueRandom.NumHistory[100, 'the_id']

我已经设置了一个带有演示的小提琴。

4

5 回答 5

4
  1. 这不是最佳做法。Imo 最好为每个需要生成的数字系列实例化一个对象。
  2. 我建议生成一个包含所有可能值的数组并将其改组。然后你就可以弹出它了。
于 2012-12-11T09:45:47.810 回答
3

我采用了 Jack 的代码并对其进行了调整以使用弹出数组方法。

function fisherYates ( myArray ) {
  var i = myArray.length;
  if ( i == 0 ) return false;
  while ( --i ) {
     var j = Math.floor( Math.random() * ( i + 1 ) );
     var tempi = myArray[i];
     var tempj = myArray[j];
     myArray[i] = tempj;
     myArray[j] = tempi;
   }
}

function RandomGenerator(maxNum) {

    this.max = maxNum;
    this.initRandomArray();

}

RandomGenerator.prototype.generate = function() {

    // if no more numbers available generate new array
    if( this.left === 0 ) this.initRandomArray();

    this.last = this.arr.pop();
    this.left = this.arr.length;
    this.history.push( this.last );
    return this.last;
}

RandomGenerator.prototype.initRandomArray = function() {

    this.arr = [];
    this.history = [];
    this.last = null;
    this.left = this.max;

    for( var i = 0; i < this.max; i++ ) {
        this.arr.push( i );
    }

    fisherYates( this.arr );

}

var mygen = new RandomGenerator(100);
console.log( mygen.generate() );

我从这里得到了 FisherYates 算法。

如果已经在历史对象中找到新随机数,则生成新随机数的方法将导致不必要的循环。

在这里提琴

于 2012-12-11T10:52:03.210 回答
1

我倾向于认为它确实不是最有效的。我没有立即得到//first time only.
此外,您可以通过跳过else return ..并编写相反的条件来使代码更具可读性,例如:

if (maxNum >= 1) {
    //code
} else {
    return maxNum;
}

变成

if (maxNum < 1) { // or maybe even if maxNum == 0
    return maxNum;
}

//code

此外,您的x变量似乎是多余的。

于 2012-12-11T09:51:29.893 回答
1

我可能会这样实现它,使用随机生成器的实际实例。这使每个生成器的历史记录分开。

function RandomGenerator(maxNum)
{
    this.max = maxNum;
    this.history = {};
    this.histn = 0;
}

// generate random number in range [0..maxNum)
RandomGenerator.prototype.generate = function()
{
    var value;

    if (this.histn == this.max ) {
        return false;
    }

    do {
        value = Math.floor(Math.random() * this.max );
    } while (this.history[value]);

    this.history['' + value] = 1;
    ++this.histn;

    return value;
}

var mygen = new RandomGenerator(100);
console.log(mygen.generate());

在我的实现中,我为历史选择了一个普通对象而不是数组;测试之前是否生成过值是通过测试属性而不是$.inArray().

于 2012-12-11T09:53:08.613 回答
0

我同意 Alex 的观点,在大多数用例中,您希望生成一个包含所有值的数组,将它们打乱,然后在需要时弹出它们。

这是一个例子:

var getShuffledUniqueRandoms = function(count, suffix) {
    var values = [];

    for (var i = 1; i < count+1; i++) {
        values.push(i + suffix);
    }

    // Shuffle function originally from:
    //+ Jonas Raoni Soares Silva
    //@ http://jsfromhell.com/array/shuffle [v1.0]

    return (function(o){ //v1.0
        for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
        return o;
    })(values);
}

var values = getShuffledUniqueRandoms(10, "index");


$('button').click(function() {

    if (values.length == 0) {
        $('body').append('<p>Out of values.</p>');
    } else {
        $('body').append('<p>' + values.pop() + '</p>');
    }
});
​

​<a href="http://jsfiddle.net/ZRMcA/" rel="nofollow">FIDDLE

使用这种算法,它的前期成本更高,但至少它有一个已知的完成时间(大约 O(n))。

对于不断检查随机值是否在数组中的算法,每次新迭代都会变得越来越慢。

现在,如果您的数据集总是相对较小,您的算法可能会工作得更好一点,但任何大于 10 左右的数据都会开始失去优势。

于 2012-12-11T10:11:18.810 回答