Skip to main content
added 2415 characters in body
Source Link
Maarten Fabré
  • 9.4k
  • 1
  • 15
  • 27

After some thinking, I've taken a stab at converting your algorithm to my data, and this is what I arrived at. The result forconverting the second testcase is still differentlist-based approach to a dict-based one

parsing people

changed the input to a lookup table:

def parse_people(input, people):
    for _ in range(people):
        person, *prefs = map(int, input().split())
        yield person, {k: i for i, k in enumerate(prefs)}
def find_partners(people, prefs_women, prefs_men):
    from collections import OrderedDict
    choices_women = OrderedDict(((key, None ) for key in prefs_women))
    choices_men = OrderedDict(((key, None) for key in prefs_men))
    
    for k in choices_men:
        hub = k
        while(hub is not None):
            hub = find_woman(choices_women, choices_men, hub, prefs_women, prefs_men)
#             print(hub, choices_women)
    return choices_women, choices_men
if(__name__ == "__main__"):
    input = iter(input_str.split('\n')).__next__
    from collections import OrderedDict
    test_cases = int(input())
    for _ in range(test_cases):
        people = int(input())
        women = OrderedDict(parse_people(input, people, 'woman', 'man'))
        men = OrderedDict(parse_people(input, people, 'man', 'woman'))
#         print('-----preferences: ', women, men)
        choices_women, choices_men = find_partners(people, menwomen, womenmen)
        
        for man, woman in choices_men.items():
            print(man, ' ', woman)
1   3
2   2
3   1
4   4
1   4
2   5
3   1
4   3
5   7
6   6
7   2

bughunting

Since the names of the men and women are the same, to hunt for a bug I changed the parse_people temporary to this:

def parse_people(input, people, sex1='man', sex2 = 'woman'):
    for _ in range(people):
        # person, *prefs = map(int, input().split())
        person, *prefs = input().split()
        # yield person, {k: i for i, k in enumerate(prefs)}
        yield sex1 + '_' + person, {sex2 + '_' + k: i for i, k in enumerate(prefs)}

for a more verbose output. This way I found out I had changed the order of women and men somewhere

I've taken a stab at converting your algorithm to my data, and this is what I arrived at. The result for the second testcase is still different

def find_partners(people, prefs_women, prefs_men):
    from collections import OrderedDict
    choices_women = OrderedDict(((key, None )for key in prefs_women))
    choices_men = OrderedDict(((key, None) for key in prefs_men))
    
    for k in choices_men:
        hub = k
        while(hub is not None):
            hub = find_woman(choices_women, choices_men, hub, prefs_women, prefs_men)
#             print(hub, choices_women)
    return choices_women, choices_men
if(__name__ == "__main__"):
    input = iter(input_str.split('\n')).__next__
    from collections import OrderedDict
    test_cases = int(input())
    for _ in range(test_cases):
        people = int(input())
        women = OrderedDict(parse_people(input, people))
        men = OrderedDict(parse_people(input, people))
        choices_women, choices_men = find_partners(people, men, women)
        
        for man, woman in choices_men.items():
            print(man, ' ', woman)

After some thinking, I've taken a stab at your algorithm, converting the list-based approach to a dict-based one

parsing people

changed the input to a lookup table:

def parse_people(input, people):
    for _ in range(people):
        person, *prefs = map(int, input().split())
        yield person, {k: i for i, k in enumerate(prefs)}
def find_partners(people, prefs_women, prefs_men):
    from collections import OrderedDict
    choices_women = OrderedDict(((key, None ) for key in prefs_women))
    choices_men = OrderedDict(((key, None) for key in prefs_men))
    
    for k in choices_men:
        hub = k
        while(hub is not None):
            hub = find_woman(choices_women, choices_men, hub, prefs_women, prefs_men)
#             print(hub, choices_women)
    return choices_women, choices_men
if(__name__ == "__main__"):
    input = iter(input_str.split('\n')).__next__
    from collections import OrderedDict
    test_cases = int(input())
    for _ in range(test_cases):
        people = int(input())
        women = OrderedDict(parse_people(input, people, 'woman', 'man'))
        men = OrderedDict(parse_people(input, people, 'man', 'woman'))
