質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.70%

ランダムに作られた数字が入った配列内に、重複がないようにしたい

解決済

回答 7

投稿

  • 評価
  • クリップ 0
  • VIEW 1,723

mi_mi

score 13

var nums =[];//PCのランダム数字6桁の配列
    var min =1;//最小値
    var max = 43;//最大値
    for ( i = 0; i < 6; i++)
    {
    nums.push(Math.floor(Math.random()*(max + 1 - min ))+min );//ランダム1~43
    }

・やりたい事
ランダムに作られた数字を配列にする事ができたのですが、
重複した数字も入ってしまうことがわかりました。
上記のコードを生かして
ランダムにつくられた数字の配列の中で重複がないようにしたいです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 7

+7

1 から 43 までの値が順に入った配列を作り、シャッフルしてから最初の 6 つを取り出すのが良いと思います。

JavaScript アルゴリズムで配列をシャッフルする

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/26 11:30

    どのくらいシャッフルすればいいかがあるとなおよいかと。

    キャンセル

  • 2018/10/26 11:40

    紹介した Fisher–Yates 法は一回で大丈夫です。

    キャンセル

  • 2018/10/26 11:42

    なるほど確かに!お返事ありがとうございます。

    キャンセル

+6

こんなやり方もできないことはないですね

var min=1;
var max=43;
var choice=6;
var nums =new Array(max).fill(null).map(function(x,y){
  return [Math.random(),y+min];
}).sort(function(x,y){
  return x[0]>y[0]?1:-1;
}).map(function(x){
  return x[1];
}).filter(function(x,y){
  return y<choice;
});
console.log(nums);

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/26 12:56

    ちなみにmax=43は43個のデータをつくる処理なので
    仮にmin=10だと10~52のデータになります。大丈夫ですよね?
    文字通り上限値を43にしたいなら、Array(max-min+1)しないといけません

    キャンセル

checkベストアンサー

+3

こんにちは。

二通りのやり方によるコードを回答します。ご質問に、

上記のコードを生かして

とありましたので、まず

  • (1) ご質問に挙げられているコードをなるべく生かす案

を示し、追加でもうひとつ、同じお題を私がスクラッチから書くとしたらこうかな、という

  • (2) Lodash の shuffle を使う案

を以下にて回答します。

 (1) ご質問に挙げられているコードをなるべく生かす案

ループ毎に1個ずつ乱数を生成してそれを nums に入れることは同じですが、その数が nums に含まれていない場合にのみ numsに追加します。このような追加の仕方でnumsの長さが 6 になったときにループから抜けます。このような考え方で修正したコードが以下です。(※これは ppn さんのご回答の、要素数を減らしたくない場合 のコードとほぼ同じです。)

var nums = [];  // ランダム数字6桁の配列

var min = 1;   // 最小値
var max = 43;  // 最大値

while (nums.length < 6) {
  var n = Math.floor(Math.random()*(max + 1 - min )) + min;
  if (!nums.includes(n))
    nums.push(n);  
}

console.log(nums);

しかし、上記だと必ず何回目かまで(あるいは、実用上耐えうる実行時間内)のループで、 nums に 6個の要素が詰め込まれる、とは言えず、無限ループの可能性がゼロではないです。実際は限りなくゼロに近いでしょうけれども、このままにしておくのも何となく心残りですので、乱数の生成回数に適度に上限を設けて、それに達したら  numsの要素が6個に満たなくてもループを抜けるようにします。以下そのような上限を追加したコードです。

var nums = [];  // ランダム数字6桁の配列

var min = 1;   // 最小値
var max = 43;  // 最大値

var count = 0; // 乱数を生成した回数
var MAX_COUNT = 100000; // 乱数生成回数の上限

while (nums.length < 6 && count < MAX_COUNT) {
  var n = Math.floor(Math.random()*(max + 1 - min )) + min;
  if (!nums.includes(n))
    nums.push(n);  
  count ++;
}

console.log(nums);

上記のコードの意図は、「1から43までの数から無作為に一個取ることを、最大でも10万回やれば、その中に異なる数字が6個あることは、ほぼほぼ期待していいだろう」ということになります。

 (2) Lodash の shuffle を使う案

与えられた配列の要素の順番をランダムに入れ替えることを、シャッフル(shuffle)といいますが、配列の様々な操作が集められているライブラリ Lodash に、 _.shuffle(collection) が用意されていますので、これを使った例を示します。(なお、以下のコードは、Zuishinさんのご回答をコードにしたものの一例にもなっています。)

// 1 から 43 までを昇順に含む配列を作成
const ary1to43 = [...Array(43)].map((_,i) => i+1);

console.log(ary1to43);

// シャッフルした配列を作成
const shuffled1to43 = _.shuffle(ary1to43);

console.log(shuffled1to43);

// 先頭6個の配列を得る
const result = shuffled1to43.slice(0,6);

console.log(result);

以下、上記の動作確認用のサンプルです。

なお、Lodash をCDNから利用する場合のURLは以下に記載されています。

以上参考になれば幸いです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

重複を削除したい場合(要素数が減ります)
(※線形リストを走査しないのですこし早いかも?)
(※ランダムな値の範囲がすごく大きいと無駄なコストがあるかも)
(※未検証です)

    var nums = [];//PCのランダム数字6桁の配列
    var min = 1;//最小値
    var max = 43;//最大値
    for (i = 0; i < 6; i++) {
        nums.push(Math.floor(Math.random() * (max + 1 - min)) + min);//ランダム1~43
    }
    var dict = {};
    nums.forEach(o => dict[o] = 0); // 数値がキーのノードを作成する(distinct)
    var newNums = Object.keys(nums);
    console.log(newNums);

要素数を減らしたくない場合

    var nums = [];//PCのランダム数字6桁の配列
    var min = 1;//最小値
    var max = 43;//最大値
    while (nums.length < 6) {
        var newNum = Math.floor(Math.random() * (max + 1 - min)) + min;
        if (nums.indexOf(newNum) == -1)
            nums.push(newNum);//ランダム1~43
    }
    console.log(nums);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/26 11:21

    数が減りませんか?

    キャンセル

  • 2018/10/26 11:24

    減らしたい(distinctしたい)のかなと。減らないのもかいときました。

    キャンセル

+3

https://qiita.com/cocottejs/items/7afe6d5f27ee7c36c61f
を参考に

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>

<body>
  <script>
     var nums =[];//PCのランダム数字6桁の配列
     var min =1;//最小値
     var max = 43;//最大値
     while(nums.length < 6)
     {
       nums.push(Math.floor(Math.random()*(max + 1 - min ))+min );//ランダム1~43
       // 重複除去
       nums = nums.filter(function (x, i, self) {
         return self.indexOf(x) === i;
       });
     }
     console.log(nums);
  </script>
</body>
</html>

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

 new Set

重複を許さない仕様なら、Set が最適だと思います。

function sample (min, max, length) {
  const set = new Set;

  while (set.size  < length) {
    set.add(Math.floor(Math.random() * (max - min + 1)) + min);
  }

  return Array.from(set);
}

console.log(sample(1, 43, 20));

演算コストを安くするなら、

function sample2 (min, max, length) {
  const set = new Set;
  let i = 0;

  while (i++ < length) {
    set.add(Math.floor(Math.random() * (max - min + 1)) + min);
  }

  while (set.size  < length) {
    set.add(Math.floor(Math.random() * (max - min + 1)) + min);
  }

  return Array.from(set);
}

console.log(sample2(2, 20, 10));

Re: mi_mi さん

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

実際のくじ引きとおなじ方法をプログラミングしてみます。
2つの方法を示します。

その1
box に 1... N を順番にいれます。
乱数で rand を得て、box の rand 番目の数字を nums に入れます。box からはその数字は削除します。
これを 6 回 繰り返します。

その2
box に 1... N を順番にいれます。
box の中身をシャッフルします。
先頭の 6 つを nums とします。

nums.js

var nums = []; // PCのランダム数字6個の配列
var min =1; // 最小値
var max = 10; // 43; // 最大値
var box = [...Array(max - min + 1).keys()].map(i => i + min)
// console.log(box);

for (var i = 0; i < 6; i++) {
    if (box.length == 0) {
    break;
    }
    p = Math.floor(Math.random() * box.length);
    nums.push(box[p]);
    box.splice(p, 1);
}
console.log(nums);

box = [...Array(max - min + 1).keys()].map(i => i + min)
for (var i = box.length - 1; i >= 0; i--) {
  // 0~iのランダムな数値を取得
  var rand = Math.floor( Math.random() * ( i + 1 ) );
  // 配列の数値を入れ替える
  [box[i], box[rand]] = [box[rand], box[i]]
}
nums = box.slice(0, 6);
console.log(nums);

実行例
イメージ説明

参考情報

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.70%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる