1から100の間の一意の乱数を生成します


98

JavaScriptを使用して1から100までのユニークな乱数を生成するにはどうすればよいですか?


18
これはJavaScriptに焦点を当てているため、実際にはまともではありません。
義母

2
@dottyよくJavascriptでこれを実行することと他の言語で実行することの間に本質的な違いはありませんが、締めくくることはしません。
Pointy

1
私はどちらかを閉じるために投票しません。これは十分に具体的です。
Josh Stodola


1
これを行うために別のクリーンな方法がありますstackoverflow.com/questions/51898200/...
Huangism

回答:


172

例:8つの一意の乱数を生成して配列に格納するには、次のようにします。

var arr = [];
while(arr.length < 8){
    var r = Math.floor(Math.random() * 100) + 1;
    if(arr.indexOf(r) === -1) arr.push(r);
}
console.log(arr);


15
実際のコードは、疑似コードよりもそのような質問に対してはるかに優れています;)(疑似コードである私の答えを削除しました...)
Roman Starkov

3
Oを選ぶことができます。var randomnumber = Math.ceil(Math.random()* 100)を使用
Alsciende

9
-1:このアルゴリズムは単純なアプローチです。それは非常に非効率的です。
Frerich Raabe

39
ワオ。ナイーブは少し強いようです。それは最善の解決策ではないかもしれませんが、何が起こっているのかを簡単、短く、簡単に確認でき、達成する必要のあるものに対して許容可能な操作パラメーター内で実行されます。次のタスクに進みます。完璧は素晴らしいですが、「完了」は「完璧」よりも優れています。
adam0101 2010年

4
関数が配列で0を返す可能性があります。このリンクによると:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…、Math.random()Returns a random number between 0 (inclusive) and 1 (exclusive)the Math.random()偶然に0が返された場合Math.ceil(0)、確率も低いですが、0も0です。
Qian Chen

43
  1. 配列に1から100までの数値を入力します。
  2. シャッフルます。
  3. 結果の配列の最初の8つの要素を取得します。

8
確かに、コードを微調整して最初の8つのシャッフルだけを行う方が効率的ですか?(そして、セミシャッフルされた配列の最後の8つの要素を取得します)
2

1
これもいつものやり方です。したがって、大量の行が含まれているファイルからランダムな10行が必要な場合は、そうしますrandlines file | head -10
tchrist

1
これは適切な答えだと思います。これは、確率分布が維持されるためです。受け入れられた答えはそうではありません
ロベルトトマス

2
N = 10 ^ 12の場合はどうなりますか?あまり効率的ではありません。
しんぞう2018年

2
@shinzou現実の世界では、JavaScriptを使用して10 ^ 12の数値をソートすることはありません。君主の質問は、ささいな答えを要求します。私は世界の飢餓を解決するためにここにいるのではありません。私はそれを行うのに十分な備えがありますが、それは問題ではありません。
ЯegDwight

14

100個の数値の順列を生成し、シリアルに選択します。

使用クヌースシャッフル(別名フィッシャーイエーツシャッフル)アルゴリズム

JavaScript:

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

     // Edited thanks to Frerich Raabe
     c++;
     if(c == stop_count)return;

   }
}

リンクからコピーされたコード。

編集

改善されたコード:

function fisherYates(myArray,nb_picks)
{
    for (i = myArray.length-1; i > 1  ; i--)
    {
        var r = Math.floor(Math.random()*i);
        var t = myArray[i];
        myArray[i] = myArray[r];
        myArray[r] = t;
    }

    return myArray.slice(0,nb_picks);
}

潜在的な問題:

100個の数値の配列{eg [1,2,3 ... 100]}があり、8回のスワップ後にスワップを停止するとします。その後、ほとんどの場合、配列は{1,2,3,76,5,6,7,8、...ここの数値はシャッフルされます... 10}のようになります。

なぜなら、すべての数値は確率1/100で交換されるからです。最初の8つの数値を交換する確率は8/100ですが、確率です。他の92の交換の92/100です。

しかし、完全な配列のアルゴリズムを実行すると、(ほぼ)すべてのエントリが交換されます。

