見出し画像

【ExcelVBA】配列の最小値を求める

今回は、配列の最小値を求めるマクロを作ってみた、という内容です。

VBAの配列ってもともと実装されている関数とかが全然ないので(ないはず)、練習がてらに作ってみたって感じです。

整数乱数発生装置

まずはそのために準備として整数乱数を発生させる関数を作ります。

実はもともと乱数を発生させる関数はあるのですが、それを使って、使いやすいものにしました。

Function get_random_number(Optional r_max As Long = 1, _
    Optional r_min As Long = 0) As Long
  Randomize
  get_random_number = Int((r_max - r_min + 1) * Rnd) + r_min
End Function

では軽く解説。

引数r_maxは発生させる整数乱数の最大値を、r_minは最小値を想定しています。

Rndは0以上1未満の乱数を発生させる関数です。

Int(A)は、A以下の最大の整数を得る関数です。

ですので、Int((r_max - r_min + 1) * Rnd) + r_minでいい具合に整数乱数を得ているわけですね(解説が雑)。

Randomizeは何かというと、乱数テーブルのリセットをしています。

もし普通にRndを使うと、Excelファイルを開きなおしたときに同じ乱数テーブルの同じ部分を参照してしまい、前回出した乱数と全く同じ出方をしてしまいます。

それでは意味がないので、リセットして毎回違う乱数テーブルになるようにしているのですね。

ちなみに、引数を設定しなければ、r_max=1、r_min=0……つまり、0か1かをランダムに出す関数になります。

配列の最小値を求める関数

では次に配列の最小値を求めます。

まずはコードから。

Function get_array_min(target As Variant) As Long
  '配列の最小値を得る

  Dim i As Long, min As Long, u_target As Long, l_target As Long

  u_target = UBound(target) ‘末尾のインデックス番号
  l_target = LBound(target) '先頭のインデックス番号

  min = target(l_target) '初期値を設定
  If u_target = l_target Then '配列の要素が一つだけのときの処理
      get_array_min = min
  End If

  For i = l_target + 1 To u_target '配列の2つ目の要素から
      If target(i) < min Then 'より小さかったら
          min = target(i) '最小値更新
      End If
  Next

End Function

UBound関数とLBound関数は、それぞれ配列の最大、最小のインデックス番号を求めるものです。

流れとしては、まず最小のインデックス番号の値をminに入れて、配列の要素が一つだけでなければ、他の要素の値をminと比較して、小さければminの値を更新する……

ということをやっているだけですね。

配列が最小値をとるインデックス番号をランダムに一つ得る

そして最後に、これらの関数を使って、配列が最小値をとるインデックス番号を一つランダムにとってくるマクロを作成します。

Sub get_array_min_element(target As Variant, _
  min_val As Long, Optional min_num As Long)
  '参照渡しで配列のその最小値とそのときの番号をランダムで渡す

  Dim temp_array() As Long
  Dim i As Long, j As Long, u_target As Long, l_target As Long

  u_target = UBound(target) ‘末尾のインデックス番号
  l_target = LBound(target) '先頭のインデックス番号

  min_val = get_array_min(target) '最小値を得る

  For i = l_target To u_target '配列の要素について
      If target(i) = min_val Then '最小値だったら
          ReDim Preserve temp_array(j) '配列のサイズを変更
          temp_array(j) = i '配列に最小値をとる要素のナンバーを保存
          j = j + 1
      End If
  Next

  min_num = temp_array(get_random_number(UBound(temp_array)))

End Sub

このマクロのキーとなるのは引数の参照渡しです。

引数の渡し方には以下の2種類があります。

・参照渡し:マクロ内で引数の値を変更すると引き継がれる
・値渡し:マクロ内で引数の値を変更しても引き継がれない

デフォルトは参照渡しです。つまり、他のマクロを呼びだして、そこで引数の値を変更すると、元のマクロでも変更した値が引き継がれるのです。

それを利用し、min_valとmin_numにあたる部分に引数を渡すと、それぞれ最小値とその番号をその引数に返すようになっています。

全体の流れとしては、最小値を得て、その最小値をとるインデックス番号をtemp_arrayに保存し、そしてその中からランダムに一つ選ぶという感じになっています。

ここで、最小値をとる番号を保存するために、temp_arrayという配列を用意していますが、どれだけ最小値にあてはまる番号があるかはわからないので、配列の要素数を設定することができません。

そこで役に立つのが「ReDim Preserve構文」。ReDimは配列の中身をリセットして要素数を更新しますが、ReDim Preserveは中身は変えずに要素数だけを変えます。

ですので、一つずつ要素を増やして配列に追加することができるわけです。

ということで、配列の最小値を求めるマクロでした。

もし使いたい方がいたら、ぜひご自由にコピペしてください。

その際、スキボタンやコメントを残してくれたら、大変喜びます。


この記事が気に入ったらサポートをしてみませんか?