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

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

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

6回答

2387閲覧

クイズ:重複しない乱数の作り方

williamsArk

総合スコア46

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/02/14 13:59

以下のように国旗クイズを作りましたが、単なる乱数のため、同じ国が2回以上出てしまったりします。
重複しないような仕組みにしたいのですが、どのようにすることができるでしょうか? きっかけも掴めないため困っています。

(function() { 'use strict'; var question = document.getElementById('question'); var btn = document.getElementById('btn'); var input = document.getElementById('input'); var check = document.getElementById('check'); var timerLabel = document.getElementById('timer'); var scoreLabel = document.getElementById('score'); var answer = ''; var score = 0; var timerId; var isPlaying = false; var timer; var flag; var flags = [ ['Nepal.png', 'ネパール'], ['India.png', 'インド'], ['Netherland.png', 'オランダ'], ['Chili.png', 'チリ'], ['Morocco.png', 'モロッコ'], ['Luxembourg.png', 'ルクセンブルク'], ['Singapore.png', 'シンガポール'], ['Pakistan.png', 'パキスタン'], ['Azerbaijan.png', 'アゼルバイジャン'], ['Jamaica.png', 'ジャマイカ'], ['Belgium.png', 'ベルギー'], ['Guana.png', 'ガーナ'], ['SierraLeone.png', 'シエラレオネ'], ['Korea.png','韓国'], ['Japan.png','日本'], ['China.png','中国'], ['Mongolia.png','モンゴル'], ['Philippines.png', 'フィリピン'], ['Italy.png', 'イタリア'], ['Egypt.png', 'エジプト'], ['Australia.png','オーストラリア'], ['NewZealand.png', 'ニュージーランド'], ]; function init() { isPlaying = false; timer = 100; score = 0; scoreLabel.innerHTML = ''; scoreLabel.className = ''; timerLabel.className = ''; timerLabel.innerHTML = timer; btn.className = ''; check.className = 'invisible'; } init(); function updateTimer() { timerId = setTimeout(function() { timer--; if(timer <=10) { timerLabel.classList.add('danger'); } timerLabel.innerHTML = timer; if(timer <= 0) { alert('Game Overです。お疲れ様でした。あなたのスコアは ' + score + '点です。'); clearTimeout(); init(); return; } updateTimer(); }, 1000); } function setQuestion() { flag = flags[Math.floor(Math.random() * flags.length)]; question.children[0].src = 'img/' + flag[0]; answer = flag[1]; } btn.addEventListener('click', function() { if (!isPlaying) { btn.classList.add('invisible'); check.classList.remove('invisible'); isPlaying = true; updateTimer(); setQuestion(); } }); check.addEventListener('click', function() { if(input.value !== '' && isPlaying) { score += (input.value === answer)? 1: -1; if(score < 0) { scoreLabel.classList.add('danger'); } else { scoreLabel.className = ''; } scoreLabel.innerHTML = score; input.value = ''; input.focus(); setQuestion(); } }); })();

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

シャッフルするならこれで十分

javascript

1var flags = ['A国','C国','B国'].map(x=>[x, Math.random()]).sort((x,y)=>x[1]-y[1]).map(x=>x[0]);; 2console.log(flags);

投稿2019/02/15 03:16

yambejp

総合スコア114827

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

papinianus

2019/02/15 03:22

こちらの回答のとおりやりたいことは「シャッフル」。やっていることは「ランダム」。ここにギャップがあります。
yambejp

2019/02/15 03:44

ランダムとはシャッフルしたものを先頭から1つずつ取り出すことで代替できます
guest

0

でたものを覚えておいて、それと合致したものが出たときはパスするとか

投稿2019/02/14 14:26

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

williamsArk

2019/02/14 14:32

回答していただきましてありがとうございます。ただ初心者の身としては、出たものを覚えさせるのに必要なコードがまだわかっておりません。どういう風に書くと、覚えさせることができるでしょうか?簡単にヒントをいただけると助かります。
y_waiwai

2019/02/14 14:40

クイズの回数分の配列変数を作っておいて、クイズの結果を順番に入れておけば。 そんで、乱数を出したときに、その中にあるかどうかを見ればいいんじゃないでしょうか
toshi17922062

2019/02/14 14:43

逆に、これだけの数であれば、予め同じ数だけの連番の配列を準備しておいて、そこからランダムに無くなるまで削除(splice)していった方が楽ではないでしょうか?
williamsArk

2019/02/14 14:55 編集

皆さんありがとうございます。不慣れなものなので、皆さんがおっしゃってることを理解するのも一苦労です。なかなか言葉、用語が難しいですね。spliceは聞いたことがありますが、使ったことはないのです。 どちらが簡単でしょうかね。。。
toshi17922062

2019/02/14 15:13