そうでなければ、私たちは疑問に直面します:どの8つの数字を選ぶべきですか?


5
このアプローチは正しいですが、最適ではありません。8つの乱数が必要なだけなので、8つのスワップ後にシャッフルを停止できます。上記のコードは配列全体(このシナリオでは100要素)を交換します。
Frerich Raabe

コードは大幅に改善される可能性があります。戻り値、副作用、関数の使用法はすべて、本当にぼやけたIMOです。おそらく、fisherYates関数を使用して、元の問題に正確に答える関数を作成すると、より明確になります。
Alsciende

1
改善されたコードで更新された回答。また、@ Frerich Raabe:8つのスワップ後に停止する問題が言及されています。
Pratik Deoghare

フィッシャーイェーツのアルゴリズムが間違っています。rはiに依存する必要があります。私anwserを参照してください。stackoverflow.com/questions/2380019/...
Alsciende

おっと私の恐ろしい間違いをごめんなさい!! あなたの実装はクールです。好きでした。+1。他に問題がある場合はお知らせください。ありがとうございます。
Pratik Deoghare

11

Set(および平均ケースO(n))を使用した最新のJSソリューション

const nums = new Set();
while(nums.size !== 8) {
  nums.add(Math.floor(Math.random() * 100) + 1);
}

console.log([...nums]);


なぜO(n)なのですか?任意の時間ループすることはできませんか?
Anthony Wieser 2018年

@AnthonyWieserあなたは正しい、最悪の場合。Set.addがo(1)なので平均的なケースを暗示していました
Alister

これは、使用するように変更される前に使用していた私の回答のように0を返す可能性があると思いますMath.floor(Math.random()*100) + 1
adam0101

SetJSで発見するのはとてもクールです!ただし、8が100に近い場合、特に最後の反復で一意性の要件が満たされるまで、このソリューションは不要な数値の生成を引き起こしませんか?したがって、私はsort以下のエレガントな答えを好むと思います。
Gilad Barner

10

上記の手法はライブラリを避けたい場合に適していますが、ライブラリに問題がないかどうかに応じて、JavaScriptでランダムなものを生成するチャンスを確認することをお勧めします。

具体的には、質問を解決するために、チャンスを使用すると、次のように簡単です。

// One line!
var uniques = chance.unique(chance.natural, 8, {min: 1, max: 100});

// Print it out to the document for this snippet so we can see it in action
document.write(JSON.stringify(uniques));
<script src="http://chancejs.com/chance.min.js"></script>

免責事項、チャンスの作者として、私は少し偏っています;)


これまでに実行コードスニペットを見たことがないため、賛成票を投じます
surfmuggle

一意である必要があるクーポンのコード(8つのランダムな英数字の文字列)を作成する場合、Chance.jsでそれを行うにはどうすればよいですか?注:クーポンはオンデマンドで作成されるため、コードの数は無期限になります
Oscar Yuandinata

@OscarYuandinata簡単var codes = chance.unique(chance.string, 8)です。特定の文字プールから取得したコードが必要な場合は、次のように指定できますchance.unique(chance.string, 8, {pool: "abcd1234"})。abcd1234には、プール内の任意の文字を指定できます。参照してくださいchancejs.com/#string
ビクター・クイン

@VictorQuinn、ごめんなさい。つまり、クーポンコードは8文字のランダムな英数字の文字列であり、8つのランダムな英数字の文字列の配列ではありません。hahaha ..
オスカーユアンディナタ2015年

ああ@OscarYuandinataはるかに簡単chance.string({ length: 8 })です。特定の文字のみをその文字列に表示したい場合chance.string({ pool: 'abcd1234', length: 8 })、abcd1234の文字からランダムな8文字の文字列が返されます。たとえば、「2c2c44bc」や「331141cc」
Victor Quinn

8

