単語テストなどをするときに、単語の順番を変えて出すような場合です。
問題は10問ですが、あらかじめ問題はあるので順番を変えればいいので
シャッフルなのかもしれません。
インターネットではいろいろな質問や回答がたくさんあったのですが、
分かりづらいこともあったので自分なりに作ってみました。
一応動くのですが、これでいいのでしょうか?
より良い方法があればお教えください。
とりあえず、作ったコードを下に書いておきます。
<コード>
Sub ransuu()
Dim ransuu(10) As Integer
Dim i As Integer
Dim j As Integer
Dim k As Integer
For i = 1 To 10
ransuu(i) = Int(Rnd() * 10) + 1
For j = 1 To i - 1
If ransuu(i) = ransuu(j) Then Exit For
Next j
If j < i Then i = i - 1
Next i
For k = 1 To 10
Cells(k, 1).Value = ransuu(k)
Next
End Sub
乱数を作っては「既出かどうか」をチェックされているわけですが、これだと効率は良いとはいえないでしょう。まぁ10個程度ならあまり気にする話ではないですが。
どうせ「1~10が一回ずつ」と決まってるわけですから、配列ransuu(#)には単純に1~10をそのままセットして、そのあとシャッフルする方が効率的です。
for i=1 to 10 ' 配列に1~10を格納
ransuu(i)=i
next
for i=1 to 10 ' 全要素をシャッフル
j=Int(Rnd() * 10) + 1
c=ransuu(i)
ransuu(i)=ransuu(j)
ransuu(j)=c
next
シャッフルするときも、単純に配列すべてを一つずつ、ランダムな位置のものと入れ替えるようにすれば、確実に漏れなくシャッフルできます。
1番目から順に乱数で取得した値をセットしているんですね。
それで 既にある場合は 飛ばして 次の値をセットしているんですね。
これだと どれだけランダムになるのかが 難しいところです。
こうやって 1から10までセットして次は その配列を シャッフルしたらいいでしょう。
つまり 乱数で 取得した 二つの値の 配列を 入れ替えるのです。
乱数で 3と5を取得した場合、3と5に入っている値を 交換します。
これを 大量回数(これも乱数で取得)やれば けっこう ランダムになるかなと思います。
すごく速い回答ありがとうございます。
やはり、シャッフルすることが必要なのですね。
大量回数というのは10個の数値に対しては100回くらいとかの感じでよいのでしょうか。
シャッフルのやりかたもなんとなく大体分かりました。
試してみます。
乱数を作っては「既出かどうか」をチェックされているわけですが、これだと効率は良いとはいえないでしょう。まぁ10個程度ならあまり気にする話ではないですが。
どうせ「1~10が一回ずつ」と決まってるわけですから、配列ransuu(#)には単純に1~10をそのままセットして、そのあとシャッフルする方が効率的です。
for i=1 to 10 ' 配列に1~10を格納
ransuu(i)=i
next
for i=1 to 10 ' 全要素をシャッフル
j=Int(Rnd() * 10) + 1
c=ransuu(i)
ransuu(i)=ransuu(j)
ransuu(j)=c
next
シャッフルするときも、単純に配列すべてを一つずつ、ランダムな位置のものと入れ替えるようにすれば、確実に漏れなくシャッフルできます。
ご回答ありがとうございます。
シャッフルの具体的なコードも示していただき分かりました。
>配列すべてを一つずつ、ランダムな位置のものと入れ替えるようにす
るということで、もれなくシャッフルできるのですね。
効率が悪いというご指摘ですが、私も、そう思っていました。
また、考え直してみたいと思います。
ありがとうございます。
ご質問のコードで間違いはありません。
ご参考まで、配列 a をシャッフルするサブルーチンを掲げます。
Sub Shuffle(a) Dim k, j As Long Dim t Randomize For k = UBound(a) To 0 Step -1 j = Fix(Rnd * (k + 1)) t = a(j) a(j) = a(k) a(k) = t Next End Sub
ご回答ありがとうございます。
すみません、UBound を知りませんでしたので、調べていました。
配列の格納できる最大値を求める関数と分かりました。
最大値から1ずつ減じてループさせて、乱数を作って、入れ替えをする。
その際、発生させる乱数の最大値も1ずつ減らす。
というふうに解釈したのですが、この場合 重複はしないのでしょうか。
jに同じ数値が発生することはないでしょうか。
素人なので申し訳ありません。お手数かけます。
No.3はこれですね。
さらに今回の場合、wikipediaの次の節にある"inside-out"式を使って配列に入れながら混ぜる方法が良いかもしれません。
ransuu(1) = 1 for i=2 to 10 j=Int(Rnd() * i) + 1 ransuu(i)=ransuu(j) ransuu(j)=i next
これは下記と等価です
for i=1 to 10 ransuu(i)=i next for i=2 to 10 j=Int(Rnd() * i) + 1 t=ransuu(i) ' ransuu(i)はまだ交換した事がないのでiと同じ ransuu(i)=ransuu(j) ransuu(j)=t next
これのループを逆に回すとNo.3になり、さらに交換ではなくてセルへの代入にすると下記になります。
for i=1 to 10 ransuu(i)=i next for i=10 to 2 Step -1 j=Int(Rnd() * i) + 1 Cells(i, 1).Value=ransuu(j) ransuu(j)=ransuu(i) ' ransuu(i)は今後アクセスすることがないので消さなくてもOK next Cells(1, 1).Value=ransuu(1)
ご回答ありがとうございます。
シャッフルについてのいくつかの方法についての説明ありがとうございます。
すべての個数についてシャッフルをするのを、昇順降順があるということですね。
私の場合は、最初から問題が決まっているので、シャッフルすればよかったのだと思いました。
コードも示していただきよくわかりました。
ご回答ありがとうございます。
シャッフルの具体的なコードも示していただき分かりました。
>配列すべてを一つずつ、ランダムな位置のものと入れ替えるようにす
るということで、もれなくシャッフルできるのですね。
効率が悪いというご指摘ですが、私も、そう思っていました。
また、考え直してみたいと思います。
ありがとうございます。