-PR-
解決済み

Rnd関数について

  • 暇なときにでも
  • 質問No.38503
  • 閲覧数429
  • ありがとう数7
  • 気になる数0
  • 回答数4
  • コメント数0

お礼率 85% (66/77)

今Rnd関数を使って問題を作ろうとしているのですが,
40問中から重複なしに20問ランダムに出題するというものを作ろうとしています。
しかし,重複を無くすためのプログラムの仕方がわかりません。
ついこの間,VBを始めたばかり(プログラムはVBが始めて)なので,
できれば詳しい解説を書いてほしいです。
今現在考えているのはこんなのです↓

Private Function RandomCnt() As Long
'*********************************
'* ランダムで問題の番号を取得 *
'*********************************

'一時的にランダムで取得した問題番号を格納
Dim RandomNo As Long

RandomNo = Int((MaxRec * Rnd) + 1) 'ランダムで番号を取得

Do While volQuizNo(RandomNo) = True 'まだ出していない問題が見つかる間
RandomNo = Int((MaxRec * Rnd) + 1) 'ランダムで番号を取得
Loop

volQuizNo(RandomNo) = True '出題問題のチェック
RandomCnt = RandomNo

End Function
通報する
  • 回答数4
  • 気になる
    質問をブックマークします。
    マイページでまとめて確認できます。

質問者が選んだベストアンサー

  • 回答No.3
レベル11

ベストアンサー率 46% (145/312)

先ほどの方法でコードを書いてみました。

1.配列を作ってそこにシリアル番号(順番)を入れます。この配列サイズは問題数です。(TanakaShinyaさんの例にもあります)
2.次にこのレコードの中身をランダムに並べ替えます。簡単な方法として、インデックス(添字)の数値と「インデックス+1」を入れ替えることを何回か行うことによって並び替えを行います。
3.後はその配列から順番にデータを取得する。

これをコードに直すと、

Option Explicit

Const DATA_MAX As Integer = 100 ' データ数
Const LOOP_MAX As Integer = 10000 ' 並べ替え用ループ数

Private s_nDataRecord(DATA_MAX) As Integer ' レコードインデックス(ランダム値)
Private s_nDataIndex As Integer ' レコードインデックスを取得するインデックス

Public Sub SetData()

Dim nCnt As Integer ' 汎用カウンタ
Dim nRnd As Integer ' ランダムな数値用
Dim nTemp As Integer ' 並べ替え時の退避用

' シリアル番号を設定
For nCnt = 1 To DATA_MAX
s_nDataRecord(nCnt) = nCnt
Next nCnt

' ランダムに並べ替え
Randomize
For nCnt = 1 To LOOP_MAX
nRnd = Int(DATA_MAX * Rnd)
nTemp = s_nDataRecord(nRnd)
s_nDataRecord(nRnd) = s_nDataRecord(nRnd + 1)
s_nDataRecord(nRnd + 1) = nTemp
Next nCnt

' 取得するインデックスの初期化
s_nDataIndex = 1

End Sub

Public Function GetData() As Integer

Dim nReturn As Integer ' 戻り値

' すべてのデータを取得し終わっていたらもう一度並べ替え
If s_nDataIndex > DATA_MAX Then
Call SetData
End If

' データ取得
nReturn = s_nDataRecord(s_nDataIndex)

' インデックスを次へ
s_nDataIndex = s_nDataIndex + 1

' 戻り値
GetData = nReturn

End Function

