简体   繁体   中英

Python Relations with Sets of Tuples

I'm trying to determine whether or not sets of tuples have a certain type of relation. I'm trying to figure out the transitive relation, and the composite relation.

For the transitive relation:

 # A relation 'Relation' is called transitive when:
 # ∀ (a, b) ∈ Relation, (b, c) ∈ Relation ==> (a, c) ∈ Relation

For example:

>>> {(1,1), (1,2), (1,4), (2,1), (2,2), (3,3), (4,1), (4,4)} # Should be False
>>> {(1,1), (2,1), (3,1), (3,2), (4,1), (4,2), (4,3)} # Should be True

For the composite relation:

# The composite of relations 'R1' and 'R2' is the relation consisting
# of tuples (a,c), such that (a,b) ∈ R1 and (b,c) ∈ R2

For example:

{(1,0), (1,1), (2,1), (2,2), (3,0), (3,1)} == R1={(1,1), (1,4), (2,3), (3,1), (3,4)}, R2={(1,0), (2,0), (3,1), (3,2), (4,1)}
# Should return True

I am uncertain on how to start coding these functions. Any help to get me started would be greatly appreciated. Thank you!

EDIT: Here are some other relations I was able to successfully code:

# Reflexive Relation
# A relation 'Relation' on a set 'Set' is called reflexive when:
# ∀ a ∈ Set, (a,a) ∈ Relation
def is_reflexive(Set, Relation):
    newSet = {(a, b) for a in Set for b in Set if a == b}
    if Relation >= newSet:
        return True

    return False

# Symmetric Relation
# A relation 'Relation' is called symmetric when:
#  ∀ (a, b) ∈ Relation, (b, a) ∈ Relation
def is_symmetric(Relation):
    if all(tup[::-1] in Relation for tup in Relation):
        return True


    return False

I believe this is a brute force approach that works. First, we are going to use an "adjacency set" representation, then just test explicitly using a deeply nested for-loop:

In [5]: r1 =  {(1,1), (1,2), (1,4), (2,1), (2,2), (3,3), (4,1), (4,3)}

In [6]: adjacency = {}
    ...: for a,b in r1:
    ...:     adjacency.setdefault(a,set()).add(b)
    ...:

In [7]: transitive = True

In [8]: for a, related in adjacency.items():
    ...:     for b in related:
    ...:         for c in adjacency[b]:
    ...:             if c not in related:
    ...:                 transitive = False
    ...:                 print("({},{}) and ({},{}) but not ({},{})".format(a, b, b, c, a,c))
    ...:
(1,4) and (4,3) but not (1,3)
(2,1) and (1,4) but not (2,4)
(4,1) and (1,2) but not (4,2)
(4,1) and (1,4) but not (4,4)

In [9]: transitive
Out[9]: False

However, for your second example:

In [7]: r2 = {(1,1), (2,1), (3,1), (3,2), (4,1), (4,2), (4,3)}

In [8]: adjacency = {}
    ...: for a,b in r2:
    ...:     adjacency.setdefault(a,set()).add(b)
    ...:

In [9]: transitive = True

In [10]: for a, related in adjacency.items():
    ...:     for b in related:
    ...:         for c in adjacency[b]:
    ...:             if c not in related:
    ...:                 transitive = False
    ...:                 print("({},{}) and ({},{}) but not ({},{})".format(a, b, b, c, a,c))
    ...:

In [11]: transitive
Out[11]: True

Using this data structure should make it a little less horrible from a time-complexity POV.

As for constructing the composite:

In [18]: def make_adjacency_set(R):
    ...:     a = {}
    ...:     for x,y in R:
    ...:         a.setdefault(x, set()).add(y)
    ...:     return a
    ...:

In [19]: def make_composite(R1, R2):
    ...:     adj1 = make_adjacency_set(R1)
    ...:     adj2 = make_adjacency_set(R2)
    ...:     composite = set()
    ...:     for a, related in adj1.items():
    ...:         for b in related:
    ...:             for c in adj2.get(b, []):
    ...:                 composite.add((a, c))
    ...:     return composite
    ...:

In [20]: R1={(1,1), (1,4), (2,3), (3,1), (3,4)}; R2={(1,0), (2,0), (3,1), (3,2), (4,1)}

In [21]: make_composite(R1, R2)
Out[21]: {(1, 0), (1, 1), (2, 1), (2, 2), (3, 0), (3, 1)}

Or, just sticking with sets of tuple:

In [25]: composite = set()
    ...: for a, b in R1:
    ...:     for c, d in R2:
    ...:         if b == c:
    ...:             composite.add((a,d))
    ...:
    ...:

In [26]: composite
Out[26]: {(1, 0), (1, 1), (2, 1), (2, 2), (3, 0), (3, 1)}

For the transitive test, just convert the math definition in Python:

def is_transitive(relation):
    for a,b in relation:
        for c,d in relation:
            if b == c and ((a,d) not in relation):
                    # print (a,b),(c,d) # uncomment for tests...
                    return False
    return True

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM