python beginner - building list of couple with a group constraints

Viewed 210

To begin, I have this list (suggested in the exercise):

Members = [('Tessa','G1'),('Evan','G2'),('Tom','G3'),
('Mia','G3'),('Claire','G3'),('Billie','G4'),('Adrian',
'G2'),('Maddie','G1'),('Lewis','G1'),('Tony','G2'),('Joyce',
'G1'),('Julian','G5'),('Joshua','G2')('Warren','G3')]

At the end, I have to write a randomMember(L) function with parameter L which is the list Members defined above to make pairs of two people with the following constraint:

Two members of the same group (G1, G2, ...) cannot meet each other.

For example, using the list Members, "Tessa" and "Lewis" cannot meet each other.

So, I have already written this (the base code has been provided, so the location of the loops should not be changed)

def randomMember(L):
    n = len(L)
    Perm = permutation(n)
    while checkConstrainst(Perm,random(L)) == False:
        Perm = permutation(n)
    LCouples=[]
    for k in range (n):
        LCouples.append((L[k],L[Perm[k]]))
    return LCouples

To explain more, checkConstrainst function takes two parameters. Perm, which is a list of integers and L is a list which represents a draw made in the Members list previously.

The purpose of this function is to ensure that 2 people are not from the same group. The input L looks like: [(('Tessa', 'G1'), ('Lewis', 'G1')), (('Joshua', 'G2'), ('Billie', 'G4'))]

def checkConstrainst(Perm,L):

    for i in range(len(Perm)):
        PeopleGroup_1, PeopleGroup_2 = L[i] 
        People1, Group1 = PeopleGroup_1
        People2, Group2 = PeopleGroup_2
        
        if Group1 == Group2:
            return False
    return True

I have to use permutation to make this working... I don't know how to use the list inputed to make sure Perm verify the constraint for the moment.

For the the list [(('Tessa', 'G1'), ('Lewis', 'G1')), (('Joshua', 'G2'), ('Billie', 'G4'))], checkConstraint returns False and for the list [(('Tessa', 'G1'), ('Joshua', 'G2')), (('Billie', 'G4'), ('Lewis', 'G1'))] returns True because each couple is not from the same group.

The function permutation is just here to generate random integers.

Here is the code if you need:

def permutation(n):
    L=[]
    Perm=[]
    j=0
    for i in range(n):
        L.append(i)
    for k in range(n):
        alea = rd.randint(0,len(L)-1)
        j=L.pop(alea)
        Perm.append(j)
    return Perm

In RandomMember, you also have a Random function which takes the Members list as parameter. This function is making couple of people WITHOUT checking if they are from the same group (this is the goal of RandomMember). Random returns something like this : [(('Tessa', 'G1'), ('Lewis', 'G1')), (('Joshua', 'G2'), ('Billie', 'G4'))].

So, my issue is that I've tested checkConstrainst and it works well. But, in the randomMember function, while building the LCouples list, there is 2 peoples of the same group in a couple...

Here if you need is the random, derangement and isDerangement function :

def isDerangement(Perm):
    for i in range(len(Perm)):
        if Perm[i]==i:
            return False
    return True

def derangement(n):
    Perm=permutation(n)
    while isDerangement(Perm)==False:
        Perm=permutation(n)
    return Perm

def random(L):
    n=len(L)
    Couples=[]
    D=derangement(n)

    for k in range(n):
        Couples.append((L[k],L[D[k]]))
    return Couples

And I don't understand where is my error despite several hours of looking...

For randomMember(L), here is the basic code I should complete:

def randomMember(L):
    .............................................
    Perm =........................................
    while ......................................:
        Perm =....................................
    LCouples =[]
    for k in range (n):
    LCouples . append (.......................).
    return LCouples
4 Answers

I don't know if your permutation() necessary? I find a way without using it.

import random
members = [
    ('Tessa','G1'),('Evan','G2'),('Tom','G3'),('Mia','G3'),('Claire','G3'),
    ('Billie','G4'), ('Adrian','G2'),('Maddie','G1'),('Lewis','G1'),
    ('Tony','G2'),('Joyce','G1'),('Julian','G5'),('Joshua','G2'),('Warren','G3')
]

# I don't know how your random(L) look like but this function should do the same
def random_couples(L):
    couples = []
    # shuffle the members
    rm = random.sample(L,len(L))
    for i in range(0, len(L), 2):
        couples.append((rm[i], rm[i+1]))
    # return couples of random members
    return couples

# no Perm needed
def check_constraint(L):
    for pg1, pg2 in L:
        if pg1[1] == pg2[1]:
            return False
    return True

