- ベストアンサー
重複しない乱数発生
初めて投稿させて頂きます。 サイコロを振って、一度出た目はもう出ないような ゲームをプログラミングしています。 Dim i As Integer Dim j As Integer Dim t As Integer Dim r(6) As Integer 'コンピュータ Dim b As Integer 'プレイヤー Private Sub Command1_Click() b = Val(Text2.Text) Randomize r(6) = Int(Rnd * 6 + 1) '1~6までの乱数発生 Text1.Text = r(6) For i = 1 To n r(i) = i 'r(i)~r(n)に1~nの値を格納 Next i For i = n To 2 Step -1 j = Int((i - 1) * Rnd() + 1) '1~i-1の範囲の乱数 t = r(i): r(i) = r(j): r(j) = t 'r(i)とr(j)の交換 Next i For i = 1 To n Text1.Text = r(i) Next i If r(i) < j Then Label2.Caption = "あなたの勝ちです" Else Label2.Caption = "あなたの負けです" End If 幾つか考えてこれで落ち着いたのですが、これでは まだ重複してしまいます。 どこが問題なのかご指摘頂けるようお願いします。 一度出た目は出ないようにするので、全部で6回試行 することになります。またその6回分の結果を表示したいのですが、 Text3.Text = r(1) Text4.Text = r(2) Text5.Text = r(3) Text6.Text = r(4) Text7.Text = r(5) Text8.Text = r(6) としてしまうと全てに0が表示され、結果が表示されません。 これについても回答をお願いします。 まだ初心者ですが、よろしくお願いします。
- みんなの回答 (10)
- 専門家の回答
質問者が選んだベストアンサー
#6,8,9です。 さっきのは、全ての乱数が出るまで探し続けるほうほうでしたが、違う方法もあります。 さっきの方法ですと、乱数の範囲が広いと最後の1個が出る確率が減り、処理時間が多くなります。数百程度の組み合わせなら気になりませんが・・・ 下は、違う方法の参考例です。 先にformloadで、解を配列に格納します。何もしないうちは、配列の格納番号がそのまま解になります。 そして、乱数で出た配列は0にします。 乱数を出した後で、0を探して、見つかればそれ以降の配列データを1個づつずらします。 次に乱数の範囲を1つ減らしていきます。 この方法ですと、探したい回数分しか乱数は実行されません。 まぁ乱数の幅がどの程度になったときに、速さが逆転するのか、あるいは逆転しないかは検証した事がないので判りませんが・・・ Private ANS(10) As Integer Private RndWhdth As Integer Private Sub Form_Load() Dim i As Integer RndWhdth = 10 For i = 1 To 10 ANS(i) = i Next End Sub Private Sub Command1_Click() Dim A As Integer Dim i1 As Integer Dim i2 As Integer Dim DmyANS As Integer If RndWhdth = 0 Then MsgBox ("END") RndWhdth = 10 For i = 1 To 10 ANS(i) = i Next Exit Sub End If A = Int(Rnd * RndWhdth) + 1 Text1.Text = ANS(A) ANS(A) = 0 For i1 = 1 To RndWhdth If ANS(i1) = 0 Then For i2 = i1 To RndWhdth - 1 ANS(i2) = ANS(i2 + 1) Next RndWhdth = RndWhdth - 1 Exit Sub End If Next RndWhdth = RndWhdth - 1 End Sub
その他の回答 (9)
- miran_2006
- ベストアンサー率25% (29/116)
#6,8です。 あまり綺麗に書いてなかったので、ちょっと書き直しました。 5列×50行です。全部出るまで探します。 それとクリアボタンを追加してあります。 Private BINGO(5, 50) As Integer Private Counter As Integer Private Sub Command1_Click() Dim A As Integer Dim B As Integer Dim C As String Dim i As Integer BINGO(0, 0) = 1 Counter = Counter + 1 If Counter > 250 Then MsgBox ("END") Exit Sub End If Do While BINGO(A, B) = 1 A = Int(Rnd * 5) + 1 B = Int(Rnd * 50) + 1 Loop Select Case A Case 1 C = "B-" Case 2 C = "I-" Case 3 C = "N-" Case 4 C = "G-" Case 5 C = "O-" End Select Text1.Text = C & B BINGO(A, B) = 1 End Sub Private Sub Command2_Click() Dim A As Integer Dim B As Integer Counter = 0 For A = 1 To 5 For B = 1 To 50 BINGO(A, B) = 0 Next Next End Sub
お礼
結果を表示するのは、リストボックスを用いることでなんとか自分で解決できました。 本当にわざわざプログラム組んで頂きありがとうございます! とてもためになりました!
- miran_2006
- ベストアンサー率25% (29/116)
>BINGO(5, 10)なので、5行10列のビンゴカードですよね? そうです。ですので、例えば5行×50列にしたければ、 Private BINGO(5, 50) As Integer If i > 5000 Then と、します。5000と言う数字が良いかどうかは判りません。 >BINGO(0, 0) = 1とBINGO(A, B) = 1は初期値に戻しているのでしょうか これの考え方を先に説明します。 BINGO(XX,YY)の配列はフラグとして使用します。 配列はXX,YYの部分がアドレスに相当します。ですので、乱数で得られたXXとYYの組み合わせが過去に出たか出なかったかを、1と0で見分けます。 次に BINGO(0, 0) = 1 と、している部分ですが、プログラム起動時には配列を含む全ての変数には0が入っていますので、 Do While BINGO(A, B) = 1 と、している部分でAとBは乱数を出す前ですので夫々0になっています。 つまり、 Do While BINGO(0, 0) = 1 となるわけです。 Do While は、While 後の条件式を満たしていなければLoop以降の部分に抜けてしまいますので、先に BINGO(0, 0) = 1 として、BINGO(0, 0) = 0の初期値をBINGO(0, 0) = 1にします。 これで、乱数部分のプログラムが実行されます。 後は、乱数で出たBINGO(A,B)が1(過去に出ていれば)ならばLOOPして、乱数を取り直します。 逆に、乱数で出たBINGO(A,B)が0(初めて出たのならば)ならばLOOPを抜けて表示して、BINGO(A,B)を1にします。 あと、 If i > 50 Then MsgBox ("END") Exit Sub End If の部分ですが、ここは無限ループを避ける為に入れてあります。 もし、配列の全ての組み合わせが1であれば、永遠に0を探し続けてしまうからです。 ですので、上記の場合なら50回やってBINGO(A,B)=0が見つからなければ、あきらめて終わりにすると言う意味です。 ですので、 If i > 50 Then の50と言う数字が適切かどうかは判りません。 なぜなら、50個の組み合わせのうち49個の組み合わせが1の場合、残りの1個の組み合わせが50回の乱数実行で出るとは限らないからです。 その辺りは、確率論の話になりそうなので、教育-数学でスレを立ててみてください。
- Werner
- ベストアンサー率53% (395/735)
#2です。 > 参考URLに行ってみたんですが、シャッフルする際は参考URLのところにも書いてあったようにArray関数を使わないといけないですか? ん?参考URLのarrayはシャッフル対象の配列ですよ。(関数じゃないです。) >> 1. 1番目の要素と、1~6番目(乱数)の要素を入れ替え。 > 最初の1番目の要素は乱数ではないんですよね?ということは、最初に配列r(6)として、この6つの配列に何か数字を入れないといけないということでしょうか? どの要素も乱数ではないです。(乱数をシャッフルしたいなら乱数で良いけど、乱数ならもうシャッフル必要ない…。) シャッフルするのですから要素はシャッフル前に準備しておきます。 今回の場合は1~6の数値を入れておけばよいでしょう。 乱数は何番目の要素と入れ替えるかを決定するために使います。 > 図付きで説明してあったのでなんとなく何が起こっているのかは分かるんですが、 とりあえず具体的にコードのどこで何をしているのかが分かるようにしましょう。 (コードを目でたどってみたり、実際に実行させてみるなどして) > この場合1~6までの乱数を発生させているので、このnは6なのかなって思っています。 その本ではどこかでnに6を代入していたのかもしれませんが、 少なくともこの質問で提示されたコード内にはnに何かを代入しているところはないです。 どこかでちゃんとnに6を代入していますか?
お礼
>どこかでちゃんとnに6を代入していますか? 言われてみればそうです…このプログラムではどこにもnは入れてないですね…。 シャッフルについてですが、Wernerさんの解説だと私の組んだプログラムのr(6) = Int(Rnd * 6 + 1)はおかしいですね。シャッフルをするとしたら、r(6)には要素を入れて、何番目かということ(私のではiとjにあたると思いますが)を乱数にすべきですね。 すごくわかりやすい説明でした。 ありがとうございます。 意見を参考にして少し自分で組んでみます。
- miran_2006
- ベストアンサー率25% (29/116)
参考でBINGOの抽選サンプルを書いてみました。 Private BINGO(5, 10) As Integer Private Sub Command1_Click() Dim A As Integer Dim B As Integer Dim C As String Dim i As Integer BINGO(0, 0) = 1 Do While BINGO(A, B) = 1 i = i + 1 A = Int(Rnd * 5) + 1 B = Int(Rnd * 10) + 1 Select Case A Case 1 C = "B-" Case 2 C = "I-" Case 3 C = "N-" Case 4 C = "G-" Case 5 C = "O-" End Select If i > 50 Then MsgBox ("END") Exit Sub End If Loop Text1.Text = C & B BINGO(A, B) = 1 End Sub
お礼
わざわざありがとうございます。 BINGO(5, 10)なので、5行10列のビンゴカードですよね? BINGO(0, 0) = 1とBINGO(A, B) = 1は初期値に戻しているのでしょうか…本当に初心者でごめんなさい。
- don_go
- ベストアンサー率31% (336/1059)
n の値及び範囲は何ですか?
お礼
実は、このFor文のあたりは本に書いてあったのを持ってきたので、私自体このnがなんなのかすらわかっていないんです…。 図付きで説明してあったのでなんとなく何が起こっているのかは分かるんですが、この場合1~6までの乱数を発生させているので、このnは6なのかなって思っています。
- don_go
- ベストアンサー率31% (336/1059)
>r(6) = Int(Rnd * 6 + 1) '1~6までの乱数発生 1~6までの乱数を発生させるなら... r(6) = Int(Rnd * 6) + 1 ↑の様にするのが定石です。 ところで... >サイコロを振って、一度出た目はもう出ないような >ゲーム との事ですが、上記の条件だと6回目には、何が出るか プレイヤーに判ってしまいますが、それでゲームとして 成立するのでしょうか?
お礼
>6回目には何が出るかプレイヤーに判ってしまいます 確かにそうです。 ゲームと呼ぶべきものではないのですが…目的は、最後には一つしか出る目が残らないなのです。 私個人で作っているのではなく、VBの授業の課題なので…申し訳ないです。
- STICKY2006
- ベストアンサー率29% (1536/5269)
こんちくわ~。 んー。。どこからツッコミましょうかねぇ。。。?(汗 とりあえず、デバッグ。。。って知ってますか? まず >>としてしまうと全てに0が表示され、結果が表示されません。 当然です。。。あなたの作ったfor文は全て1度も通りませんので。。。 for文が3つあると思いますが、3つとも、一度も通りません。判定文で弾かれます。 もし、弾かれずにfor文の中を通ってもー。。 途中で用意した配列数よりも数が大きくなってあふれエラーになっちゃいます。。。 とりあえず、「全てにおいて0が表示され結果が表示されない」 のは配列の中に数字が入っていないから。という答えになります。 他の部分に関しては、そこが直った段階で。。。ですかね。 とりあえず、直して~からって事で補足要求~です。
お礼
的確なご意見ありがとうございます。 >配列の中に数字が入っていないから というのはランダムで数字を発生することで数字を各配列に入れているということにはならないですか? この文だと全ての配列に数字を入れないと通らないということなのでしょうか?
- Werner
- ベストアンサー率53% (395/735)
「シャッフル」すればよいと言うのはその通りだと思いますが、 1000回も繰り返す必要はないと思います。 要素数6なら以下の手順で5回のループで良さそうです。 1. 1番目の要素と、1~6番目(乱数)の要素を入れ替え。 2. 2番目の要素と、2~6番目(乱数)の要素を入れ替え。 (中略) 5. 5番目の要素と、5~6番目(乱数)の要素を入れ替え。
お礼
参考URLに行ってみたんですが、シャッフルする際は参考URLのところにも書いてあったようにArray関数を使わないといけないですか? >1. 1番目の要素と、1~6番目(乱数)の要素を入れ替え。 最初の1番目の要素は乱数ではないんですよね?ということは、最初に配列r(6)として、この6つの配列に何か数字を入れないといけないということでしょうか?
1~6をランダムに並べ替えた順列を得たいということですよね? 一番手っ取り早いのは「シャッフル」することです。 1~6が格納された配列を用意して、1~6の乱数を2個発生させ、それぞれの中身を入れ替える、これを1000回くらい繰り返せば、1~6をランダムに並べ替えた順列が得られますよ。
お礼
早く回答して頂いたのにも関わらず、お礼が遅くなって申し訳ございません。 >1~6の乱数を2個発生させ、それぞれの中身を入れ替える ソートのような感じでしょうか? それぞれの中身を入れ替えて、一度出たものは捨てる といった感じでしょうか?
お礼
わざわざプログラムを組んで頂き、大変感謝しております。 すごくわかりやすかったので、重複しない乱数を発生することが出来ました。ありがとうございます。 あと結果を表示したいのですが、色々やってみているのですがなかなか出来ません。 ボタンをクリックするごとに、所定のテキストに結果を表示していきたいのですが、このプログラムだとANS(A) = 0となっているので結果表示の際ANS(A)は使えないのでしょうか?