長くて信頼できないシャッフルを回避するには、次のようにします...

  1. 1から100までの数を順番に含む配列を生成します。
  2. 1から100までの乱数を生成します
  3. 配列内のこのインデックスで番号を検索し、結果に保存します
  4. 配列から要素を削除し、1つ短くします
  5. 手順2から繰り返しますが、乱数の上限として99を使用します
  6. 手順2から繰り返しますが、乱数の上限として98を使用します
  7. 手順2から繰り返しますが、乱数の上限として97を使用します
  8. 手順2から繰り返しますが、乱数の上限として96を使用します
  9. 手順2から繰り返しますが、乱数の上限として95を使用します
  10. 手順2から繰り返しますが、乱数の上限として94を使用します
  11. 手順2から繰り返しますが、乱数の上限として93を使用します

出来上がり-繰り返される数字はありません。

興味があれば、後で実際のコードを投稿することもあります。

編集:それはおそらく私にとって競争の激しいものですが、@ Alsciendeの投稿を見て、約束したコードを投稿することに抵抗できませんでした。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>8 unique random number between 1 and 100</title>
<script type="text/javascript" language="Javascript">
    function pick(n, min, max){
        var values = [], i = max;
        while(i >= min) values.push(i--);
        var results = [];
        var maxIndex = max;
        for(i=1; i <= n; i++){
            maxIndex--;
            var index = Math.floor(maxIndex * Math.random());
            results.push(values[index]);
            values[index] = values[maxIndex];
        }
        return results;
    }
    function go(){
        var running = true;
        do{
            if(!confirm(pick(8, 1, 100).sort(function(a,b){return a - b;}))){
                running = false;
            }
        }while(running)
    }
</script>
</head>

<body>
    <h1>8 unique random number between 1 and 100</h1>
    <p><button onclick="go()">Click me</button> to start generating numbers.</p>
    <p>When the numbers appear, click OK to generate another set, or Cancel to stop.</p>
</body>


しかし、8番目の数字は1から100までではなく、1から92までのランダムな数字です。90の数字を選ぶ必要がある場合、最後の数字は1から10までしか選ばれませんね。
adam0101 2010年

@ adam0101いいえ、選択するときに数字を削除するためです。したがって、ステップ5では、彼の配列には99個の数値しかありません。@belugabobあなたはKnuth Shuffleよりも効率的ではありません。実際、スプライスはシャッフル(完全に信頼できる)よりもおそらく高価です
Alsciende

@ adam0101:選択した要素を配列から削除し(上記の手順4を参照)、要素が2回選択されるのを防ぎます。次に、単に配列が短いため、次の乱数に下限の上限を使用します。
Frerich Raabe

@アルシエンデ、はい-シャッフルを使用してこれをより効率的に行う方法があると考えましたが、完全にはわかりませんでした。配列から項目を削除しないようにするには、配列の最後のエントリ(選択したものでない場合)を選択した位置にコピーします。
belugabob

1
values.lengthを減分しない理由は、配列の長さの減少がメモリの再割り当てによって行われないという保証がないためです。maxIndexを使用しても、配列内の最後のエントリが無関係になるため、単に無視するだけで同じ効果があります。
belugabob

8

もう1つのアプローチは、昇順の数値で100アイテムの配列を生成し、ランダムに並べ替えることです。これは実際には非常に短く、(私の意見では)単純なスニペットにつながります。

const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));


これは私の中で一番好きな答えです。Dunnoなぜ6票しか得られなかったのか。エレガントで、かなり複雑です(ただしsort、十分に実装されている場合、それは確かです)。
Gilad Barner

3

私はこれを行います:

function randomInt(min, max) {
    return Math.round(min + Math.random()*(max-min));
}
var index = {}, numbers = [];
for (var i=0; i<8; ++i) {
    var number;
    do {
        number = randomInt(1, 100);
    } while (index.hasOwnProperty("_"+number));
    index["_"+number] = true;
    numbers.push(number);
}
delete index;

3

これは、配列のランダムな一意/非一意の整数を生成するために私が作成した非常に一般的な関数です。この回答では、このシナリオで最後のパラメーターがtrueであると想定します。

