集合运算

常见的集合运算包括集合的交集运算、并集运算、差集运算、对称差集运算和子集和超集运算等。Python中提供了相应的函数可以直接进行计算,Excel VBA中则需要自己编程求取。Excel VBA编程时用到编写函数的知识,请参见第10章的介绍。

交集运算

对于两个给定的集合,交集运算求取两个集合中都有的元素。

【Excel VBA】

下面的Intersection函数求取两个给定集合的交集。两个集合分别用两个一维数组给出,它们的交集用一个一维数组返回。求交集时,首先用第1个数组的值作为键创建一个字典,然后遍历第2个数组,用字典对象的Exists函数判断第2个数组中的每个元素在字典中是否存在,如果存在,就是交集中的元素,把它添加到一个新的一维数组中。最后返回这个一维数组,即要求取的交集。示例文件的存放路径为Samples\ch09\Excel VBA\交集运算.xlsm。

code.vba
Function Intersection(arr1(), arr2())
  Dim intI As Integer
  Dim intK As Integer
  Dim arr3()
  Dim d As Dictionary
  Set d = New Dictionary

  '用第1个数组的值作为键创建字典d
  For intI = LBound(arr1) To UBound(arr1)
    d(arr1(intI)) = ""
  Next
  intK = 0
  '遍历第2个数组,判断元素在字典d中是否存在
  '如果存在,则添加到新数组中
  For intI = LBound(arr2) To UBound(arr2)
    If d.Exists(arr2(intI)) Then
      ReDim Preserve arr3(intK)
      arr3(intK) = arr2(intI)
      intK = intK + 1
    End If
  Next
  '返回新数组,即所求交集
  Intersection = arr3
End Function
Sub Test()
  Dim arr1(3), arr2(2)
  Dim arr3()
  arr1(0) = 9: arr1(1) = 1: arr1(2) = 8: arr1(3) = 12
  arr2(0) = 8: arr2(1) = 7: arr2(2) = 1
  '调用Intersection函数求交集
  arr3 = Intersection(arr1, arr2)
  Debug.Print "集合1:" & vbTab;
  For intI = LBound(arr1) To UBound(arr1)
    Debug.Print arr1(intI);
  Next
  Debug.Print
  Debug.Print "集合2:" & vbTab;
  For intI = LBound(arr2) To UBound(arr2)
    Debug.Print arr2(intI);
  Next
  Debug.Print
  Debug.Print "交集:" & vbTab;
  For intI = LBound(arr3) To UBound(arr3)  '输出交集中的元素
    Debug.Print arr3(intI);
  Next
End Sub

运行过程,在立即窗口中输出交集中的元素。

code.vba
集合1:  9  1  8  12
集合2:  8  7  1
交集:   8  1

【Python】

Python中,可用&运算符或集合对象的intersection方法求两个给定集合的交集。

code.python
>>> {9,1,8,12} & {8,7,1}
{8, 1}
>>> {9,1,8,12}.intersection({8,7,1})
{8, 1}

可见,两个给定集合的交集即这两个集合共有的元素组成的新集合。

并集运算

将两个集合的元素放到一起并进行去重处理,得到的就是它们的并集。

【Excel VBA】

下面的Union函数求取两个给定集合的并集。两个集合分别用两个一维数组给出,它们的并集用一个一维数组返回。求并集时,用两个数组的值作为键创建字典,由于字典的键在字典中必须是唯一的,起到了去重的作用。最后用字典对象的Keys方法获取所有键,它们以一个一维数组的形式返回,即为所求并集。示例文件的存放路径为Samples\ch09\Excel VBA\并集运算.xlsm。

code.vba
Function Union(arr1(), arr2())
  Dim intI As Integer
  Dim d As Dictionary
  Set d = New Dictionary

  On Error Resume Next
  '用两个数组中的值创建字典
  For intI = LBound(arr1) To UBound(arr1)
    d(arr1(intI)) = ""
  Next
  For intI = LBound(arr2) To UBound(arr2)
    d(arr2(intI)) = ""
  Next

  '用字典对象的Keys方法返回所有键,对应数组即为所求并集
  Union = d.Keys
End Function
Sub Test()
  Dim arr1(3), arr2(2)
  Dim arr3()
  arr1(0) = 9: arr1(1) = 1: arr1(2) = 8: arr1(3) = 12
  arr2(0) = 5: arr2(1) = 7: arr2(2) = 1
  '求并集
  arr3 = Union(arr1, arr2)
  Debug.Print "集合1:" & vbTab;
  For intI = LBound(arr1) To UBound(arr1)
    Debug.Print arr1(intI);
  Next
  Debug.Print
  Debug.Print "集合2:" & vbTab;
  For intI = LBound(arr2) To UBound(arr2)
    Debug.Print arr2(intI);
  Next
  Debug.Print
  Debug.Print "并集:" & vbTab;
  For intI = LBound(arr3) To UBound(arr3)
    Debug.Print arr3(intI);
  Next
End Sub

运行过程,在立即窗口中输出并集中的所有元素。