def random_member(L):
    Lcouples = random_couples(L)
    while check_constraint(Lcouples) == False:
        Lcouples = random_couples(L)
    return Lcouples

print(random_member(members))

Output:

[(('Warren', 'G3'), ('Lewis', 'G1')), (('Maddie', 'G1'), ('Adrian', 'G2')), 
(('Evan', 'G2'), ('Tessa', 'G1')), (('Claire', 'G3'), ('Julian', 'G5')), 
(('Mia', 'G3'), ('Tony', 'G2')), (('Tom', 'G3'), ('Joyce', 'G1')), 
(('Joshua', 'G2'), ('Billie', 'G4'))]
[(('Warren', 'G3'), ('Joshua', 'G2')), (('Tony', 'G2'), ('Mia', 'G3')),
(('Billie', 'G4'), ('Tom', 'G3')), (('Claire', 'G3'), ('Lewis', 'G1')), 
(('Evan', 'G2'), ('Maddie', 'G1')), (('Tessa', 'G1'), ('Adrian', 'G2')), 
(('Joyce', 'G1'), ('Julian', 'G5'))]
[(('Lewis', 'G1'), ('Warren', 'G3')), (('Tony', 'G2'), ('Joyce', 'G1')), 
(('Adrian', 'G2'), ('Tom', 'G3')), (('Joshua', 'G2'), ('Tessa', 'G1')), 
(('Billie', 'G4'), ('Evan', 'G2')), (('Julian', 'G5'), ('Mia', 'G3')), 
(('Maddie', 'G1'), ('Claire', 'G3'))]

For Perm requirement

I probably know what to do now, but I want to say why your code doesn't work first.

Perm in your randomMember(L) which passed to checkConstrainst(Perm,random(L)) has no relation with D in random(L). You generate two different lists of numbers, using D to create random couples and do verification on it but then using Perm to return final result which did not verified.

The key point is using Perm to create random couples.

import random

members = [
    ('Tessa','G1'),('Evan','G2'),('Tom','G3'),('Mia','G3'),('Claire','G3'),
    ('Billie','G4'), ('Adrian','G2'),('Maddie','G1'),('Lewis','G1'),
    ('Tony','G2'),('Joyce','G1'),('Julian','G5'),('Joshua','G2'),('Warren','G3')
]

def check_constraint(perm,L):
    n = len(L)
    # use perm passed in to create random couples
    couples = [(L[i], L[perm[i]]) for i in range(n)]
    # check if they are in same group
    for pg1, pg2 in couples:
        if pg1[1] == pg2[1]:
            return False
    return True

def random_members(L):
    n = len(L)
    # create a list of size n with shuffled numbers equal to your permutation(n)
    perm = random.sample(range(n),n)
    # move random selection inside check_constraint() so just pass L here
    while check_constraint(perm,L) == False:
        perm = random.sample(range(n),n)
    couples = [(L[i], L[perm[i]]) for i in range(n)]
    return couples

print(random_members(members))

Output:

[(('Tessa', 'G1'), ('Tom', 'G3')), (('Evan', 'G2'), ('Maddie', 'G1')),
(('Tom', 'G3'), ('Tessa', 'G1')), (('Mia', 'G3'), ('Adrian', 'G2')),
(('Claire', 'G3'), ('Julian', 'G5')), (('Billie', 'G4'), ('Mia', 'G3')),
(('Adrian', 'G2'), ('Warren', 'G3')), (('Maddie', 'G1'), ('Joshua', 'G2')),
(('Lewis', 'G1'), ('Evan', 'G2')), (('Tony', 'G2'), ('Billie', 'G4')),
(('Joyce', 'G1'), ('Claire', 'G3')), (('Julian', 'G5'), ('Tony', 'G2')),
(('Joshua', 'G2'), ('Lewis', 'G1')), (('Warren', 'G3'), ('Joyce', 'G1'))]

I feel strange that if this your expected result. We will have 14 couples instead of 7 couples for 14 people. Some of them are duplicates like (('Tessa', 'G1'), ('Tom', 'G3')) and (('Tom', 'G3'), ('Tessa', 'G1')), the remaining will be one to two relationship like (('Evan', 'G2'), ('Maddie', 'G1')) and (('Lewis', 'G1'), ('Evan', 'G2')).

Here is how you can use the combinations method to get a list of all possible groups of 2, where the members of the group of 2 aren't from the same group:

from itertools import combinations