/* Creates an array of random integers between the range specified 
     len = length of the array you want to generate
     min = min value you require
     max = max value you require
     unique = whether you want unique or not (assume 'true' for this answer)
*/
    function _arrayRandom(len, min, max, unique) {
        var len = (len) ? len : 10,
                min = (min !== undefined) ? min : 1,
                max = (max !== undefined) ? max : 100,
                unique = (unique) ? unique : false,
                toReturn = [], tempObj = {}, i = 0;

        if(unique === true) {
            for(; i < len; i++) {
                var randomInt = Math.floor(Math.random() * ((max - min) + min));
                if(tempObj['key_'+ randomInt] === undefined) {
                    tempObj['key_'+ randomInt] = randomInt;
                    toReturn.push(randomInt);
                } else {
                    i--;
                }
            }
        } else {
            for(; i < len; i++) {
                toReturn.push(Math.floor(Math.random() * ((max - min) + min)));
            }
        }

        return toReturn;
    }

ここで「tempObj」は非常に便利なオブジェクトです。生成されたすべての乱数は、そのキーがすでに存在する場合にこのtempObjを直接チェックインします。存在しない場合は、現在の乱数がすでに存在するため、1回余分に実行する必要があるため、iを1つ減らします。 。

あなたの場合、以下を実行してください

_arrayRandom(8, 1, 100, true);

それで全部です。


0を含めたい場合はどうなりますか?行min = (min) ? min : 1,は常に1を返します(したがって0は選択されません)
TBE

非常に良い点です。:)。ありがとう、私は適切な変更を行いました。0を渡しても戻ります
。– kaizer1v

2

1から100までの数値をシャッフルするのが正しい基本的な戦略ですが、8つのシャッフルされた数値のみが必要な場合は、100の数値すべてをシャッフルする必要はありません。

Javascriptについてはよく知りませんが、100個のnullの配列をすばやく作成するのは簡単だと思います。次に、8ラウンドで、配列のn番目の要素(nは0から始まります)をn + 1から99までのランダムに選択された要素と交換します。もちろん、要素がまだ入力されていない場合は、要素が実際に元のインデックスに1を加えたものなので、簡単に組み込むことができます。8ラウンドを完了すると、配列の最初の8要素に8つのシャッフルされた数値が含まれます。


2
var arr = []
while(arr.length < 8){
  var randomnumber=Math.ceil(Math.random()*100)
  if(arr.indexOf(randomnumber) === -1){arr.push(randomnumber)}  
}
document.write(arr);

私が見た他の答えよりも短い


1

The Machine Charmerと同じ順列アルゴリズムですが、プロトタイプの実装があります。多数のピックに適しています。使用可能な場合は、js 1.7破壊代入を使用します。

// swaps elements at index i and j in array this
// swapping is easy on js 1.7 (feature detection)
Array.prototype.swap = (function () {
    var i=0, j=1;
    try { [i,j]=[j,i]; }
    catch (e) {}
    if(i) {
        return function(i,j) {
            [this[i],this[j]] = [this[j],this[i]];
            return this;
        }
    } else {
        return function(i,j) {
            var temp = this[i];
            this[i] = this[j];
            this[j] = temp;
            return this;
        }
    }
})();


// shuffles array this
Array.prototype.shuffle = function() {
    for(var i=this.length; i>1; i--) {
        this.swap(i-1, Math.floor(i*Math.random()));
    }
    return this;
}

// returns n unique random numbers between min and max
function pick(n, min, max) {
    var a = [], i = max;
    while(i >= min) a.push(i--);
    return a.shuffle().slice(0,n);
}

pick(8,1,100);

編集:ベルガボブの答えに基づく、少数のピックに適している別の命題。一意性を保証するために、選択した数値を配列から削除します。

// removes n random elements from array this
// and returns them
Array.prototype.pick = function(n) {
    if(!n || !this.length) return [];
    var i = Math.floor(this.length*Math.random());
    return this.splice(i,1).concat(this.pick(n-1));
}

// returns n unique random numbers between min and max
function pick(n, min, max) {
    var a = [], i = max;
    while(i >= min) a.push(i--);
    return a.pick(n);
}

pick(8,1,100);

素晴らしい再帰的な実装-スプライスを使用しない代替案を投稿しましたが、これは回避可能なパフォーマンスヒットであると感じています(OPがパフォーマンスに問題を抱えていたわけではありません)
belugabob

あなたの解決策は賢いですが、私がArray#pickメソッドでそれを使用することはありません。これは、返されたときに要素の順序が入れ替わらないようにするためです。
Alsciende 2010年

シャッフルしたくないアレイ、元の1-100アレイ、またはその結果?前者は機能する配列であるため、問題にはなりません。後者は、コードの性質上、いずれにしてもランダムな順序で出力されます。理由がよくわかりません。
belugabob

オリジナルのもの。有用なArray#pickメソッドを実装しました。場合、この関数は知りません、これは作業配列かではありません。汎用的であるために、それは変更されません。この必要以上に。
アルシエンデ2010年

しかし、それでも、たとえ少しでも、それを変更します。これは、この手法を使用する場合には不可能です。
belugabob

1

このような穴のある配列の場合 [,2,,4,,6,7,,] は、私の問題はこれらの穴を埋めることでした。だから私は私の必要に応じてそれを変更しました:)

次の修正されたソリューションは私のために働きました:)

var arr = [,2,,4,,6,7,,]; //example
while(arr.length < 9){
  var randomnumber=Math.floor(Math.random()*9+1);
  var found=false;
  for(var i=0;i<arr.length;i++){
    if(arr[i]==randomnumber){found=true;break;}
  }

  if(!found)
    for(k=0;k<9;k++)
    {if(!arr[k]) //if it's empty  !!MODIFICATION
      {arr[k]=randomnumber; break;}}
}

alert(arr); //outputs on the screen

1

以前の最良の答えは、 sje397。できるだけ早く、できるだけ良い乱数を取得します。

私の解決策は彼の解決策とよく似ています。ただし、ランダムな番号の乱数が必要な場合があるため、回答を投稿することにしました。また、一般的な機能を提供します。

function selectKOutOfN(k, n) {
  if (k>n) throw "k>n";
  var selection = [];
  var sorted = [];
  for (var i = 0; i < k; i++) {
    var rand = Math.floor(Math.random()*(n - i));
    for (var j = 0; j < i; j++) {
      if (sorted[j]<=rand)
        rand++;
      else
        break;
    }
    selection.push(rand);
    sorted.splice(j, 0, rand);
  }
  return selection;
}

alert(selectKOutOfN(8, 100));

1

これが私が一緒に作った私のES6バージョンです。もう少し統合できると思います。

function randomArray(i, min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  
  let arr = Array.from({length: i}, () => Math.floor(Math.random()* (max - min)) + min);
  
  return arr.sort();
 }
 
 let uniqueItems = [...new Set(randomArray(8, 0, 100))]
 console.log(uniqueItems);


0

オブジェクトプロパティをハッシュテーブルとして使用するのはどうですか?このようにして、最良のシナリオは、8回だけランダム化することです。数値の範囲のごく一部が必要な場合にのみ有効です。また、配列にスペースを割り当てる必要がないため、Fisher-Yatesよりもメモリ使用量がはるかに少なくなります。

var ht={}, i=rands=8;
while ( i>0 || keys(ht).length<rands) ht[Math.ceil(Math.random()*100)]=i--;
alert(keys(ht));

その後、Object.keys(obj)がECMAScript 5の機能であることを発見したので、上記は現在インターネットではほとんど役に立ちません。このようなキー関数を追加してECMAScript 3互換にしたので、心配しないでください。

if (typeof keys == "undefined") 
{ 
  var keys = function(obj) 
  {
    props=[];
    for (k in ht) if (ht.hasOwnProperty(k)) props.push(k);
    return props;
  }
}

0
var bombout=0;
var checkArr=[];
var arr=[];
while(arr.length < 8 && bombout<100){
  bombout++;
  var randomNumber=Math.ceil(Math.random()*100);
  if(typeof checkArr[randomNumber] == "undefined"){
    checkArr[randomNumber]=1;
    arr.push(randomNumber);
  }
}​

