这一章我们首先学习如何使程序理解并操作数集,然后学习集合如何帮助我们理解概率中的基本概念,最后我们将学习如何通过产生随机数来模拟随机事件。正式开始!
5.1 什么是集合?
集合(set)由不同的对象组成,这些对象通常也被称为元素(element)或成员(member)。集合的两个特征使其不同于任何对象组合。首先,集合是“被明确定义的”,意味着问题“一个具体的对象在这个组合里吗?”总是有明确的答案(”是“或”否“),答案通常基于某一规则或某些给定的标准。第二个特征是集合中的任意两个元素都是不同的。一个集合可以包含任何东西,如数字,人、事物、词语等。
接下来我们学习如何在Python中用Sympy对集合进行操作,同时也会介绍集合的一些基本属性。
5.1.1 创建集合
在数学符号中,可以将集合元素括在大括号中表示一个集合。例如,{2, 4, 6}表示一个以2、4和6为元素的集合。要在Python中创建集合,可以使用Sympy包中的FiniteSet类,如下所示:
>>> from sympy import FiniteSet
>>> s = FiniteSet(2, 4, 6)
>>> s
{2, 4, 6}
首先从Sympy导入FiniteSet类,然后通过传递集合元素来创建这个类的一个对象,我们使用s标签来指代刚刚创建的集合。
同一集合可以包含不同类型的数字,如整数、浮点数和分数。
>>> from sympy import FiniteSet
>>> from fractions import Fraction
>>> s = FiniteSet(1, 1.5, Fraction(1,5))
>>> s
{1/5, 1, 1.5}
集合的基数(cardinality)是指该集合中元素的个数,可以通过len()函数得到。
>>> s = FiniteSet(1, 1.5, 3)
>>> len(s)
3
判断数字是否在集合中
要判断一个数字是否为现有集合的元素,可以使用运算符in。 这一运算符询问Python:“这个数在这个集合中吗?”如果数字属于集合,返回真(True), 否则返回 假(False)。比如,我们想判断4是否在前一个集合中,可以执行如下操作:
>>> 4 in s
False
因为4不在集合中,所以运算结果返回了False。
创建一个空集合
如果想要创建一个空集(empty set),即集合中没有任何元素或者成员,那么创建一个没有参数的FiniteSet对象,该结果就是一个EmptySet对象:
>>> s = FiniteSet(s)
>>> s
EmptySet()
通过列表或者元组创建集合
也可以将列表或者元组作为参数传递给FiniteSet的方式来创建集合:
>>> members = [1, 2, 3]
>>> s = FiniteSet(*members)
>>> s
{1, 2, 3}
此处,我们并不是直接将集合元素作为参数传递给FiniteSet,而是先将集合元素存储在一个列表中,称列表为members。然后,使用一种特殊的 Python语法将此列表传递给FiniteSet, 该语法解释为:创建一个 FiniteSet对象,将列表中的元素作为单独的参数传递,而不是作为列表传递。也就是说,这个方法创建一个FiniteSet对象等价于使用FiniteSet(1,2,3),当集合的元素是在程序运行中生成时,我们使用该语法。
集合重复与排序
Python中的集合(如数学中的集合)会忽略重复的元素,也不记下集合元素的顺序。例如,如果你要从一个列表中创建一个集合,该列表中某一数字出现多次,则该数字只会被添加到集合中一次。
>>> from sympy import FiniteSet
>>> members = [1, 2, 3, 2]
>>> FiniteSet(*members)
{1, 2, 3}
这里我们传入的列表中有两个2,但是在创建的集合中2只出现了一次。
在Python列表与元组中,每个元素以特定的顺序存储,但对集合来说并不总是这样,例如,我们可以通过以下方式迭代输出集合的每个元素:
>>> from sympy import FiniteSet
>>> s = FiniteSet(1, 2, 3)
>>> for member in s:
print(member)
1
2
3
当运行这段代码时,元素可能以任意顺序输出。这是因为集合在Python中的存储只考虑哪些元素在集合中,而不考虑这些元素存储的顺序。
再来看一个例子,如果两个集合的元素相同,我们称两个集合相等。在Python中,可以使用相等运算符(==)来判断两个集合是否相等:
>>> from sympy import FiniteSet
>>> s = FiniteSet(3, 4, 5)
>>> n = FiniteSet(3, 5, 4)
>>> s == n
True
虽然这两个集合中的元素以不同的顺序出现,但这两个元素仍是相等的。
5.1.2 子集、超集与幂集
同数学中的概念一样,如果集合s中的所有元素也是另一个集合t的元素,则称集合s是集合t的子集(subset)。例如,集合{1}是集合{1, 2}的子集。可以使用is_subset()函数来判断一个集合是否是另一个集合的子集:
>>> s = FiniteSet(1)
>>> n = FiniteSet(1, 2)
>>> s.is_subset(n)
True
>>> n.is_subset(s)
>>> False
注意:空集是任意集合的子集,此外,任何集合也是自身的一个子集,如下所示:
>>> s.is_subset(s)
True
>>> t.is_subset(t)
True
类似地,如果集合t包含另一个集合s中所有的元素,则称集合t是集合s的超集(superset)。可以使用is_superse()函数来判断一个集合是否是另一个集合的超集。
>>> s.is_superset(n)
False
>>> n.is_superset(s)
True
集合s的幂集(power set)是s的所有子集构成的集合。任意集合s,共有2^{|s|}个子集,这里|s|为集合的基数。例如,集合{1, 2, 3}的基数是3,所以他共有2^3 = 8个子集:{}(空集)、{1}、{2}、{3}、{1, 2}、{1, 3}、{2, 3}和{1, 2,3}。
所有子集的集合构成幂集,可以使用powerset()函数得到幂集:
>>> s = FiniteSet(1, 2, 3)
>>> ps = s.powerset()
FiniteSet(EmptySet, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3})
幂集本身就是一个集合,我们也可以使用len()函数来获得它的基数:
>>> len(ps)
8
此处幂集基数 = 2^{原集合基数} = 8。
基于子集的定义,任意两个包含完全相同元素的集合互为子集和超集。仅当s的所有元素都在t中并且t中至少有一个元素不在s中。集合s称为集合t的真子集(proper subset), 所以如果集合t包含了元素1、2、3以及至少另外一个不同于1、2、3的元素时,s={1,2, 3}才是t的真子集。这也意味着t是s的一个真超集(proper superset)。 可以使用is_proper_subset()和is_proper_superset()函 数来判断这两种关系:
>>> from sympy import FiniteSet
>>> s = FiniteSet(1, 2, 3)
>>> t = FiniteSet(1, 2, 3)
>>> t.is_proper_subset(s)
False
>>> s.is_proper_superset(t)
False
现在,我们重建集合t使其包含另外一个元素,现在s是t的真子集,t是s的真超集:
>>> s = FiniteSet(1, 2, 3, 4)
>>> t.is_proper_subset(s)
True
>>> s.is_proper_superset(t)
True
5.1.3 集合运算
集合运算(如并集、交集和笛卡尔积等)允许我们以特定的方式合并集合,当我们必须同时考虑多个集合时,这些集合运算在实际的问题解决中非常有用。在这一张后面的部分,我们将看到如何使用这些运算,将公式应用到多个数据集合中并计算随机事件的概率。
并集和交集
两个集合并集(union)是一个集合,它包含两个集合中所有不同的元素。在集合论中,使用符号U来表示并集运算。例如,{1, 2} U {2, 3}的并集为{1, 2, 3}。在Sympy中,可以调用union()函数来得到两个集合的并集
>>> from sympy import FiniteSet
>>> s = FiniteSet(1, 2, 3)
>>> t = FiniteSet(2, 3, 4)
>>> s.union(t)
{1, 2, 3, 4}
通过对集合s应用union方法并将t作为参数传递,可以得到集合s和t的并集,其中包含了集合s和t的所有不同元素。换句话说,并集中的每个元素均为前两个集合中的某一元素或者为它们的共同元素。
两个集合的交集(intersection)生成一个仅包含两个集合共同元素的新集合。例如,集合{1, 2}和{2, 3}的交集是一个仅包含一个共同元素{2}的新集合。数学上,这个运算可记为{1,2} n {2, 3}。
在Sympy中,可以使用intersect()函数来得到交集。
>>> from sympy import FiniteSet
>>> s = FiniteSet(1, 2)
>>> t = FiniteSet(2, 3)
>>> s.intersect(t)
{2}
并集运算是找到这些集合中的所有元素(重复元素只出现一次),而交集是找到这些集合中的共同元素。这两种运算也可以应用于两个以上的集合。例如,计算三个集合的交集:
>>> from sympy import FiniteSet
>>> s = FiniteSet(1, 2, 3)
>>> t = FiniteSet(2, 3, 4)
>>> u = FiniteSet(3, 4, 5)
>>> s.union(t).union(u)
{1, 2, 3, 4, 5}
类似地,下面这句话告诉了我们如何计算三个集合的交集:
>>> s.intersect(t).intersect(u)
{3}
集合s、t和u中均包含元素3,所有求得的交集为{3},注意交集和并集都是一个集合。
笛卡尔积
两个集合的笛卡尔积(Cartesian product)创建了一个集合,该集合由每一个集合中提取一个元素所组成的元素对组成。例如,集合{1, 2}和{3,4}的笛卡尔积是{(1, 2), (1, 4), (2, 3), (2, 4)}。在Sympy中,可以通过是用乘法运算符号来得到两个集合的笛卡尔积:
>>> from sympy import FiniteSet
>>> s = FiniteSet(1, 2)
>>> t = FiniteSet(3, 4)
>>> p = s*t
>>> p
ProductSet({1, 2}, {3, 4})
这个运算得到了集合s和集合t的笛卡尔积,结果存储为p。为了更方便地看到笛卡尔积中的每一对元素,我们可以使用如下的方式迭代输出具体的元素:
>>> for elem in p:
print(elem)
(1, 3)
(2, 3)
(1, 4)
(2, 4)
笛卡尔积中的每一对元素都是一个元素,包含着来自于第一个集合和第二个集合的元素。
笛卡尔积的基数是两个集合各自基数的乘积。我们可以在Python中就行如下验证:
>>> len(p) == len(s)*len(t)
True
如果我们对一个集合应用指数运算(**),那么可以得到这个集合与它本身相乘指定次数的笛卡尔积。
>>> from sympy import FiniteSet
>>> s = FiniteSet(1,2)
>>> p = s**3
>>> p
ProductSet({1, 2}, {1, 2}, {1, 2})
这里的例子是计算集合的3次幂。因为我们计算的是三个集合的笛卡尔积,因此在这里给出了一个由来自于每一个集合的元素的所有可能的三元组集合。
>>> for elem in p:
print(elem)
(1, 1, 1)
(2, 1, 1)
(1, 2, 1)
(2, 2, 1)
(1, 1, 2)
(2, 1, 2)
(1, 2, 2)
(2, 2, 2)
计算笛卡尔积对找到集合所有可能的组合是非常有帮助的,接下来我们将看到这一点。
对多个变量集合应用一个公式
考虑一个长度为L的单摆,这个单摆的时间周期T(单摆完成一次摆动所花费的全部时间)由公式给出
这里Π是数学常数,g是当地重力加速度,在地球上近似等于。由于Π和g是常数,一次查高度L是方程右侧唯一变量。
如果你想知道一个单摆的时间周期是如何随其长度变化的,就需要设定长度的不同取值并用公式计算每个值对应的时间周期。一个经典的高中实验是将你使用上述公式得到的时间和周期(理论结果)与你在实验室测量的时间(实验结果)进行比较。例如,选择5个不同的值(长度均以厘米为单位):15,18,21,22.5,25。使用Python,我们可以编写一个简短的程序来加速理论计算的结果:
from sympy import FiniteSet, pi
def time_prod(length): # ①
g = 9.8
T = 2*pi*(length/g)**0.5
return T
if __name__ == '__main__':
L = FiniteSet(15, 18, 21, 22.5, 25) # ②
for l in L:
t = time_prod(l / 100) # ③
print('Length: {0} cm Time Period: {1:.3f} s '.format(float(l), float(t)))
我们在①处定义time_period()函数,这个函数把单摆的时间周期应用于一个给定的长度值,该长度值通过参数length传递,然后程序在②处定义了一个长度集合,并在③处将time_period()函数应用于长度集合中的每一个长度值。在此需要注意,我们单摆长度的单位均为厘米,在运算时需要将其转化为国际单位m,故此需要除以100。最后运行程序,我们会发现:
Length: 15.0 cm Time Period: 0.777 s
Length: 18.0 cm Time Period: 0.852 s
Length: 21.0 cm Time Period: 0.920 s
Length: 22.5 cm Time Period: 0.952 s
Length: 25.0 cm Time Period: 1.004 s
不同的重力,不同的结果
现在,想象一下我们在三个不同的地方进行了这个实验:澳大利亚布里斯班、北极和赤道。地心引力根据你所在位置的纬度不同而稍有差异:在赤道处稍微偏低(),而在北极则偏高一些()。 这表明我们可以在上述公式中将重力加速度视为一个变量,而不是一个常数,然后计算三个不同重力加速度值{9.8, 9.78, 9.83}下的结果。 如果我们想在这三个位置分别计算5个长度下的单摆周期,一个系统化地计算这些值的组合的方法是使用笛卡儿积,如下述程序所示:
from sympy import FiniteSet, pi
def time_period(length, g):
return 2*pi*(length / g)**0.5
if __name__ == '__main__':
L = FiniteSet(15, 18, 21, 22.5,25)
g_values = FiniteSet(9.8, 9.78, 9.83)
print('{0:^15}{1:^15}{2:^15}'.format('Length(cm)', 'Gravitu(m/s^2)', 'Time Period(s)')) # ①
for elem in L*g_values: # ②
l = elem[0] # ③
g = elem[1] # ④
t = time_period(l/100, g)
print('{0:^15}{1:^15}{2:^15}'.format(float(l), float(g), float(t))) # ⑤
在?处,我们取两个变量集合(L和g_ values)的笛卡儿积,并对笛卡儿积得到的每个组合进行迭代以计算时间周期。每个组合用一个元组表示,对每个元组,在?处提取其第一个值(长度),在?处提取其第二个值(重力加速度),然后,和之前一样,调用time period()函数。将提取的两个值作为参数进行计算,最后输出长度(I)、 重力(g)和相应的时间周期(T)。 为了简单易读,我们将输出结果显示在一个表格中。表格由?和?处的print语句编排。编排字符串{0:^15} {1:^15} {2:^15.3f}创建了三个区域,每个区域有15个空格宽,符号^使显示结果在区域内居中。在?处print语句中的最后一个字段中,'.3f"表示将小数点后的位数限制为3。 运行程序后将看到如下结果:
Length(cm) Gravitu(m/s^2) Time Period(s)
15.0 9.78 0.7781370075512535
18.0 9.78 0.8524063837307787
15.0 9.8 0.7773425846718076
21.0 9.78 0.9207041237711209
18.0 9.8 0.8515361370682372
15.0 9.83 0.7761554993500163
22.5 9.78 0.953019309238396
21.0 9.8 0.9197641499438565
18.0 9.83 0.8502357502513805
25.0 9.78 1.00457055710456
22.5 9.8 0.9520463438910783
21.0 9.83 0.9183595716492436
25.0 9.8 1.0035449615772465
22.5 9.83 0.9505924672313103
25.0 9.83 1.0020124410166384
这个实验展示了一种简单的情形,即当你需要多个集合(或一组数字)的元素的所有可能组合时,笛卡尔积正是你所需要的。
创业项目群,学习操作 18个小项目,添加 微信:790838556 备注:小项目!
如若转载,请注明出处:https://www.zoodoho.com/34282.html