という感じになります。
わかりやすくするために、インデックスデータをモジュール変数にしています。(また、このサイトの制限でしょうが、インデントが全て無くなっています。(^_^;)
これを標準モジュールに組み込み、最初にCall SetDataして、あとは、GetDataを呼び出すとレコード番号が返されます。
注意としては、SetDataを呼び出さずにGetDataを呼んではいけないということです。

なお、ここでは問題数を Integerの範囲内としていますが、適当に Long等に変更してください。変更する場合は、すべての Dim宣言を変更しましょう。

このままのコードを使わずに、理解して自分のものにして使用しましょうね。
ここに書かれてあるキーワードはヘルプで検索できると思うので、詳しく見てみましょうね。

思ったより簡単でしょ?
お礼コメント
nox_neo

お礼率 85% (66/77)

どうも,お返事ありがとうございます。
最初見たときLOOP_MAX=10000ってなんだ?と思ったんですが,
読んでから,わかりました。どうやら,TanakaShinyaさんとは
少し違うようですね。今教えてもらえたのは,数値を選んでそれを前後
並べ替えという方法ですよね。確かに,これだと絶対に重複はなくなります。
こんなアルゴリズム自分ではまったく思いつきません。
ところで,最近プログラムが楽しくて仕方がありません。
もう一日中でもプログラムのことが頭から離れません。
こんな風なアルゴリズムのことを考えるだけで楽しくて仕方ありません。
そんな私ってちょっとおかしい?
なにはともあれ,ご教授ありがとうございました。
また,何か会ったらお願いします。
投稿日時 - 2001-02-12 09:57:51
関連するQ&A
-PR-
-PR-

その他の回答 (全3件)

  • 回答No.1
レベル11

ベストアンサー率 46% (145/312)

シリアル番号の配列を作って、それをランダムに並べ替えして、順番にその番号を取得するのが確実かつ簡単だと思います。 ...続きを読む
シリアル番号の配列を作って、それをランダムに並べ替えして、順番にその番号を取得するのが確実かつ簡単だと思います。
お礼コメント
nox_neo

お礼率 85% (66/77)

お返事ありがとうございます。
この方法は「TanakaShinya」さんと同じ方法のようですね。
自分から,ランダムに番号を作るんじゃなくて並べ替えるという方法が
とてもわかりやすいです。
一日かけて重複なしのランダム関数を作ろうとしていたのですが,
ついにわからずじまいでした。もっと勉強しなくちゃ。
こういう風な直接な回答じゃなくてもどんな風に考えるかという回答でも
とても助かります。
投稿日時 - 2001-02-10 00:46:59


  • 回答No.2
レベル7

ベストアンサー率 63% (7/11)

私のよく使うアルゴリズムは次のようなものです。 まず、問題番号のIndexを入れたテーブルを作成します。 Const c_MaxQuizNum = 40 ' 全問題数 Const c_QuizNum = 20 ' 出題数 Dim QuizIndex(c_MaxQuizNum) As Integer ' 問題の Index 格納用 Dim QuizLast ...続きを読む
私のよく使うアルゴリズムは次のようなものです。

まず、問題番号のIndexを入れたテーブルを作成します。

Const c_MaxQuizNum = 40 ' 全問題数
Const c_QuizNum = 20 ' 出題数

Dim QuizIndex(c_MaxQuizNum) As Integer ' 問題の Index 格納用
Dim QuizLastNum as Integer ' 問題の残り数
Dim TmpRndm As Integer ' 選択された値
Dim RandomNo As Integer ' 選択された問題番号
Dim i As Integer

For i = 1 To c_MaxQuizNum
 QuizIndex(i) = i '初期値を格納
Next

これを利用し、重複しない問題番号を取り出すことが可能です。

QuizLastNum = c_MaxQuizNum ' 問題の残り数初期化

For i = 1 To c_QuizNum
 TmpRndm = Int((QuizLastNum * Rnd) + 1) ' 1~QuizLastNum の乱数
 RandomNo = QuizIndex(TmpRndm) ' 問題番号を取得
 QuizIndex(TmpRndm) = QuizIndex(QuizLastNum) ' 問題の選択肢をつめる
 QuizLastNum = QuizLastNum - 1 ' 問題の残り数を減らす
 ' 以降、ユーザ処理
Next

つまり、簡単な例として c_MaxQuizNum = 5 としたとき、
QuizIndex には、 1 , 2 , 3 , 4 , 5 が順に入っています。
QuizLastNum には c_MaxQuizNum = 5 が入っていますね。

ここで、出題用の For ループ処理を始めると
TmpRndm は 1~5 のいずれかの整数が入ります。
よって、RandomNo には 1 , 2 , 3 , 4 , 5 のいずれかが選ばれるわけです。
そして、ここからがミソなのですが、今仮に TmpRndm に 3 が選ばれるとします。
QuizIndex(TmpRndm) = QuizIndex(QuizLastNum)
の処理により、QuizIndex には、 1 , 2 , 5 , 4 , 5 が入るわけです。
そして、QuizLastNum の値を 1 減らします。
すると 2 回目の For ループは どうなるでしょうか?
TmpRndm は QuizLastNum を減らしたため 1~4 のいずれかの整数が入ります。
よって、RandomNo には 1 , 2 , 5 , 4 のいずれかが選ばれ、
先ほど出題した 3 は選択されません。

重複を無くし、かつ効率の良い方法でしょう?
このアルゴリズムおわかりいただけましたでしょうか?
補足コメント
nox_neo

お礼率 85% (66/77)

プログラムの意味はよくわかりました。
でも,「以降ユーザー処理」をどのようにしたらいいのかわかりません。
それと,RandomNoをどのように使ったらいいのかもわかりません。
まだ,配列もわからない状態で,やれ「インデックス~」とか「定数式がひつようです」とかエラーが大量に出てくる始末です。
何とか,自分で工夫してRandomNoのところをQuizrecord(20)とかいう配列にして
QuizIndex(TmpRndm) から来るランダムな数値を格納して20までループして
Quizrecord(20)を問題の順序に使用かとしたのですが,同じ乱数が同じ順序で出てきます。(8,9,7,5,2とか,これが何度も出る。)そのあたりもちょっとわかりにくい説明ですが,アドバイスいただけないでしょうか。お願いします。
投稿日時 - 2001-02-10 00:25:26
お礼コメント
nox_neo

お礼率 85% (66/77)

どうもありがとうございました。
いろいろなエラーもKojiSさんのアルゴリズムと見比べてみて
両方のアルゴリズムともにうまくいくようになりました。
話は違うんですが,なんかアルゴリズムって響きがいいですよね?
TanakaShinyaさんに聞いてなんか気に入ってよく使っています。
投稿日時 - 2001-02-12 10:02:59
  • 回答No.4
レベル11

ベストアンサー率 46% (145/312)

ちょっと一部間違っていました。(^_^; 並べ替えの時に配列のインデックスを1からにしているのに0が混じっちゃ駄目ですよね。 誤: nRnd = Int(DATA_MAX * Rnd) 正: nRnd = Int((DATA_MAX - 1) * Rnd) + 1 ...続きを読む
ちょっと一部間違っていました。(^_^;

並べ替えの時に配列のインデックスを1からにしているのに0が混じっちゃ駄目ですよね。

誤:
nRnd = Int(DATA_MAX * Rnd)

正:
nRnd = Int((DATA_MAX - 1) * Rnd) + 1
お礼コメント
nox_neo

お礼率 85% (66/77)

おっと,訂正ですか。
実はこの間違いはわかりました。
実は数値をひとつずつに入れていって,計算してみてこのようにすれば
うまく0を出さなくなるということがわかりました。
ところでコンピューターって人の頭脳の何倍の処理能力があるんでしょうか?
左脳の働きで比較してみると?この数値代入法?で確認しているときに
そんなことを考えながら自分の脳って遅いなーと思いました。
投稿日時 - 2001-02-12 10:08:28
このQ&Aで解決しましたか?
関連するQ&A
-PR-
-PR-
こんな書き方もあるよ!この情報は知ってる?あなたの知識を教えて!
このQ&Aにはまだコメントがありません。
あなたの思ったこと、知っていることをここにコメントしてみましょう。

その他の関連するQ&A、テーマをキーワードで探す

キーワードでQ&A、テーマを検索する
-PR-
-PR-
-PR-

特集


新大学生・新社会人のパソコンの悩みを解決!

いま みんなが気になるQ&A

関連するQ&A

-PR-

ピックアップ

-PR-
ページ先頭へ