#         print('-----preferences: ', women, men)
        choices_women, choices_men = find_partners(people, women, men)
        
        for man, woman in choices_men.items():
            print(man, ' ', woman)
1   3
2   2
3   1
4   4
1   4
2   5
3   1
4   3
5   7
6   6
7   2

bughunting

Since the names of the men and women are the same, to hunt for a bug I changed the parse_people temporary to this:

def parse_people(input, people, sex1='man', sex2 = 'woman'):
    for _ in range(people):
        # person, *prefs = map(int, input().split())
        person, *prefs = input().split()
        # yield person, {k: i for i, k in enumerate(prefs)}
        yield sex1 + '_' + person, {sex2 + '_' + k: i for i, k in enumerate(prefs)}

for a more verbose output. This way I found out I had changed the order of women and men somewhere

added 2415 characters in body
Source Link
Maarten Fabré
  • 9.4k
  • 1
  • 15
  • 27

Actual algorithm

I've taken a stab at converting your algorithm to my data, and this is what I arrived at. The result for the second testcase is still different

Find the partners

the part in your main routine, the main difference is that I work with an OrderedDict as lookup table, instead of a list, and I use None as sentinel value instead of 0

def find_partners(people, prefs_women, prefs_men):
    from collections import OrderedDict
    choices_women = OrderedDict(((key, None )for key in prefs_women))
    choices_men = OrderedDict(((key, None) for key in prefs_men))
    
    for k in choices_men:
        hub = k
        while(hub is not None):
            hub = find_woman(choices_women, choices_men, hub, prefs_women, prefs_men)
#             print(hub, choices_women)
    return choices_women, choices_men

finding the woman

Here I just tried to convert your code to the different indexing and lookup. By changing the comparison, you can omit the continue statement

def find_woman(choices_women, choices_men, index_man, prefs_women, prefs_men):
    for woman in prefs_men[index_man]:
        hub = choices_women[woman]
        if hub is None:
            choices_women[woman] = index_man
            choices_men[index_man] = woman
            return None
        elif prefs_women[woman][index_man] <= prefs_women[woman][hub]:
            choices_men[hub] = None
            choices_women[woman] = index_man
            choices_men[index_man] = woman
            return hub

combining it

if(__name__ == "__main__"):
    input = iter(input_str.split('\n')).__next__
    from collections import OrderedDict
    test_cases = int(input())
    for _ in range(test_cases):
        people = int(input())
        women = OrderedDict(parse_people(input, people))
        men = OrderedDict(parse_people(input, people))
        choices_women, choices_men = find_partners(people, men, women)
        
        for man, woman in choices_men.items():
            print(man, ' ', woman)

I adapted my earlier version slightly

NB: for Python 3.6, you can use dict instead of OrderedDict, but that is an implementation detail

Actual algorithm

I've taken a stab at converting your algorithm to my data, and this is what I arrived at. The result for the second testcase is still different

Find the partners

the part in your main routine, the main difference is that I work with an OrderedDict as lookup table, instead of a list, and I use None as sentinel value instead of 0

def find_partners(people, prefs_women, prefs_men):
    from collections import OrderedDict
    choices_women = OrderedDict(((key, None )for key in prefs_women))
    choices_men = OrderedDict(((key, None) for key in prefs_men))
    
    for k in choices_men:
        hub = k
        while(hub is not None):
            hub = find_woman(choices_women, choices_men, hub, prefs_women, prefs_men)
#             print(hub, choices_women)
    return choices_women, choices_men

finding the woman

Here I just tried to convert your code to the different indexing and lookup. By changing the comparison, you can omit the continue statement

def find_woman(choices_women, choices_men, index_man, prefs_women, prefs_men):
    for woman in prefs_men[index_man]:
        hub = choices_women[woman]
        if hub is None:
            choices_women[woman] = index_man
            choices_men[index_man] = woman
            return None
        elif prefs_women[woman][index_man] <= prefs_women[woman][hub]:
            choices_men[hub] = None
            choices_women[woman] = index_man
            choices_men[index_man] = woman
            return hub

