生成随机值,保留返回值的历史记录

时间:2012-12-11 09:38:54

标签: javascript jquery random

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

该功能还需要传递id。这样,如果您需要多个随机数,每个都有自己的历史记录,id会跟踪所有这些。

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

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

    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']
    

    我已经设置了一个带有演示的Fiddle

5 个答案:

答案 0 :(得分:4)

  1. 这不是最好的做法。 Imo最好是为每个需要生成的数字系列实例化一个对象。
  2. 我建议生成一个包含所有可能值的数组并对其进行混洗。然后你可以弹出它。

答案 1 :(得分:3)

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

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

我从here获得了fisherYates算法。

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

小提琴here

答案 2 :(得分: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变量似乎也是多余的。

答案 3 :(得分: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()来测试之前是否生成了值。

答案 4 :(得分: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>');
    }
});
​

FIDDLE

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

使用该算法,您不断检查数组中是否存在随机值,每次新迭代都会变得越来越慢。

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