// untested - hence bombout

0

より一意が必要な場合は、配列(1..100)を生成する必要があります。

var arr=[];
function generateRandoms(){
for(var i=1;i<=100;i++) arr.push(i);
}
function extractUniqueRandom()
{
   if (arr.length==0) generateRandoms();
   var randIndex=Math.floor(arr.length*Math.random());
   var result=arr[randIndex];
   arr.splice(randIndex,1);
   return result;

}
function extractUniqueRandomArray(n)
{
   var resultArr=[];
   for(var i=0;i<n;i++) resultArr.push(extractUniqueRandom());
   return resultArr;
}

上記のコードはより高速です:
extractUniqueRandomArray(50)=> [2、79、38、59、63、42、52、22、78、50、39、77、1、88、40、23、48、84、91、 49、4、54、93、36、100、82、62、41、89、12、24、31、86、92、64、75、70、61、67、98、76、80、56、90、 83、44、43、47、7、53]


0

JavaScript 1.6のindexOf関数を使用して、同じコードの別のより良いバージョン(承認された回答)を追加します。重複をチェックするたびに配列全体をループする必要はありません。

var arr = []
while(arr.length < 8){
  var randomnumber=Math.ceil(Math.random()*100)
  var found=false;
    if(arr.indexOf(randomnumber) > -1){found=true;}
  if(!found)arr[arr.length]=randomnumber;
}

古いバージョンのJavascriptでも、上部のバージョンを使用できます

PS:wikiの更新を提案しようとしましたが、拒否されました。私はまだそれが他の人に役立つかもしれないと思います。


0

これは私の個人的な解決策です:

<script>

var i, k;
var numbers = new Array();
k = Math.floor((Math.random()*8));
numbers[0]=k;
    for (var j=1;j<8;j++){
        k = Math.floor((Math.random()*8));
i=0;
while (i < numbers.length){
if (numbers[i] == k){
    k = Math.floor((Math.random()*8));
    i=0;
}else {i++;}
}
numbers[j]=k;
    }
    for (var j=0;j<8;j++){
alert (numbers[j]);
    }
</script>

8つの一意の配列値(0から7の間)をランダムに生成し、アラートボックスを使用してそれらを表示します。


0
function getUniqueRandomNos() {
    var indexedArrayOfRandomNo = [];
    for (var i = 0; i < 100; i++) {
        var randNo = Math.random();
        indexedArrayOfRandomNo.push([i, randNo]);
    }
    indexedArrayOfRandomNo.sort(function (arr1, arr2) {
        return arr1[1] - arr2[1]
    });
    var uniqueRandNoArray = [];
    for (i = 0; i < 8; i++) {
        uniqueRandNoArray.push(indexedArrayOfRandomNo[i][0]);
    }
    return uniqueRandNoArray;
}

この方法は、ほとんどの回答で示されている方法とは異なると思うので、ここに回答を追加することを考えた(質問は4年前に行われた)。

100個の乱数を生成し、それぞれに1から100までの数字でタグ付けします。次に、これらのタグ付けされた乱数を並べ替えると、タグがランダムにシャッフルされます。あるいは、この質問で必要に応じて、タグ付き乱数の上位8つを見つけるだけで済ますことができます。上位8項目を見つける方が、配列全体を並べ替えるよりも安価です。

ここで、ソートアルゴリズムがこのアルゴリズムに影響を与えることに注意する必要があります。使用されるソートアルゴリズムが安定している場合は、数値が小さい方が有利です。理想的には、ソートアルゴリズムを不安定にし、安定性(または不安定性)に偏らないようにして、完全に均一な確率分布の答えを生成する必要があります。


0

これは、最大20桁の一意の乱数の生成を処理できます。

JS

 var generatedNumbers = [];

    function generateRandomNumber(precision) { // input --> number precision in integer 
        if (precision <= 20) {
            var randomNum = Math.round(Math.random().toFixed(precision) * Math.pow(10, precision));
            if (generatedNumbers.indexOf(randomNum) > -1) {
                if (generatedNumbers.length == Math.pow(10, precision))
                    return "Generated all values with this precision";
                    return generateRandomNumber(precision);
            } else {
                generatedNumbers.push(randomNum);
                return randomNum;
            }
        } else
           return "Number Precision shoould not exceed 20";
    }
    generateRandomNumber(1);

