【Educoder离散数学实训】关系基础

【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 \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... RR2R3...只需要计算到 R n − 1 R^{n-1} Rn1即可。因为一个城市如果真的能到另一个城市,最多只需要走 n − 1 n-1 n1天,就是把其他 n − 2 n-2 n2个城市都经历一遍。这是容易理解的。再多的 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即可。

其余的题没有聊的必要了,都是书上的定义而已,简单的代码复现。
代码:

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作业,离散数学,python,算法,开发语言,数据结构)