Members = [('Tessa','G1'),('Evan','G2'),('Tom','G3'),
           ('Mia','G3'),('Claire','G3'),('Billie','G4'),
           ('Adrian','G2'),('Maddie','G1'),('Lewis','G1'),
           ('Tony','G2'),('Joyce','G1'),('Julian','G5'),
           ('Joshua','G2'), ('Warren','G3')]

random = []
for a, b in combinations(Members, 2):
    if a[1] != b[1]:
        random.append((a, b))

print(random)

Output:

[(('Tessa', 'G1'), ('Evan', 'G2')), (('Tessa', 'G1'), ('Tom', 'G3')), (('Tessa', 'G1'), ('Mia', 'G3')), (('Tessa', 'G1'), ('Claire', 'G3')), (('Tessa', 'G1'), ('Billie', 'G4')), (('Tessa', 'G1'), ('Adrian', 'G2')), (('Tessa', 'G1'), ('Tony', 'G2')), (('Tessa', 'G1'), ('Julian', 'G5')), (('Tessa', 'G1'), ('Joshua', 'G2')), (('Tessa', 'G1'), ('Warren', 'G3')), (('Evan', 'G2'), ('Tom', 'G3')), (('Evan', 'G2'), ('Mia', 'G3')), (('Evan', 'G2'), ('Claire', 'G3')), (('Evan', 'G2'), ('Billie', 'G4')), (('Evan', 'G2'), ('Maddie', 'G1')), (('Evan', 'G2'), ('Lewis', 'G1')), (('Evan', 'G2'), ('Joyce', 'G1')), (('Evan', 'G2'), ('Julian', 'G5')), (('Evan', 'G2'), ('Warren', 'G3')), (('Tom', 'G3'), ('Billie', 'G4')), (('Tom', 'G3'), ('Adrian', 'G2')), (('Tom', 'G3'), ('Maddie', 'G1')), (('Tom', 'G3'), ('Lewis', 'G1')), (('Tom', 'G3'), ('Tony', 'G2')), (('Tom', 'G3'), ('Joyce', 'G1')), (('Tom', 'G3'), ('Julian', 'G5')), (('Tom', 'G3'), ('Joshua', 'G2')), (('Mia', 'G3'), ('Billie', 'G4')), (('Mia', 'G3'), ('Adrian', 'G2')), (('Mia', 'G3'), ('Maddie', 'G1')), (('Mia', 'G3'), ('Lewis', 'G1')), (('Mia', 'G3'), ('Tony', 'G2')), (('Mia', 'G3'), ('Joyce', 'G1')), (('Mia', 'G3'), ('Julian', 'G5')), (('Mia', 'G3'), ('Joshua', 'G2')), (('Claire', 'G3'), ('Billie', 'G4')), (('Claire', 'G3'), ('Adrian', 'G2')), (('Claire', 'G3'), ('Maddie', 'G1')), (('Claire', 'G3'), ('Lewis', 'G1')), (('Claire', 'G3'), ('Tony', 'G2')), (('Claire', 'G3'), ('Joyce', 'G1')), (('Claire', 'G3'), ('Julian', 'G5')), (('Claire', 'G3'), ('Joshua', 'G2')), (('Billie', 'G4'), ('Adrian', 'G2')), (('Billie', 'G4'), ('Maddie', 'G1')), (('Billie', 'G4'), ('Lewis', 'G1')), (('Billie', 'G4'), ('Tony', 'G2')), (('Billie', 'G4'), ('Joyce', 'G1')), (('Billie', 'G4'), ('Julian', 'G5')), (('Billie', 'G4'), ('Joshua', 'G2')), (('Billie', 'G4'), ('Warren', 'G3')), (('Adrian', 'G2'), ('Maddie', 'G1')), (('Adrian', 'G2'), ('Lewis', 'G1')), (('Adrian', 'G2'), ('Joyce', 'G1')), (('Adrian', 'G2'), ('Julian', 'G5')), (('Adrian', 'G2'), ('Warren', 'G3')), (('Maddie', 'G1'), ('Tony', 'G2')), (('Maddie', 'G1'), ('Julian', 'G5')), (('Maddie', 'G1'), ('Joshua', 'G2')), (('Maddie', 'G1'), ('Warren', 'G3')), (('Lewis', 'G1'), ('Tony', 'G2')), (('Lewis', 'G1'), ('Julian', 'G5')), (('Lewis', 'G1'), ('Joshua', 'G2')), (('Lewis', 'G1'), ('Warren', 'G3')), (('Tony', 'G2'), ('Joyce', 'G1')), (('Tony', 'G2'), ('Julian', 'G5')), (('Tony', 'G2'), ('Warren', 'G3')), (('Joyce', 'G1'), ('Julian', 'G5')), (('Joyce', 'G1'), ('Joshua', 'G2')), (('Joyce', 'G1'), ('Warren', 'G3')), (('Julian', 'G5'), ('Joshua', 'G2')), (('Julian', 'G5'), ('Warren', 'G3')), (('Joshua', 'G2'), ('Warren', 'G3'))]