ここに画像の説明を入力してください

jsFiddle


0

このソリューションは、が配列に存在するかどうかをチェックするよりもはるかにパフォーマンスの高いO(1)であるハッシュを使用します。安全チェックも追加されています。それが役に立てば幸い。

function uniqueArray(minRange, maxRange, arrayLength) {
  var arrayLength = (arrayLength) ? arrayLength : 10
  var minRange = (minRange !== undefined) ? minRange : 1
  var maxRange = (maxRange !== undefined) ? maxRange : 100
  var numberOfItemsInArray = 0
  var hash = {}
  var array = []

  if ( arrayLength > (maxRange - minRange) ) throw new Error('Cannot generate unique array: Array length too high')

  while(numberOfItemsInArray < arrayLength){
    // var randomNumber = Math.floor(Math.random() * (maxRange - minRange + 1) + minRange)
    // following line used for performance benefits
    var randomNumber = (Math.random() * (maxRange - minRange + 1) + minRange) << 0

    if (!hash[randomNumber]) {
      hash[randomNumber] = true
      array.push(randomNumber)
      numberOfItemsInArray++
    }
  }
  return array
}
document.write(uniqueArray(1, 100, 8))

0

これをジェネレーターとして実装すると、操作が非常に簡単になります。この実装は、入力配列全体を最初にシャッフルする必要がある実装とは異なります。

このsample関数は遅延して機能し、反復ごとに1つのランダムなアイテムNを、要求したアイテムまで提供します。1000のリストから3つのアイテムだけが必要な場合は、最初に1000のアイテムすべてに触れる必要がないため、これは便利です。

// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
  let ys = xs.slice(0);
  let len = xs.length;
  while (n > 0 && len > 0) {
    let i = (Math.random() * len) >> 0;
    yield ys.splice(i,1)[0];
    n--; len--;
  }
}

// example inputs
let items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// get 3 random items
for (let i of sample(3) (items))
  console.log(i); // f g c

// partial application
const lotto = sample(3);
for (let i of lotto(numbers))
  console.log(i); // 3 8 7

// shuffle an array
const shuffle = xs => Array.from(sample (Infinity) (xs))
console.log(shuffle(items)) // [b c g f d e a]

私はsample入力配列を変更しない方法で実装することを選択しましたが、変更の実装が有利であると簡単に主張できます。

たとえば、shuffle関数は元の入力配列を変更したい場合があります。または、同じ入力からさまざまなタイミングでサンプリングし、そのたびに入力を更新することもできます。

// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
  let len = xs.length;
  while (n > 0 && len > 0) {
    let i = (Math.random() * len) >> 0;
    yield xs.splice(i,1)[0];
    n--; len--;
  }
}

// deal :: [Card] -> [Card]
const deal = xs => Array.from(sample (2) (xs));

// setup a deck of cards (13 in this case)
// cards :: [Card]
let cards = 'A234567890JQK'.split('');

// deal 6 players 2 cards each
// players :: [[Card]]
let players = Array.from(Array(6), $=> deal(cards))

console.log(players);
// [K, J], [6, 0], [2, 8], [Q, 7], [5, 4], [9, A]

// `cards` has been mutated. only 1 card remains in the deck
console.log(cards);
// [3]

sample配列の入力の変更により、は純粋な関数ではなくなりましたが、特定の状況(上記で説明)では、より意味のある場合があります。


配列を返すだけの関数の代わりにジェネレータを選択したもう1つの理由は、特定の条件までサンプリングを続けたい場合があるためです。

おそらく、私は1,000,000個の乱数のリストから最初の素数を求めています。

  • 「いくつサンプルすべきですか?」–指定する必要はありません
  • 「最初にすべての素数を見つけてから、ランダムな素数を選択する必要がありますか?」–いいえ。

ジェネレータを使用しているので、このタスクは簡単です

const randomPrimeNumber = listOfNumbers => {
  for (let x of sample(Infinity) (listOfNumbers)) {
    if (isPrime(x))
      return x;
  }
  return NaN;
}

これにより、一度に1つの乱数が連続的にサンプリングされ、x素数かどうかが確認され、素数の場合は戻りxます。素数が見つかる前に数値のリストがなくなった場合は、NaNが返されます。


注意:

この回答は元々、この質問の複製としてクローズされた別の質問で共有されました。ここで提供されている他のソリューションとは非常に異なるため、ここでも共有することにしました


0
getRandom (min, max) {
  return Math.floor(Math.random() * (max - min)) + min
}

getNRandom (min, max, n) {
  const numbers = []
  if (min > max) {
    return new Error('Max is gt min')
  }

  if (min === max) {
    return [min]
  }

  if ((max - min) >= n) {
    while (numbers.length < n) {
      let rand = this.getRandom(min, max + 1)
      if (numbers.indexOf(rand) === -1) {
        numbers.push(rand)
      }
    }
  }

  if ((max - min) < n) {
    for (let i = min; i <= max; i++) {
      numbers.push(i)
    }
  }
  return numbers
}

0

aを使用するのSetが最速のオプションです。コールバックジェネレータを使用する一意のランダムを取得するための一般的な関数を次に示します。今では高速再利用可能です。

// Get a unique 'anything'
let unique = new Set()

function getUnique(generator) {
  let number = generator()
  while (!unique.add(number)) {
    number = generator()
  }
  return number;
}

// The generator.  Return anything, not just numbers.
const between_1_100 = () => 1 + Math.floor(Math.random() * 100)

// Test it
for (var i = 0; i < 8; i++) {
  const aNumber = getUnique(between_1_100)
}
// Dump the 'stored numbers'
console.log(Array.from(unique))


0

これは、Fisher Yates / Durstenfeld Shuffleの実装です が、配列を実際に作成しないため、利用可能な要素の数と比較してピックサイズが小さい場合に必要なスペースの複雑さやメモリが削減されます。

100から8つの数値を選択するには、100要素の配列を作成する必要はありません。

配列が作成されていると仮定すると、

  • array(100)の終わりから、rnd1から100までの乱数()を 取得します
  • 100と乱数を入れ替える rnd
  • array(99)で手順1を繰り返します

配列が作成されない場合、実際のスワップ位置を記憶するためにhashMapが使用される場合があります。生成された2番目の乱数が以前に生成された数値の1つと等しい場合、マップは実際の値ではなく、その位置の現在の値を提供します。

const getRandom_ = (start, end) => {
  return Math.floor(Math.random() * (end - start + 1)) + start;
};
const getRealValue_ = (map, rnd) => {
  if (map.has(rnd)) {
    return getRealValue_(map, map.get(rnd));
  } else {
    return rnd;
  }
};
const getRandomNumbers = (n, start, end) => {
  const out = new Map();
  while (n--) {
    const rnd = getRandom_(start, end--);
    out.set(getRealValue_(out, rnd), end + 1);
  }
  return [...out.keys()];
};

console.info(getRandomNumbers(8, 1, 100));
console.info(getRandomNumbers(8, 1, Math.pow(10, 12)));
console.info(getRandomNumbers(800000, 1, Math.pow(10, 15)));


0

以下は、0から100(0と100の両方を含む)の範囲から取得された、重複のない5つの乱数の例です。

let finals = [];
const count = 5; // Considering 5 numbers
const max = 100;

for(let i = 0; i < max; i++){
  const rand = Math.round(Math.random() * max);
  !finals.includes(rand) && finals.push(rand)
}

finals = finals.slice(0, count)

1
コードの説明を追加
Arghya Sadhu

-1

次のような1つのライナーで行うこともできます。

[...((add, set) => add(set, add))((set, add) => set.size < 8 ? add(set.add(Math.floor(Math.random()*100) + 1), add) : set, new Set())]


何も割り当てないという純粋さのために。
MarcinKról、2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.