【Educoder离散数学实训】关系基础
题有点多,能聊的不多。有些题还是比较有价值的
就单独说几个题,代码放在最后。所有函数都改成自己写的了,没准比答案给的好读一点?
T1 求给定集合的对角线关系(diagonal relation)
我觉得如果卡住的话,第一关会是一个大坎。
因为我们并不知道他到底在说啥,于是我们选择输出一下看一看。
首先,我们在__init__函数里输入
print(type(sets), type(rel))
我们发现,他俩都是
s
e
t
set
set
紧接着,我们对于
r
e
l
rel
rel内部的元素输出,想看一看序偶的存在形式是什么。
我们输入
for x in rel :
print(type(rel))
发现,序偶是以元组的形式存储的。
那么就好办了,我们就可以按照自己的理解写好这个函数。
写完了看到注释,代表
I
A
I_A
IA的
R
e
l
a
t
i
o
n
Relation
Relation对象,其实说的就是返回一个类的实例回去。
或者在调试的时候发现,给出的报错中含有:
print(Relation(sets[i], EmptySet).diagonalRelation() == Relation(sets[i], rels[i]))
我们也可以发现,返回的就是一个类的实例而已。
T3 关系的幂运算
这个题是递归做的,需要判断掉指数是 0 0 0和 − 1 -1 −1的情况,分别返回转置和 I A I_A IA
T14 关系的传递闭包
这个题我想简单聊两句(估计没人看
首先,我们抽象出来一个数学模型:
有
n
n
n个城市,如果
<
x
,
y
>
∈
R
<x,y>\in R
<x,y>∈R则代表
x
x
x和
y
y
y有一条路可达,这条路需要走一天。
那么
R
R
R的关系矩阵
M
M
M代表什么呢?代表的是,如果
M
i
,
j
M_{i,j}
Mi,j是
1
1
1代表的是
i
i
i可以走
1
1
1天到
j
j
j
问题来了,
R
2
R^2
R2代表什么呢?其实通过关系的合成我们可以明白,如果
R
2
R^2
R2得到的关系矩阵
M
′
M'
M′,其中若
M
i
,
j
′
M'_{i,j}
Mi,j′代表的是
i
i
i走
2
2
2天可以到
j
j
j
同理,
R
k
R^k
Rk就是走
k
k
k天罢了。
那这时,我们就可以发现,其实
R
∪
R
2
∪
R
3
.
.
.
R\cup R^2\cup R^3...
R∪R2∪R3...只需要计算到
R
n
−
1
R^{n-1}
Rn−1即可。因为一个城市如果真的能到另一个城市,最多只需要走
n
−
1
n-1
n−1天,就是把其他
n
−
2
n-2
n−2个城市都经历一遍。这是容易理解的。再多的
R
R
R的幂次也就可以很容易的证明是无效贡献的。
T15 利用Warshall算法求传递闭包
这个题其实就是
f
l
o
y
d
floyd
floyd。
本质上讲,就是一种以
O
(
n
3
)
O(n^3)
O(n3)的时间复杂度求任意两点最短路的算法。
其基于的理论是最短路的两点,途径的任意两点之间都是最短路,这是显然的。
至于
f
l
o
y
d
floyd
floyd算法的正确性可以用数学归纳法进行证明,这里不过多赘述了。
这个题其实是
f
l
o
y
d
floyd
floyd的一种特殊应用,也就是求传递闭包。
明白了求最短路,传递闭包其实就是路径大小变成是否可达而已。
T17 计算等价类
这个题有更快的一种数据结构,叫并查集。感兴趣可以自行查询。
这里的做法非常的平凡,建立一个
v
i
s
i
t
e
d
visited
visited数组,表示这个元素有没有进别的等价类里面。如果没有的话,就遍历整个
s
e
t
set
set找到他的所有等价类,并将他们的
v
i
s
i
t
e
d
visited
visited全部设置成
T
r
u
e
True
True即可。文章来源:https://www.toymoban.com/news/detail-731306.html
其余的题没有聊的必要了,都是书上的定义而已,简单的代码复现。
代码:文章来源地址https://www.toymoban.com/news/detail-731306.html
import functools
class Relation(object):
def __init__(self, sets, rel):
#rel为sets上的二元关系
assert not(len(sets)==0 and len(rel) > 0) #不允许sets为空而rel不为空
assert sets.issuperset(set([x[0] for x in rel]) | set([x[1] for x in rel])) #不允许rel中出现非sets中的元素
self.rel = rel
self.sets = sets
def __str__(self):
relstr = '{}'
setstr = '{}'
if len(self.rel) > 0:
relstr = str(self.rel)
if len(self.sets) > 0:
setstr = str(self.sets)
return 'Relation: ' + relstr + ' on Set: ' + setstr
def __eq__(self, other):
#判断两个Relation对象是否相等,关系及集合都要相等
return self.sets == other.sets and self.rel == other.rel
def diagonalRelation(self):
#返回代表IA的Relation对象
n = len(self.sets)
ret = set()
for x in self.sets :
ret.add((x, x))
return Relation(self.sets, ret)
def __mul__(self, other):
assert self.sets == other.sets
#实现两个关系的合成,即self*other表示other合成self。请注意是先看other的序偶
#返回合成的结果,为一个Relation对象
ret = set()
for x in other.rel :
for y in self.rel :
if x[1] == y[0] :
ret.add((x[0], y[1]))
return Relation(self.sets, ret)
def __pow__(self, power, modulo=None):
assert power >= -1
# 实现同一关系的多次合成,重载**运算符,即self*self*self=self**3
# 在每个分支中返回对应的结果,结果是一个Relation对象
if power == -1 :
ret = set()
for x in self.rel :
ret.add((x[1], x[0]))
return Relation(self.sets, ret)
elif power == 0 :
return self.diagonalRelation()
else :
return self * self ** (power - 1)
def __add__(self, other):
assert self.sets == other.sets
#实现两个关系的并运算,重载+运算符,即self+other表示self并other
#请注意,是Relation对象rel成员的并返回结果为一个Relation对象
ret = set()
for x in self.rel :
ret.add(x)
for y in other.rel :
ret.add(y)
return Relation(self.sets, ret)
def toMatrix(self):
#将序偶集合形式的关系转换为矩阵。
#为保证矩阵的唯一性,需对self.sets中的元素先排序
matrix = []
elems = sorted(list(self.sets))
line = [0]*len(self.sets)
for elem in elems:
#实现转换为矩阵的功能
line = [0] * len(self.sets)
for x in self.rel :
if x[0] == elem :
line[elems.index(x[1])] = 1
matrix.append(line)
return matrix
def isReflexive(self):
#判断self是否为自反关系,是则返回True,否则返回False
for x in self.sets :
if not (x, x) in self.rel :
return False
return True
def isIrreflexive(self):
# 判断self是否为反自反关系,是则返回True,否则返回False
for x in self.sets :
if (x, x) in self.rel :
return False
return True
def isSymmetric(self):
# 判断self是否为对称关系,是则返回True,否则返回False
for x in self.rel :
if not (x[1], x[0]) in self.rel :
return False
return True
def isAsymmetric(self):
# 判断self是否为非对称关系,是则返回True,否则返回False
for x in self.rel :
if (x[1], x[0]) in self.rel :
return False
return True
def isAntiSymmetric(self):
# 判断self是否为反对称关系,是则返回True,否则返回False
for x in self.rel :
if x[0] != x[1] and (x[1], x[0]) in self.rel :
return False
return True
def isTransitive(self):
# 判断self是否为传递关系,是则返回True,否则返回False
for x in self.rel :
for y in self.rel :
if x[1] == y[0] and not (x[0], y[1]) in self.rel :
return False
return True
def reflexiveClosure(self):
#求self的自反闭包,注意使用前面已经重载过的运算符
#返回一个Relation对象,为self的自反闭包
ret = self.rel.copy()
for x in self.sets :
ret.add((x, x))
return Relation(self.sets, ret)
def symmetricClosure(self):
# 求self的对称闭包,注意使用前面已经重载过的运算符
# 返回一个Relation对象,为self的对称闭包
ret = self.rel.copy()
for x in self.rel :
ret.add((x[1], x[0]))
return Relation(self.sets, ret)
def transitiveClosure(self):
closure = self
# 求self的传递闭包,注意使用前面已经重载过的运算符
# 该方法实现的算法:严格按照传递闭包计算公式求传递闭包
n = len(self.sets)
for i in range(2, n) :
closure = closure + self ** i
return closure
def transitiveClosure3(self):
#该方法利用Roy-Warshall计算传递闭包
#现将关系转换为矩阵,再调用__warshall函数
m = self.toMatrix()
return self.__warshall(m)
def __warshall(self, a):
assert (len(row) == len(a) for row in a)
n = len(a)
#请在下面编程实现Roy-Warshall求传递闭包的算法
#参数a:为一个关系矩阵
for k in range(n) :
for i in range(n) :
for j in range(n) :
a[i][j] |= a[i][k] & a[k][j]
return a
def isEquivalenceRelation(rel):
#该函数对给定的Relation对象rel,判断其是否为等价关系
#是则返回True,否则返回False
return rel.isReflexive() & rel.isSymmetric() & rel.isTransitive()
def createPartition(rel):
#对给定的Relation对象rel,求其决定的rel.sets上的划分
#如果rel不是等价关系,返回空集
if not isEquivalenceRelation(rel):
print("The given relation is not an Equivalence Relation")
return set([])
#如rel是等价关系,请编程实现求划分的程序
partition = set([])
n = len(rel.sets)
L = list(rel.sets)
visited = [0] * n
for i in range(n) :
mdl = set()
mdl.add(L[i])
if not visited[i] :
visited[i] = 1
for j in range(n) :
if (L[i], L[j]) in rel.rel :
mdl.add(L[j])
visited[j] = 1
partition.add(frozenset(mdl))
return partition
def createEquivalenceRelation(partition, A):
#对给定的集合A,以及A上的一个划分partition
#生成由该划分决定的等价关系
assert functools.reduce(lambda x, y: x.union(y), partition) == A
ret = set()
for S in partition :
for x in S :
for y in S :
ret.add((x, y))
return Relation(A, ret)
def isPartialOrder(rel):
# 该函数对给定的Relation对象rel,判断其是否为半序关系
#是则返回True,否则返回False。
return rel.isReflexive() & rel.isAntiSymmetric() & rel.isTransitive()
def isQuasiOrder(rel):
# 该函数对给定的Relation对象rel,判断其是否为拟序关系
# 是则返回True,否则返回False。
return rel.isIrreflexive() & rel.isAntiSymmetric() & rel.isTransitive()
def isLinearOrder(rel):
# 该函数对给定的Relation对象rel,判断其是否为全序关系
if not isPartialOrder(rel):
return False
else:
S = rel.sets
for x in S :
for y in S :
if not (x, y) in rel.rel and not (y, x) in rel.rel :
return False
return True
def join(rel1, rel2):
#对给定的关系rel1和rel2
assert rel1.sets == rel2.sets
#首先得到二者的矩阵
M1 = rel1.toMatrix()
M2 = rel2.toMatrix()
m = len(M1)
n = m
M = []
#实现关系矩阵的join运算,结果存于M中
for i in range(n) :
mdl = []
for j in range(n) :
mdl.append(M1[i][j] | M2[i][j])
M.append(mdl)
return M
def meet(rel1, rel2):
# 对给定的关系rel1和rel2
assert rel1.sets == rel2.sets
# 首先得到二者的矩阵
M1 = rel1.toMatrix()
M2 = rel2.toMatrix()
m = len(M1)
n = m
M = []
# 实现关系矩阵的meet运算,结果存于M中
for i in range(n) :
mdl = []
for j in range(n) :
mdl.append(M1[i][j] & M2[i][j])
M.append(mdl)
return M
def booleanProduct(rel1, rel2):
# 对给定的关系rel1和rel2
assert rel1.sets == rel2.sets
# 首先得到二者的矩阵
M1 = rel1.toMatrix()
M2 = rel2.toMatrix()
m = len(M1)
n = m
M = []
#********** Begin **********#
# 实现关系矩阵的布尔乘积运算,结果存于M中
for i in range(n) :
mdl = []
for j in range(n) :
tmp = 0
for k in range(n) :
tmp |= M1[i][k] & M2[k][j]
mdl.append(tmp)
M.append(mdl)
#********** End **********#
return M
到了这里,关于【Educoder离散数学实训】关系基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!