VBAを用いた集合の理解 ④ 和集合と積集合
先日から高校数学の学び直しとして、VBAを用いた集合の理解に挑戦している。
infoment.hatenablog.com
今日も、先日の続きから。
今日は、和集合と積集合について考えてみる。
例えば、以下のグループがあるとする。
- Aグループ:りんご、みかん、ばなな
- Bグループ:ぶどう、みかん、なし
この時、和集合と積集合は次のようになる。
- 和集合 A∪B AとBのどちらかにいる
⇒ りんご、みかん、ばなな、ぶどう、なし - 積集合 A∩B 必ずAとBの両方にいる
⇒ みかん
AとBを配列とした場合、次の作戦で和集合と積集合を求めてみる。
和集合の場合 A∪B
両グループの果物を全部一まとめにして、重複があれば更に一まとめにする。
今回は、キーの重複登録ができない「辞書(連想配列)」を活用してみよう。
折角なので、和集合の中身を並び替えるオプションもつけてみよう。
Public Function SortArray(ByVal source_array As Variant, _ Optional sort_order As Excel.XlSortOrder = xlAscending) As Variant Dim aryList As Object Set aryList = CreateObject("System.Collections.ArrayList") Dim s As Variant For Each s In source_array Call aryList.Add(s) Next Select Case sort_order Case xlAscending ' 昇順でソート。 Call aryList.Sort Case xlDescending ' 昇順でソートののち、降順へ反転。 Call aryList.Sort Call aryList.Reverse End Select SortArray = aryList.ToArray End Function
' 集合Aと集合Bの和集合を取得。 ' A ∪ B ' ※配列Aと配列Bの次元数に関わらず、AまたはBの要素全てを ' 一次元配列に格納する。 ' ※AまたはB内で重複していた要素は、この処理により重複除去される。 Public Function GetUnionSet(ByVal set_A As Variant, _ ByVal set_B As Variant, _ Optional sort_order As Excel.XlSortOrder) As Variant If Not IsArray(set_A) Or Not IsArray(set_B) Then Exit Function Dim a As Variant Dim Dict As Object Set Dict = CreateObject("Scripting.Dictionary") ' 連想配列の「キーの重複を認めない」仕様を利用して、重複除去している。 ' 従ってキーに対応するアイテムは不問であり、今回はキーそのものをセットしている。 Dim arr As Variant For Each arr In Array(set_A, set_B) For Each a In arr Dict(a) = a Next Next arr = Dict.keys If sort_order <> 0 Then arr = SortArray(arr, sort_order) End If GetUnionSet = arr End Function
積集合の場合 A∩B
Aの各要素について、Bに存在するものだけを辞書に登録する。
存在するか否かは、先日作成したIsElementを利用する。
' 指定文字列が、ある集合の要素であるか否かを返す。 ' a ∈ A:aは集合Aの要素である。 Public Function IsElement(ByVal element As Variant, _ ByVal target_set As Variant, _ Optional LookAt As XlLookAt = xlWhole) As Boolean ' 集合ではないものが指定された場合、Falseを返す。 If Not IsArray(target_set) Then Exit Function ' 配列内の値と順にelementを照合し、同じものがある時点でTrueを返す。 ' 従って、この関数はtarget_set内で複数の同じ値が含まれる場合を除外しない。 Dim a As Variant If LookAt = xlPart Then element = "*" & element & "*" For Each a In target_set If a Like element Then IsElement = True Exit Function End If Next End Function
' 集合Aと集合Bの積集合を取得。 ' A ∩ B ' ※配列Aと配列Bの次元数に関わらず、AまたはBの要素全てを ' 一次元配列に格納する。 ' ※AまたはB内で重複していた要素は、この処理により重複除去される。 Public Function GetIntersectionSet(ByVal set_A As Variant, _ ByVal set_B As Variant, _ Optional sort_order As Excel.XlSortOrder) As Variant If Not IsArray(set_A) Or Not IsArray(set_B) Then Exit Function ' AとBのどちらか一方でも空集合の場合、積集合は空集合となる。 If UBound(set_A) = -1 Or UBound(set_B) = -1 Then GetIntersectionSet = EmptySet Exit Function End If Dim a As Variant Dim Dict As Object Set Dict = CreateObject("Scripting.Dictionary") ' 連想配列の「キーの重複を認めない」仕様を利用して、重複除去している。 ' 従ってキーに対応するアイテムは不問であり、今回はキーそのものをセットしている。 For Each a In set_A If IsElement(a, set_B) Then Dict(a) = a End If Next ' 辞書に何も登録されていない場合、積集合は存在しない。 If Dict.Count = 0 Then GetIntersectionSet = EmptySet Exit Function End If Dim arr As Variant arr = Dict.keys If sort_order <> 0 Then arr = SortArray(arr, sort_order) End If GetIntersectionSet = arr End Function
結果の確認
それでは、簡単なテストを一つ。
Sub test() Dim MS As VBAProject.MathSet Set MS = New VBAProject.MathSet Dim 果物1 As Variant 果物1 = Array("りんご", "みかん", "ばなな") Dim 果物2 As Variant 果物2 = Array("ぶどう", "みかん", "なし") Debug.Print Join(MS.GetUnionSet(果物1, 果物2), ", ") Debug.Print Join(MS.GetIntersectionSet(果物1, 果物2), ", ") End Sub
冒頭に紹介した事例と、同じ結果を得ることができた。
そろそろ、終盤に差し掛かってきた。
次回は、補集合と差集合に挑戦です。
参考まで。