code.vba
集合1:  9  1  8  12
集合2:  5  7  1
并集:   9  1  8  12  5  7

【Python】

用|运算符或集合对象的union方法求两个给定集合的并集。

code.python
>>> {9,1,8,12} | {8,7,1}
{1, 7, 8, 9, 12}
>>> {9,1,8,12}.union({8,7,1})
{1, 7, 8, 9, 12}

可见,两个给定集合的并集即这两个集合的所有元素放在一起并去掉重复元素后得到的新集合。

差集运算

对于给定的两个集合A和B,A和B的差集为A减去A和B的交集,B和A的差集为B减去A和B的交集。

【Excel VBA】

下面的Difference函数求取先后给定的两个集合的差集。两个集合分别用两个一维数组给出,它们的差集用一个一维数组返回。求差集时,用第2个数组的值作为键创建字典,遍历第1个数组,如果它的元素在字典中不存在,将它添加到新数组中。最后返回新数组,即为先后给定的两个集合的差集。示例文件的存放路径为Samples\ch09\Excel VBA\差集运算.xlsm。

code.vba
Function Difference(arr1(), arr2())
  Dim intI As Integer
  Dim intK As Integer
  Dim arr3()
  Dim d As Dictionary
  Set d = New Dictionary

'用数组2的元素作为键构造字典
  For intI = LBound(arr2) To UBound(arr2)
    d(arr2(intI)) = ""
  Next
  intK = 0
  '遍历数组1,如果元素不在字典中,添加到新数组
  For intI = LBound(arr1) To UBound(arr1)
    If Not d.Exists(arr1(intI)) Then
      ReDim Preserve arr3(intK)
      arr3(intK) = arr1(intI)
      intK = intK + 1
    End If
  Next
  '返回新数组
  Difference = arr3
End Function
Sub Test()
  Dim arr1(3), arr2(2)
  Dim arr3()
  arr1(0) = 9: arr1(1) = 1: arr1(2) = 8: arr1(3) = 12
  arr2(0) = 8: arr2(1) = 7: arr2(2) = 1
  '求差集arr1-arr2
  arr3 = Difference(arr1, arr2)
  Debug.Print "集合1:" & vbTab;
  For intI = LBound(arr1) To UBound(arr1)
    Debug.Print arr1(intI);
  Next
  Debug.Print
  Debug.Print "集合2:" & vbTab;
  For intI = LBound(arr2) To UBound(arr2)
    Debug.Print arr2(intI);
  Next
  Debug.Print
  Debug.Print "差集,集合1-集合2:" & vbTab;
  For intI = LBound(arr3) To UBound(arr3)
    Debug.Print arr3(intI);
  Next
  Debug.Print
  Debug.Print "差集,集合2-集合1:" & vbTab;
  '求差集arr2-arr1
  arr3 = Difference(arr2, arr1)
  For intI = LBound(arr3) To UBound(arr3)
    Debug.Print arr3(intI);
  Next
End Sub

运行过程,在立即窗口中输出集合1减去集合2的差集和集合2减去集合1的差集。

code.vba
集合1:  9  1  8  12
集合2:  8  7  1
差集,集合1-集合2:  9  12
差集,集合2-集合1:  7

【Python】

用减号(-)或集合对象的difference方法求两个给定集合的差集。

code.python
>>> {9,1,8,12}-{8,7,1}
{9, 12}
>>> {9,1,8,12}.difference({1,2,5})
{9, 12}
>>> {8,7,1} - {9,1,8,12}
{7}
>>> {8,7,1}.difference({9,1,8,12})
{7}

可见,两个给定集合的差集即它们各自减去二者的并集后得到的新集合。

对称差集运算

对于给定的两个集合,它们的并集减去交集得到它们的对称差集。

【Excel VBA】

下面的SymDif函数求取两个给定集合的对称差集。两个集合分别用两个一维数组给出,它们的对称差集用一个一维数组返回。下面的代码首先求给定集合的交集和并集,然后求并集和交集的差集并返回,此即为所求的两个给定集合的对称差集。示例文件的存放路径为Samples\ch09\Excel VBA\对称差集运算.xlsm。

code.vba
Function SymDif(arr1(), arr2())
  Dim intI As Integer
  Dim intK As Integer
  Dim arr3(), arr4(), arr5()
  Dim d As Dictionary
  Dim d2 As Dictionary
  Set d = New Dictionary
  Set d2 = New Dictionary

  '求交集arr3
  For intI = LBound(arr1) To UBound(arr1)
    d(arr1(intI)) = ""
  Next
  intK = 0
  For intI = LBound(arr2) To UBound(arr2)
    If d.Exists(arr2(intI)) Then
      ReDim Preserve arr3(intK)
      arr3(intK) = arr2(intI)
      d2(arr3(intK)) = ""
      intK = intK + 1
    End If
  Next

  '求并集
  For intI = LBound(arr2) To UBound(arr2)
    d(arr2(intI)) = ""
  Next
  arr4 = d.Keys

  '求对称差集,并集-交集
  intK = 0
  For intI = LBound(arr4) To UBound(arr4)
    If Not d2.Exists(arr4(intI)) Then
      ReDim Preserve arr5(intK)
      arr5(intK) = arr4(intI)
      intK = intK + 1
    End If
  Next
  SymDif = arr5
