目標
For~Nextのネストを利用して複雑な処理を記述できる
ネストした繰り返し文の動きをイメージできる
Rnd関数とFor文のネストを利用して重複のないランダムな数列を生成できる
乱数の概要
乱数と疑似乱数
規則性のない予測不能な数値のことを「乱数」言います。コンピュータによって生成される乱数は実は一定のルールで生成されます。これを「疑似乱数」といいます。
規則性のない予測不能な数値を生成するのは何やら難しそうですが、プログラミング言語には、その為の関数が準備されています。VBAにもRnd関数という関数があり、これを利用してランダムな数値(疑似乱数)を生成することができます。
Rnd関数とRandomizeステートメントの利用方法
Rnd関数の文法
Rnd関数は1つの引数を取ることが出来ます。
Rnd(引数)
Rnd(-1)の場合
マイナスの値を引数に設定すると、この引数がシード値として利用され、毎回同じ値を生成することになります。
Rnd(0)の場合
数値の「0」を引数とするとその直前に生成したシード値を再利用することになります。数値の「0」はマイナスの値と違って引数で使用してもシード値として利用されることはありません。
Rnd(4)やRan()の場合
引数に「正の値」か「値を省略」した場合は疑似乱数はシーケンスの次の番号を戻り値として返します。※戻り値:ここはシード値のことではありません。
シード値って何?
ランダムな数値を生成するときは専用のアルゴリズムを利用しています。それらのアルゴリズムは「任意の値」を基にして数値を生成するように組まれています。この任意の値を「シード値」と言います。
例えばシード値を「-1」にすると「-1」を基にした数の生成を行います。シード値が同じ場合は毎回同じ値が生成されます。毎回同じ値となると規則性がない数値と言いづらいですが、このシード値を時間などで設定したりすることで生成される数値をランダムに見せかけることが出来ます。
Rnd関数の戻り値
Rnd関数はいずれの引数を使用したとしても「0 以上 1 未満の値」を返します。
サンプルプロシージャ1の実行結果(下に画面キャプチャあり)がRnd関数の戻り値です。
Randomizeステートメント
Randomizeステートメントの構文
Randomize[数値]
Rnd関数の直前に利用されて新しいシード値の初期化(設定)を行ってくれます。Randomize[数値]の[数値]部分を省略するとシード値にはシステムクロック(パソコンの時刻)が利用されます。
Rnd(-1)の場合
Rnd関数の引数がマイナスの場合はRnd関数の引数がシード値として利用されます。
Rnd(0)の場合
Rnd関数の引数が「0」の場合はシード値が更新されます。
Rnd(4)やRan()の場合
Rnd関数の引数が「正の値」か「値を省略」した場合はシード値が更新されます。
サンプルプロシージャ1
Sub rndfunc()
Dim i As Single, j As Single, k As Single, l As Single, m As Single
i = Rnd(3)
j = Rnd(1)
’Randomizeステートメントを利用しても変化なし
'Rnd関数の引数がマイナスの場合はそちらがシード値として利用されます。
k = Rnd(-1)
’Randomizeステートメントを利用しても変化なし
'Rnd関数の引数がマイナスの場合はそちらがシード値として利用されます。
l = Rnd(-4)
Randomize 'シード値にシステムクロックを利用します。
m = Rnd()
MsgBox i & Chr(10) & _
j & Chr(10) & _
k & Chr(10) & _
l & Chr(10) & _
m
End Sub
実行結果
Rnd関数を利用して重複のない数列を生成する
重複がない乱数の生成
重複のない数列を作成するには配列を利用して値を格納していきます。格納する時に既に存在する値があれば再度、乱数の生成を行って重複がなければ配列に代入を行っていきます。
サンプルプロシージャ2
Sub RandFunc()
Dim i As Integer, j As Integer
Dim StrNumBackUp As String
Dim StrNum(5) As String
For i = 1 To 5
Randomize
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'【注意】
'Rnd関数はいずれの引数を使用したとしても「0 以上 1 未満の値」を返します。
'Rnd関数の戻り値に今回は「9」を掛けています『Rnd*9の部分』これによって
'「0 以上 1 未満の値」が「0 以上 9未満の値」となります。
'その数を四捨五入しているので「0 以上 9未満の値」が「0 以上 9以下の値」となります。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
StrNumBackUp = Round(Rnd * 9, 0)
For j = 1 To 5
If StrNum(j) <> "" Then
If StrNumBackUp <> StrNum(j) Then
Else
i = j
Exit For
End If
Else
StrNum(j) = StrNumBackUp
Exit For
End If
Next j
Next i
Debug.Print StrNum(1)
Debug.Print StrNum(2)
Debug.Print StrNum(3)
Debug.Print StrNum(4)
Debug.Print StrNum(5)
MsgBox "イミディエイトウィンドウのクリア"
Debug.Print String(200, vbCrLf)
End Sub
実行結果
重複ない値を生成することが出来ました。
重複のない乱数を応用したプログラムの作成
次は、重複のない乱数を動的配列を利用して格納し、いくつかのグループにメンバーを均等に分割するプログラムを作成します。これは例えば、学校の出席番号を基にして、3つのチームにランダムに分けるなどチーム分けをするときに使えるようなプログラムとなります。
Sub getIntoGroups()
Dim i As Integer, j As Integer, ListNum As Integer, GroupNum As Integer, BackUpLN As Integer
ListNum = InputBox("参加人数を入力")
GroupNum = InputBox("グループの数を入力")
BackUpLN = ListNum
Dim StrNumBackUp As String
Dim StrNum() As String
ReDim StrNum(ListNum)
Dim GroupMember As Integer
GroupMember = WorksheetFunction.RoundDown(ListNum / GroupNum, 0)
For i = 1 To ListNum
Randomize
StrNumBackUp = WorksheetFunction.RoundUp(Rnd * ListNum, 0)
For j = 1 To ListNum
If StrNum(j) <> "" Then
If StrNumBackUp <> StrNum(j) Then
Else
i = j
Exit For
End If
Else
StrNum(j) = StrNumBackUp
Exit For
End If
Next j
Next i
Range("A1").Select
ActiveCell.CurrentRegion.Select
Selection.ClearContents
For i = 1 To GroupNum
For j = 1 To GroupMember
If i = 1 Then
ListNum = j
Else
ListNum = j + (GroupMember * (i - 1))
End If
On Error Resume Next
Debug.Print ListNum
Cells(i, j) = StrNum(ListNum)
Next j
Next i
Range("A1").Select
Selection.End(xlToRight).Select
ActiveCell.Offset(, 1).Select
For i = 1 To GroupNum
ListNum = ListNum + 1
Debug.Print ListNum
On Error Resume Next
ActiveCell.Value = StrNum(ListNum)
ActiveCell.Offset(1).Select
Next i
End Sub
実行結果
プロシージャの解説
Sub getIntoGroups()
Dim i As Integer, j As Integer, ListNum As Integer, GroupNum As Integer, BackUpLN As Integer
ListNum = InputBox("参加人数を入力")
GroupNum = InputBox("グループの数を入力")
BackUpLN = ListNum
Dim StrNumBackUp As String
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'動的配列(後から箱の個数を決めることが出来る配列)を作成する時は
'先ず、配列の変数宣言時に変数名の引数部分を空にしておきます。
'その後、ReDimステートメントという文を利用して作りたいだけの個数をあてます。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim StrNum() As String
ReDim StrNum(ListNum)
Dim GroupMember As Integer
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'マークシート関数のRoundDown関数を呼び出すプログラムを記述しています。
'ワークシート関数を呼び出すにはWorksheetFunctionオブジェクトを利用して
'続けてワークシートの関数を記述します。
'関数の利用方法はエクセルのRoundDown関数と同じです。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
GroupMember = WorksheetFunction.RoundDown(ListNum / GroupNum, 0)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'ひとつ目のFor文のネストで重複のない乱数列の生成をしています。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
For i = 1 To ListNum
Randomize
StrNumBackUp = WorksheetFunction.RoundUp(Rnd * ListNum, 0)
For j = 1 To ListNum
If StrNum(j) <> "" Then
If StrNumBackUp <> StrNum(j) Then
Else
i = j
Exit For
End If
Else
StrNum(j) = StrNumBackUp
Exit For
End If
Next j
Next i
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'シートに既にランダムなチーム分けの値が入っていた場合にそれらをクリア(消去)します。
'表の中身の消去には表全体を選択させて、それらのセレクションに対してClearContentsメソッドをあてます。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Range("A1").Select
ActiveCell.CurrentRegion.Select
Selection.ClearContents
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'分割するチームは割り算の「商」で行います。
'これはGroupMemberとしてプロシージャの最初の方で作成しています。
'分割時にRoundDownを利用しているのは余った人数については最初のチームから
'Offset(1,0)を利用して配分する方がプログラムがシンプルになるからです。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
For i = 1 To GroupNum
For j = 1 To GroupMember
If i = 1 Then
ListNum = j
Else
ListNum = j + (GroupMember * (i - 1))
End If
On Error Resume Next
Debug.Print ListNum
Cells(i, j) = StrNum(ListNum)
Next j
Next i
Range("A1").Select
Selection.End(xlToRight).Select
ActiveCell.Offset(, 1).Select
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'余った人数を最初のチームからひとつずつ分配していきます。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
For i = 1 To GroupNum
ListNum = ListNum + 1
Debug.Print ListNum
On Error Resume Next
ActiveCell.Value = StrNum(ListNum)
ActiveCell.Offset(1).Select
Next i
End Sub
今回は以上となります。
ブックマークのすすめ
「ほわほわぶろぐ」を常に検索するのが面倒だという方はブックマークをお勧めします。ブックマークの設定は別記事にて掲載しています。