VBAを用いた集合の理解 ⑤ 差集合と補集合
先日から高校数学の学び直しとして、VBAを用いた集合の理解に挑戦している。
infoment.hatenablog.com
今日も、先日の続きから。
今回は、集合Bから集合Aの要素を除いた差を求めてみる。
差集合(B-A)
作戦は至ってシンプル。
集合Bに相当する配列Bから、集合Aに相当する配列Aの要素を除去するだけ。
- 配列Bの要素をキー情報とした辞書を作成。
- 配列Aの要素が辞書に存在する場合、辞書から同キーを取り除く。
- 残ったキーを配列として取得。
今回も結果をソートしたので、こちらを再掲。
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の差集合を取得。 ' B-A ' ※配列Aと配列Bの次元数に関わらず、AまたはBの要素全てを ' 一次元配列に格納する。 ' ※AまたはB内で重複していた要素は、この処理により重複除去される。 Public Function GetDifferenceSet(ByVal set_A As Variant, _ ByVal set_B As Variant, _ Optional check_subset As Boolean = True, _ Optional sort_order As Excel.XlSortOrder) As Variant If Not IsArray(set_A) Or Not IsArray(set_B) Then Exit Function ' 配列Aが配列Bに内包されているか確認。 ' これを不問とする場合、配列Aが配列Bに存在しない要素を含んでいても関係なく、 ' 単に配列Aと配列Bとに共に存在する要素を引くことになる。 If check_subset Then If Not IsSubset(set_A, set_B) Then GetDifferenceSet = EmptySet Exit Function End If End If Dim a As Variant Dim Dict As Object Set Dict = CreateObject("Scripting.Dictionary") ' まず、集合Bの要素を全て辞書に登録する。 ' 次いで、集合Aの要素が辞書に存在する場合、 ' 辞書から除去していく。 ' 最終的に残った辞書のキーが、B-Aとなる。 For Each a In set_B ' 必要なのはkey情報のみ。item情報は不問のため、 ' 何でもよい。今回は簡単のため「1」をセット。 Dict(a) = 1 Next For Each a In set_A If Dict.Exists(a) Then Dict.Remove a End If Next ' 辞書に何も登録されていない場合、差集合は存在しない。 If Dict.Count = 0 Then GetDifferenceSet = EmptySet Exit Function End If Dim arr As Variant arr = Dict.keys If sort_order <> 0 Then arr = SortArray(arr, sort_order) End If GetDifferenceSet = arr End Function
なお、集合Bが全体集合である場合、B-Aは補集合となる(=じゃない方?)。
' 全体集合 Public UniversalSet As Variant
' 集合Aの補集合を取得。 ' 上記差集合における集合Bが、特に全体集合Uであるとき。 ' U-A ' ※配列Aと配列Uの次元数に関わらず、AまたはUの要素全てを ' 一次元配列に格納する。 ' ※AまたはU内で重複していた要素は、この処理により重複除去される。 Public Function GetComplementarySet(ByVal set_A As Variant, _ Optional sort_order As Excel.XlSortOrder) As Variant GetComplementarySet = GetDifferenceSet(set_A, UniversalSet, sort_order) 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.GetDifferenceSet(果物2, 果物1), ", ") End Sub
上手くいったように見えるが、配列Aが配列Bに含まれるか否かを不問とする引数(check_subset)を設けている時点で、数学的にNGかもしれない。
でも、これができると後々便利なので、大目に見てもらえると有難いです。
参考まで。