01-VBA

制御構文|Excel VBA (Part.7)【For Next】ネスト編(後編)重複のないランダムな数の生成 | 現役エンジニア&プログラミングスクール講師

目標

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

今回は以上となります。

初心者も実践で通用!「VBA・VBS」おすすめ書籍5選 | 現役エンジニア&プログラミングスクール講師「VBA・VBS」初心者の方が実践業務の中でそれらを活用しt活躍できるために必要な知識を習得できる書籍を紹介しています。ページの下部には「おすすめのITスクール情報」「おすすめ求人サイト」について情報を掲載中。...

ブックマークのすすめ

「ほわほわぶろぐ」を常に検索するのが面倒だという方はブックマークをお勧めします。ブックマークの設定は別記事にて掲載しています。

「お気に入り」の登録・削除方法【Google Chrome / Microsoft Edge】| 現役エンジニア&プログラミングスクール講師「お気に入り」の登録・削除方法【Google Chrome / Microsoft Edge】について解説している記事です。削除方法も掲載しています。...
【パソコン選び】失敗しないための重要ポイント | 現役エンジニア&プログラミングスクール講師【パソコン選び】失敗しないための重要ポイントについての記事です。パソコンのタイプと購入時に検討すべき点・家電量販店で見かけるCPUの見方・購入者が必要とするメモリ容量・HDDとSSDについて・ディスプレイの種類・バッテリーの持ち時間や保証・Officeソフト・ウィルス対策ソフトについて書いています。...
RELATED POST
01-VBA

ExcelVBAの変数 (Part.3) バリアント型編 | 現役エンジニア&プログラミングスクール講師

2022年9月16日
プログラミング学習 おすすめ書籍情報発信 パソコン初心者 エンジニア希望者 新人エンジニア IT業界への就職・転職希望者 サポートサイト Programming learning Recommended schools Recommended books Information dissemination Computer beginners Prospective engineers New engineers Prospective job seekers in the IT industry Support site
01-VBA

初めてのExcelVBA:開発タブの表示 | 現役エンジニア&プログラミングスクール講師

2022年8月5日
プログラミング学習 おすすめ書籍情報発信 パソコン初心者 エンジニア希望者 新人エンジニア IT業界への就職・転職希望者 サポートサイト Programming learning Recommended schools Recommended books Information dissemination Computer beginners Prospective engineers New engineers Prospective job seekers in the IT industry Support site
01-VBA

演算子|Excel VBA (Part.5)【論理演算子】| 現役エンジニア&プログラミングスクール講師

2022年10月29日
プログラミング学習 おすすめ書籍情報発信 パソコン初心者 エンジニア希望者 新人エンジニア IT業界への就職・転職希望者 サポートサイト Programming learning Recommended schools Recommended books Information dissemination Computer beginners Prospective engineers New engineers Prospective job seekers in the IT industry Support site
01-VBA

Excel VBA の 配列(動的配列)| 現役エンジニア&プログラミングスクール講師

2022年12月24日
プログラミング学習 おすすめ書籍情報発信 パソコン初心者 エンジニア希望者 新人エンジニア IT業界への就職・転職希望者 サポートサイト Programming learning Recommended schools Recommended books Information dissemination Computer beginners Prospective engineers New engineers Prospective job seekers in the IT industry Support site
01-VBA

制御構文|Excel VBA (Part.9)【Do Loop】基本編 | 現役エンジニア&プログラミングスクール講師

2022年11月30日
プログラミング学習 おすすめ書籍情報発信 パソコン初心者 エンジニア希望者 新人エンジニア IT業界への就職・転職希望者 サポートサイト Programming learning Recommended schools Recommended books Information dissemination Computer beginners Prospective engineers New engineers Prospective job seekers in the IT industry Support site
01-VBA

Excel VBA の「コメント」と「一括コメント」| 現役エンジニア&プログラミングスクール講師

2022年12月12日
プログラミング学習 おすすめ書籍情報発信 パソコン初心者 エンジニア希望者 新人エンジニア IT業界への就職・転職希望者 サポートサイト Programming learning Recommended schools Recommended books Information dissemination Computer beginners Prospective engineers New engineers Prospective job seekers in the IT industry Support site