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

デモでFiddleをセットアップしました。

4

5 に答える 5

4
  1. これはベストプラクティスではありません。Imoは、生成する必要のある一連の数値ごとにオブジェクトをインスタンス化する方がよいでしょう。
  2. 可能なすべての値の配列を生成し、それをシャッフルすることをお勧めします。次に、それをポップすることができます。
于 2012-12-11T09:45:47.810 に答える
3

私は Jack のコードを採用し、popping array メソッドで動作するようにそれを適応させました。

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

ほとんどのユースケースでは、すべての値の配列を生成し、それらをシャッフルしてから、代わりに必要に応じてポップしたいというアレックスに同意します。

次に例を示します。

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">フィドル

このアルゴリズムでは、初期費用が大きくなりますが、少なくとも完了するまでにかかる時間はわかっています (およそ O(n))。

ランダムな値が配列内にあるかどうかを常にチェックしているアルゴリズムでは、新しい反復ごとにますます遅くなります。

データセットが常に比較的小さい場合、アルゴリズムはもう少しうまく機能する可能性がありますが、10 を超えるとその優位性が失われ始めます。

于 2012-12-11T10:11:18.810 に答える