下のshinji709さんの仕組みも私と同じような方法だと思います。 予め破壊可能な配列に設問と同じだけの連番を用意して、 私の場合は、ランダムにspliceで削除していく。 shinji709さんの場合は、それ自体をランダム配列にしておいて、popで削除していく。 の違いです。 ただ、↓を参照すると、「シャッフルの結果に偏りが見られる」ともありますが、どうなんでしょうね。 JavaScriptで配列をシャッフルする方法 - Qiita https://qiita.com/komaji504/items/62a0f8ea43053e90555a
toshi17922062

2019/02/14 15:58

ちなみにですが、元の二次元配列自体をランダムソートして、シーケンスに読みだす場合は、以下が参考になるかと思います。 JavaScript - JavaScriptで二次元配列の値をランダムに並び替え|teratail https://teratail.com/questions/81199 あと、shinji709さんのソースを良く見るとflagsを並べ替えているようなので、どっちかっていうとこちらの手法でしたね。であればpopでは削除されるので、普通に0から最後まで参照すればよい作りになりますね。 あとは、好みの問題でしょうか。
williamsArk

2019/02/15 01:41

皆さんご協力いただきまして本当にありがとうございました。色々なアプローチがあると学んだだけでも大きな収穫でございます。使いこなせるよう頑張ります。
guest

0

Fisher-​Yates シャッフル

インデックス値は確定情報なので、シャッフルが良いのではないかと。

  1. Array.prototype.keys() + Array.from() or Object.keys()
  2. Fisher-​Yatesアルゴリズムでシャッフル
  3. 先頭から順番にindexを持つ要素を抽出
  • Array.prototype.keys() はイテレータを返すので、配列に変換する必要があります。また、イテレータ故に [1,2,,4] のような欠番要素を読み飛ばしてくれません。
  • Object.keys() は配列の欠番を読み飛ばします。

更新履歴

  • 2019/02/16 11:42 イテレータの配列化処理が抜けていたので、Array.from() と Object.keys() を追記しました

Re: williamsArk さん

投稿2019/02/15 04:23

編集2019/02/16 02:42
think49

総合スコア18162

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

質問者さんのコードを質問に関する範囲で単純化するとこうなりますよね。

JavaScript

1var flags = ['A国', 'B国', 'C国']; 2function setQuestion() { 3 console.log(flags[Math.floor(Math.random() * flags.length)]); 4} 5// 実行例: 6setQuestion(); //=> A国 7setQuestion(); //=> C国 8setQuestion(); //=> B国 9setQuestion(); //=> A国(既に出ている!これをなくしたい)

こうしては?

JavaScript

1var flags = ['A国', 'C国', 'B国'].sort(() => Math.random() - 0.5); 2function setQuestion() { 3 console.log(flags.pop()); 4} 5// 実行例: 6setQuestion(); //=> B国 7setQuestion(); //=> C国 8setQuestion(); //=> A国 9setQuestion(); //=> undefined(二度出なくするのだから出尽くした場合どうするのか別途要検討)

投稿2019/02/14 14:47

shinji709

総合スコア805

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

williamsArk

2019/02/15 01:18

返信が遅くなりましたが、貴重なアドバイスありがとうございました。この - 0.5とは何を意味しているのでしょうか?
shinji709

2019/02/15 04:35

- 0.5は、Math.random()が0以上1未満の数を返すものなので、その区間を-0.5以上0.5未満に変換するために使ってますね。負の値と正の値を半々の確率で得るための処理です。 シャッフルの方法は皆さん言われてるように偏るそうなのでsort使わずに他のアルゴリズム使ったほうがいいかも。
williamsArk

2019/02/15 13:44

ありがとうございました。皆さん色々なアドバイスを与えてくださいましたが、最初にこれを使って重複しないランダムが出来上がったのでBAとさせていただきました。また後ほどそのほかのやり方について質問をさせていただくかもしれません。
guest

0

具体的なコードは何ともですが…こんな感じにしてはいかがかなと。

1.出現ナンバー記録用変数を作成
→var flag;の下あたりにvar previously = {};など?

2.出題部分に出現ナンバーを記録
→setQuestion 内で Math.floor(Math.random() * flags.length) を保存…previously[Math.floor(Math.random() * flags.length)] = true;など?

3.[2]より前の処理で出現ナンバーが既出であるか確認し、出ていればやり直す
→if( previously[Math.floor(Math.random() * flags.length)] == true ){ return; }など?
※[Math.floor(Math.random() * flags.length)の内容は[2][3]で共通にしておく必要があります。

4.初期化部分に出現ナンバー記録用変数の初期化を入れる
→previously = {};など?

難点は、この方法だと不必要に何度もflagsの内容を確認しに行くことですね。
出来れば shinji709 さんのような形で出現対象を減らしていくほうが良いとは思います。(要件によってできないことはありますが…)

投稿2019/02/14 15:28

arcrista_qg

総合スコア70

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

williamsArk

2019/02/15 13:18

詳しい説明ありがとうございました。頂いたアドバイスを元に試行錯誤していきたいと思います。
guest

0

投稿2019/02/14 14:02

bochan2

総合スコア2050

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

williamsArk

2019/02/15 01:41

このリンク先でも確かに同じような処理を相談していますね。あとでじっくり読んでみます。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問