JavaScriptを使用して一意のランダム 1から100までの数を生成するにはどうすればよいですか?
たとえば、8つの一意の乱数を生成して配列に保存するには、次のようにします。
var arr = []
while(arr.length < 8){
var r = Math.floor(Math.random()*100) + 1;
if(arr.indexOf(r) === -1) arr.Push(r);
}
document.write(arr);
100個の数字の順列を生成し、連続して選択します。
Knuth Shuffle(Fisher-Yates shuffle)Algorithm を使用します。
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個の数字の配列があるとします{e.g. [1,2,3 ... 100]}そして、8回のスワップ後にスワップを停止します。ほとんどの場合、配列は{1,2,3,76,5,6,7,8、... numbers here shuffled ... 10}のようになります。
すべての数字は確率1/100で交換されるため、問題はありません。最初の8つの数字を交換することは8/100ですが、問題はありません。他の92のスワップの92/100です。
しかし、配列全体に対してアルゴリズムを実行すると、(ほぼ)すべてのエントリが確実に交換されます。
さもなければ、私たちは質問に直面します:どの8つの数字を選ぶべきですか?
ライブラリを回避したい場合は上記の手法が適していますが、ライブラリで問題ないかどうかに応じて、JavaScriptでランダムなものを生成するために Chance をチェックすることをお勧めします。
特に、Chanceを使用して質問を解決するには、次のように簡単です。
// 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>
免責事項、チャンスの著者として、私は少し偏っています;)
長くて信頼できないシャッフルを避けるために、私は次のことをします...
出来上がり-繰り返される数字はありません。
誰かが興味を持っているなら、後で実際のコードを投稿するかもしれません。
編集:それはおそらく私にとっては競争の激しいものですが、@ 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>
Set (および平均ケースO(n))を使用したモダンなJSソリューション
const nums = new Set();
while(nums.size !== 8) {
nums.add(Math.floor(Math.random() * 100) + 1);
}
console.log([...nums]);
これは、配列に対してランダムな一意/非一意の整数を生成するために作成した非常に汎用的な関数です。この回答のこのシナリオでは、最後のパラメーターが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);
それで全部です。
私はこれをします:
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;
もう1つの簡単な方法は、昇順の数字で100個のアイテムの配列を生成し、ランダムに並べ替えることです。これは実際には非常に短く、(私の意見では)単純なスニペットにつながります。
const numbers = [ ...Array(100).keys() ].map(num => num + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));
数字を1から100にシャッフルするのが正しい基本戦略ですが、シャッフルした数字が8つだけ必要な場合は、100個すべての数字をシャッフルする必要はありません。
私はJavascriptをあまりよく知りませんが、100個のnullの配列をすばやく簡単に作成できると信じています。次に、8ラウンドで、配列のn番目の要素(nは0から始まる)を、n + 1から99までのランダムに選択された要素と交換します。もちろん、まだ実装されていない要素は、元のインデックスに1を加えたものです。したがって、考慮に入れるのは簡単です。8回のラウンドが終了すると、配列の最初の8個の要素に8個のシャッフルされた数字が入ります。
私の問題はこれらの穴を埋めることでしたので、このような[,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
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);
最も早い回答は、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));
var arr = []
while(arr.length < 8){
var randomnumber=Math.ceil(Math.random()*100)
if(arr.indexOf(randomnumber) === -1){arr.Push(randomnumber)}
}
document.write(arr);
私が見た他の答えよりも短い
Set
を使用するのが最も速いオプションです。コールバックジェネレーターを使用する一意のランダムを取得するための汎用関数を次に示します。現在はfastおよびreusableです。
// 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))
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の更新を提案しようとしましたが、拒否されました。私はまだ他の人に役立つかもしれないと思います。
これは私の個人的な解決策です:
<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)をランダムに生成し、アラートボックスを使用して表示します。
さらに一意が必要な場合は、array(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]
このソリューションは、配列内に存在するかどうかを確認するよりもはるかにパフォーマンスが高い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))
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
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
}
これは一緒にまとめた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);
これにより、最大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);
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項目を見つける方が、配列全体を並べ替えるよりも安価です。
ここで、ソートアルゴリズムがこのアルゴリズムに影響することに注意する必要があります。使用されるソートアルゴリズムが安定している場合は、小さい数字を優先するわずかなバイアスがあります。理想的には、ソートアルゴリズムを不安定にし、安定性(または不安定性)に偏らないようにして、完全に均一な確率分布で答えを生成するようにします。
これをジェネレーターとして実装すると、作業が非常に便利になります。この実装は、最初に入力配列全体をシャッフルする必要がある実装とは異なることに注意してください。
この
sample
関数は遅延して動作し、要求するN
アイテムまでの反復ごとに1ランダムアイテムを提供します。 3のリストから1000、最初に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
は、配列入力の変更のためにpure関数ではなくなりましたが、特定の状況(上記)でより意味があります。
配列を返すだけの関数の代わりにジェネレーターを選択したもう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
が返されます。
注:
この回答 は元々、この質問の複製として閉じられた別の質問で共有されました。ここで提供される他のソリューションとは非常に異なるため、ここでも共有することにしました
ハッシュテーブルとしてのオブジェクトプロパティ を使用してはどうですか?このように、最良のシナリオは、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;
}
}