End Function
Sub Test()
  Dim arr1(3), arr2(2)
  Dim arr3()
  arr1(0) = 9: arr1(1) = 1: arr1(2) = 8: arr1(3) = 12
  arr2(0) = 8: arr2(1) = 7: arr2(2) = 1
  arr3 = SymDif(arr1, arr2)
  Debug.Print "集合1:" & vbTab;
  For intI = LBound(arr1) To UBound(arr1)
    Debug.Print arr1(intI);
  Next
  Debug.Print
  Debug.Print "集合2:" & vbTab;
  For intI = LBound(arr2) To UBound(arr2)
    Debug.Print arr2(intI);
  Next
  Debug.Print
  Debug.Print "对称差集:" & vbTab;
  For intI = LBound(arr3) To UBound(arr3)
    Debug.Print arr3(intI);
  Next
End Sub

运行过程,在立即窗口中输出给定集合的对称差集。

code.vba
集合1:  9  1  8  12
集合2:  8  7  1
对称差集:   9  12  7

【Python】

使用^运算符或集合对象的symmetric_difference方法计算给定集合的对称差集。

code.python
>>> {9,1,8,12}^{8,7,1}
{7, 9, 12}
>>> {9,1,8,12}.symmetric_difference({8,7,1})
{7, 9, 12}

集合{9,1,8,12}和{8,7,1}的并集为{1, 7, 8, 9, 12},交集为{1,8},对称差集等于给定集合的并集减去交集,所以为{7, 9, 12}。

子集和超集运算

对于给定的集合A和B,如果集合A大于等于集合B,并且集合B中所有元素都在集合A中,则称集合B是集合A的子集,称集合A是集合B的超集。

【Excel VBA】

下面的IsSubset函数判断给定的集合arr2是否为arr1的子集。两个集合分别用两个一维数组给出。如果集合arr2是arr1的子集,返回True,否则返回False。示例文件的存放路径为Samples\ch09\Excel VBA\子集和超集运算.xlsm。

code.vba
Function IsSubset(arr1(), arr2()) As Boolean
  'arr1>=arr2,判断arr2是否为arr1的子集
  Dim intI As Integer
  Dim intK As Integer
  Dim arr3()
  Dim d As Dictionary
  Set d = New Dictionary
  '用第1个数组的值作为键创建字典
  For intI = LBound(arr1) To UBound(arr1)
    d(arr1(intI)) = ""
  Next

  IsSubset = True
  '如果arr2比arr1大,返回False
  If UBound(arr2) - LBound(arr2) > UBound(arr1) - LBound(arr2) Then
    IsSubset = False
  '否则
  Else
    '如果第2个数组中的元素都在字典中,返回True
    For intI = LBound(arr2) To UBound(arr2)
      If Not d.Exists(arr2(intI)) Then
        IsSubset = False
        Exit Function
      End If
    Next
  End If
End Function
Sub Test()
  Dim arr1(3), arr2(2)
  arr1(0) = 9: arr1(1) = 1: arr1(2) = 8: arr1(3) = 12
  arr2(0) = 8: arr2(1) = 9: arr2(2) = 1
  Debug.Print "集合1:" & vbTab;
  For intI = LBound(arr1) To UBound(arr1)
    Debug.Print arr1(intI);
  Next
  Debug.Print
  Debug.Print "集合2:" & vbTab;
  For intI = LBound(arr2) To UBound(arr2)
    Debug.Print arr2(intI);
  Next
  Debug.Print
  Debug.Print "集合2是集合1的子集:” & vbTab;
  Debug.Print IsSubset(arr1, arr2)  '子集判断
End Sub

运行过程,在立即窗口中输出下面的结果。判断结果为True,说明集合2是集合1的子集。

code.vba
集合1:  9  1  8  12
集合2:  8  9  1
集合2是集合1的子集:    True

【Python】

用<=运算符或集合对象的issubset方法进行子集运算。对于集合A和集合B,如果A<=B,或A.issubset(B)的返回值为True,则集合A是集合B的子集。

code.python
>>> {8,9,1} <= {9,1,8,12}
True
>>> {8,9,1}.issubset({9,1,8,12})
True

对于集合A和集合B,如果A<B,则集合A是集合B的真子集。

code.python
>>> {8,9,1} < {9,1,8,12}
True

对于集合A和集合B,如果A>=B,或A.issuperset(B)的返回值为True,则集合A是集合B的超集。

code.python
>>> {9,1,8,12} >= {8,9,1}
True
>>> {9,1,8,12}.issuperset({8,9,1})
True

对于集合A和集合B,如果A>B,则集合A是集合B的真超集。

code.python
>>> {9,1,8,12} > {8,9,1}
True