To me it looks like in this line

    while checkConstrainst(Perm,random(L)) == False:

you should not have random(L) just L. You are already randomizing your permutations

If you need to make 'L' random, either do it it right after you enter the function:

def randomMember(L):
    L = random(L)
    n = len(L)

or not have it at all, and call the function with randomized L:

...
randomMember(random(L))

The rationale being that in your original function you are checking permutations against a randomized L but then put couples together from the original L

Here's a way to do this. I have commented to give explanation of what I am doing in the program.

import random
Members = [('Tessa','G1'), ('Evan','G2'),  ('Tom','G3'),
           ('Mia','G3'),   ('Claire','G3'),('Billie','G4'),
           ('Adrian','G2'),('Maddie','G1'),('Lewis','G1'),
           ('Tony','G2'),  ('Joyce','G1'), ('Julian','G5'),
           ('Joshua','G2'),('Warren','G3')]

def randomMember(L):
    LCouples = []

    for memb1 in Members: #iterate thru each member of Members list

        #now create a list of all members that do not belong to same Group as memb1
        memb_others = [j for j in Members if j[1] != memb1[1]]

        #generate a random number between 0 and length of the new list of members
        r = random.randint(0,len(memb_others)-1)

        #pair them as couples and append to LCouples
        #the pairing will ensure no couple from same group are paired
        LCouples.append((memb1,memb_others[r]))

    #return the list of all randomly paired couples
    return LCouples

#print the full list of random members
print (randomMember(Members))

A few runs of this generated the following outputs:

Run 1:

[(('Tessa', 'G1'), ('Julian', 'G5')), (('Evan', 'G2'), ('Julian', 'G5')), (('Tom', 'G3'), ('Adrian', 'G2')), (('Mia', 'G3'), ('Tessa', 'G1')), (('Claire', 'G3'), ('Julian', 'G5')), (('Billie', 'G4'), ('Claire', 'G3')), (('Adrian', 'G2'), ('Warren', 'G3')), (('Maddie', 'G1'), ('Billie', 'G4')), (('Lewis', 'G1'), ('Warren', 'G3')), (('Tony', 'G2'), ('Julian', 'G5')), (('Joyce', 'G1'), ('Julian', 'G5')), (('Julian', 'G5'), ('Joshua', 'G2')), (('Joshua', 'G2'), ('Claire', 'G3')), (('Warren', 'G3'), ('Tessa', 'G1'))]

Run 2:

[(('Tessa', 'G1'), ('Adrian', 'G2')), (('Evan', 'G2'), ('Claire', 'G3')), (('Tom', 'G3'), ('Tessa', 'G1')), (('Mia', 'G3'), ('Evan', 'G2')), (('Claire', 'G3'), ('Maddie', 'G1')), (('Billie', 'G4'), ('Joshua', 'G2')), (('Adrian', 'G2'), ('Claire', 'G3')), (('Maddie', 'G1'), ('Billie', 'G4')), (('Lewis', 'G1'), ('Billie', 'G4')), (('Tony', 'G2'), ('Mia', 'G3')), (('Joyce', 'G1'), ('Mia', 'G3')), (('Julian', 'G5'), ('Joshua', 'G2')), (('Joshua', 'G2'), ('Tessa', 'G1')), (('Warren', 'G3'), ('Julian', 'G5'))]

Run 3:

[(('Tessa', 'G1'), ('Warren', 'G3')), (('Evan', 'G2'), ('Lewis', 'G1')), (('Tom', 'G3'), ('Lewis', 'G1')), (('Mia', 'G3'), ('Joyce', 'G1')), (('Claire', 'G3'), ('Julian', 'G5')), (('Billie', 'G4'), ('Lewis', 'G1')), (('Adrian', 'G2'), ('Warren', 'G3')), (('Maddie', 'G1'), ('Tony', 'G2')), (('Lewis', 'G1'), ('Mia', 'G3')), (('Tony', 'G2'), ('Maddie', 'G1')), (('Joyce', 'G1'), ('Warren', 'G3')), (('Julian', 'G5'), ('Billie', 'G4')), (('Joshua', 'G2'), ('Warren', 'G3')), (('Warren', 'G3'), ('Julian', 'G5'))]
Related