combining it

if(__name__ == "__main__"):
    input = iter(input_str.split('\n')).__next__
    from collections import OrderedDict
    test_cases = int(input())
    for _ in range(test_cases):
        people = int(input())
        women = OrderedDict(parse_people(input, people))
        men = OrderedDict(parse_people(input, people))
        choices_women, choices_men = find_partners(people, men, women)
        
        for man, woman in choices_men.items():
            print(man, ' ', woman)

I adapted my earlier version slightly

NB: for Python 3.6, you can use dict instead of OrderedDict, but that is an implementation detail

Source Link
Maarten Fabré
  • 9.4k
  • 1
  • 15
  • 27

Your code is too obscure for me, I can't seem to wrap my head around it on a Monday morning. What I do understand is the way you parse the inputs. That way I do this is overwrite te builtin input during testing.

Define the sample data

input_str = '''2
4
1 4 3 1 2
2 2 1 3 4
3 1 3 4 2
4 4 3 1 2
1 3 2 4 1
2 2 3 1 4
3 3 1 2 4
4 3 2 4 1
7
1 3 4 2 1 6 7 5
2 6 4 2 3 5 1 7
3 6 3 5 7 2 4 1
4 1 6 3 2 4 7 5
5 1 6 5 3 4 7 2
6 1 7 3 4 5 6 2
7 5 6 2 4 3 7 1
1 4 5 3 7 2 6 1
2 5 6 4 7 3 2 1
3 1 6 5 4 3 7 2
4 3 5 6 7 2 4 1
5 1 7 6 4 3 5 2
6 6 3 7 5 2 4 1
7 1 7 4 2 6 5 3'''

Define how a line of data is parsed

def parse_people(input, people):
    for _ in range(people):
        person, *prefs = map(int, input().split())
        yield person, prefs

combine it

if(__name__ == "__main__"):
    input = iter(input_str.split('\n')).__next__
    test_cases = int(input())
    for _ in range(test_cases):
        people = int(input())
        women = dict(parse_people(input, people))
        men = dict(parse_people(input, people))
        print(people, men, women)

Then all you need to do when submitting, is removing or commenting the input = iter(input_str.split('\n')).__next__

I've also adopted more descriptive variable names, and instead of

w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)

I work with a generator and a dict. The result of my parsin is:

 women
{1: [3, 4, 2, 1, 6, 7, 5],
 2: [6, 4, 2, 3, 5, 1, 7],
 3: [6, 3, 5, 7, 2, 4, 1],
 4: [1, 6, 3, 2, 4, 7, 5],
 5: [1, 6, 5, 3, 4, 7, 2],
 6: [1, 7, 3, 4, 5, 6, 2],
 7: [5, 6, 2, 4, 3, 7, 1]}

That way you don't have to do the if(woman == 0):, and a lot of the indexing becomes easier. 0 or 1- indexing, or even names for input will not make a difference like this.

indexing

Following Gareth Rees' comment, instead of returning lists, you could let parse_people yield a lookup table, changing the call to .index to a dict lookup

def parse_people(input, people):
    for _ in range(people):
        person, *prefs = map(int, input().split())
        yield person, {k: i for i, k in enumerate(prefs)}
women
{1: {1: 3, 2: 2, 3: 0, 4: 1, 5: 6, 6: 4, 7: 5},
 2: {1: 5, 2: 2, 3: 3, 4: 1, 5: 4, 6: 0, 7: 6},
 3: {1: 6, 2: 4, 3: 1, 4: 5, 5: 2, 6: 0, 7: 3},
 4: {1: 0, 2: 3, 3: 2, 4: 4, 5: 6, 6: 1, 7: 5},
 5: {1: 0, 2: 6, 3: 3, 4: 4, 5: 2, 6: 1, 7: 5},
 6: {1: 0, 2: 6, 3: 2, 4: 3, 5: 4, 6: 5, 7: 1},
 7: {1: 6, 2: 2, 3: 4, 4: 3, 5: 0, 6: 1, 7: 5}}