Performance of stable marriage solution in Python 3
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
6
down vote
favorite
I am trying to solve the stable marriage problem in SPOJ in Python 3.
There are given $n$ men and $n$ women. Each woman ranks all men in order of her preference (her first choice, her second choice, and so on). Similarly, each man sorts all women according to his preference. The goal is to arrange $n$ marriages in such a way that if a man $m$ prefers some woman $w$ more than his wife, then $w$ likes her husband more than $m$. In this way, no one leaves his partner to marry somebody else. This problem always has a solution and your task is to find one.
Input
The first line contains a positive integer $t le 100$ indicating the number of test cases. Each test case is an instance of the stable marriage problem defined above. The first line of each test case is a positive integer $n le 500$ (the number of marriages to find). The next $n$ lines are the woman's preferences: $i$th line contains the number $i$ (which means that this is the list given by the $i$th woman) and the numbers of men (the first choice of $i$th woman, the second choice,...). Then, the men's preferences follow in the same format.
Output
For each test case print $n$ lines, where each line contains two numbers $m$ and $w$, which means that the man number $m$ and the woman number $w$ should get married.
I have tried optimising the code as much as I can (remove slicing, keep it minimal array, remove printing one by one... etc).
But by far the best code I have been able to get runs in 0.13(s?) time and 33M(??) memory. But the best code for the same problem in Python 3 (submitted by @_@) runs in 0.09 time and 13M memory. So I would like suggestions on how to attain the best time and space usage with my code
from sys import stdin, stdout
def findWoman(manArray, womanArray, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = womanArray[woman - 1]
if(hub == 0):
womanArray[woman - 1] = index
manArray[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
manArray[hub - 1] = 0
womanArray[woman - 1] = index
manArray[index - 1] = woman
return hub
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(0, n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(0, n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
manArray = [0 for _ in range(n)]
womanArray = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = findWoman(manArray, womanArray, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(manArray[k]) + 'n'
stdout.write(out)
python performance algorithm python-3.x programming-challenge
add a comment |Â
up vote
6
down vote
favorite
I am trying to solve the stable marriage problem in SPOJ in Python 3.
There are given $n$ men and $n$ women. Each woman ranks all men in order of her preference (her first choice, her second choice, and so on). Similarly, each man sorts all women according to his preference. The goal is to arrange $n$ marriages in such a way that if a man $m$ prefers some woman $w$ more than his wife, then $w$ likes her husband more than $m$. In this way, no one leaves his partner to marry somebody else. This problem always has a solution and your task is to find one.
Input
The first line contains a positive integer $t le 100$ indicating the number of test cases. Each test case is an instance of the stable marriage problem defined above. The first line of each test case is a positive integer $n le 500$ (the number of marriages to find). The next $n$ lines are the woman's preferences: $i$th line contains the number $i$ (which means that this is the list given by the $i$th woman) and the numbers of men (the first choice of $i$th woman, the second choice,...). Then, the men's preferences follow in the same format.
Output
For each test case print $n$ lines, where each line contains two numbers $m$ and $w$, which means that the man number $m$ and the woman number $w$ should get married.
I have tried optimising the code as much as I can (remove slicing, keep it minimal array, remove printing one by one... etc).
But by far the best code I have been able to get runs in 0.13(s?) time and 33M(??) memory. But the best code for the same problem in Python 3 (submitted by @_@) runs in 0.09 time and 13M memory. So I would like suggestions on how to attain the best time and space usage with my code
from sys import stdin, stdout
def findWoman(manArray, womanArray, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = womanArray[woman - 1]
if(hub == 0):
womanArray[woman - 1] = index
manArray[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
manArray[hub - 1] = 0
womanArray[woman - 1] = index
manArray[index - 1] = woman
return hub
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(0, n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(0, n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
manArray = [0 for _ in range(n)]
womanArray = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = findWoman(manArray, womanArray, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(manArray[k]) + 'n'
stdout.write(out)
python performance algorithm python-3.x programming-challenge
@BillalBEGUERADJ Haha agreed :)
â Nannan AV
Jan 28 at 8:37
It looks like you're writing FORTRAN in Python. I'll try to find some time to write a more Pythonic solution.
â Eric Duminil
Jan 28 at 12:17
The obvious place where time is lost is in the the calls to theindex
method infindWoman
which take time proportional to the length of the list. These could be sped up by making reverse lookup tables.
â Gareth Rees
Jan 28 at 12:20
@GarethRees Ok, let me check what reverse lookup tables are and how to implement them!
â Nannan AV
Jan 30 at 18:12
add a comment |Â
up vote
6
down vote
favorite
up vote
6
down vote
favorite
I am trying to solve the stable marriage problem in SPOJ in Python 3.
There are given $n$ men and $n$ women. Each woman ranks all men in order of her preference (her first choice, her second choice, and so on). Similarly, each man sorts all women according to his preference. The goal is to arrange $n$ marriages in such a way that if a man $m$ prefers some woman $w$ more than his wife, then $w$ likes her husband more than $m$. In this way, no one leaves his partner to marry somebody else. This problem always has a solution and your task is to find one.
Input
The first line contains a positive integer $t le 100$ indicating the number of test cases. Each test case is an instance of the stable marriage problem defined above. The first line of each test case is a positive integer $n le 500$ (the number of marriages to find). The next $n$ lines are the woman's preferences: $i$th line contains the number $i$ (which means that this is the list given by the $i$th woman) and the numbers of men (the first choice of $i$th woman, the second choice,...). Then, the men's preferences follow in the same format.
Output
For each test case print $n$ lines, where each line contains two numbers $m$ and $w$, which means that the man number $m$ and the woman number $w$ should get married.
I have tried optimising the code as much as I can (remove slicing, keep it minimal array, remove printing one by one... etc).
But by far the best code I have been able to get runs in 0.13(s?) time and 33M(??) memory. But the best code for the same problem in Python 3 (submitted by @_@) runs in 0.09 time and 13M memory. So I would like suggestions on how to attain the best time and space usage with my code
from sys import stdin, stdout
def findWoman(manArray, womanArray, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = womanArray[woman - 1]
if(hub == 0):
womanArray[woman - 1] = index
manArray[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
manArray[hub - 1] = 0
womanArray[woman - 1] = index
manArray[index - 1] = woman
return hub
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(0, n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(0, n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
manArray = [0 for _ in range(n)]
womanArray = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = findWoman(manArray, womanArray, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(manArray[k]) + 'n'
stdout.write(out)
python performance algorithm python-3.x programming-challenge
I am trying to solve the stable marriage problem in SPOJ in Python 3.
There are given $n$ men and $n$ women. Each woman ranks all men in order of her preference (her first choice, her second choice, and so on). Similarly, each man sorts all women according to his preference. The goal is to arrange $n$ marriages in such a way that if a man $m$ prefers some woman $w$ more than his wife, then $w$ likes her husband more than $m$. In this way, no one leaves his partner to marry somebody else. This problem always has a solution and your task is to find one.
Input
The first line contains a positive integer $t le 100$ indicating the number of test cases. Each test case is an instance of the stable marriage problem defined above. The first line of each test case is a positive integer $n le 500$ (the number of marriages to find). The next $n$ lines are the woman's preferences: $i$th line contains the number $i$ (which means that this is the list given by the $i$th woman) and the numbers of men (the first choice of $i$th woman, the second choice,...). Then, the men's preferences follow in the same format.
Output
For each test case print $n$ lines, where each line contains two numbers $m$ and $w$, which means that the man number $m$ and the woman number $w$ should get married.
I have tried optimising the code as much as I can (remove slicing, keep it minimal array, remove printing one by one... etc).
But by far the best code I have been able to get runs in 0.13(s?) time and 33M(??) memory. But the best code for the same problem in Python 3 (submitted by @_@) runs in 0.09 time and 13M memory. So I would like suggestions on how to attain the best time and space usage with my code
from sys import stdin, stdout
def findWoman(manArray, womanArray, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = womanArray[woman - 1]
if(hub == 0):
womanArray[woman - 1] = index
manArray[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
manArray[hub - 1] = 0
womanArray[woman - 1] = index
manArray[index - 1] = woman
return hub
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(0, n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(0, n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
manArray = [0 for _ in range(n)]
womanArray = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = findWoman(manArray, womanArray, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(manArray[k]) + 'n'
stdout.write(out)
python performance algorithm python-3.x programming-challenge
edited Jan 28 at 10:51
Gareth Rees
41.1k394168
41.1k394168
asked Jan 28 at 8:29
Nannan AV
614
614
@BillalBEGUERADJ Haha agreed :)
â Nannan AV
Jan 28 at 8:37
It looks like you're writing FORTRAN in Python. I'll try to find some time to write a more Pythonic solution.
â Eric Duminil
Jan 28 at 12:17
The obvious place where time is lost is in the the calls to theindex
method infindWoman
which take time proportional to the length of the list. These could be sped up by making reverse lookup tables.
â Gareth Rees
Jan 28 at 12:20
@GarethRees Ok, let me check what reverse lookup tables are and how to implement them!
â Nannan AV
Jan 30 at 18:12
add a comment |Â
@BillalBEGUERADJ Haha agreed :)
â Nannan AV
Jan 28 at 8:37
It looks like you're writing FORTRAN in Python. I'll try to find some time to write a more Pythonic solution.
â Eric Duminil
Jan 28 at 12:17
The obvious place where time is lost is in the the calls to theindex
method infindWoman
which take time proportional to the length of the list. These could be sped up by making reverse lookup tables.
â Gareth Rees
Jan 28 at 12:20
@GarethRees Ok, let me check what reverse lookup tables are and how to implement them!
â Nannan AV
Jan 30 at 18:12
@BillalBEGUERADJ Haha agreed :)
â Nannan AV
Jan 28 at 8:37
@BillalBEGUERADJ Haha agreed :)
â Nannan AV
Jan 28 at 8:37
It looks like you're writing FORTRAN in Python. I'll try to find some time to write a more Pythonic solution.
â Eric Duminil
Jan 28 at 12:17
It looks like you're writing FORTRAN in Python. I'll try to find some time to write a more Pythonic solution.
â Eric Duminil
Jan 28 at 12:17
The obvious place where time is lost is in the the calls to the
index
method in findWoman
which take time proportional to the length of the list. These could be sped up by making reverse lookup tables.â Gareth Rees
Jan 28 at 12:20
The obvious place where time is lost is in the the calls to the
index
method in findWoman
which take time proportional to the length of the list. These could be sped up by making reverse lookup tables.â Gareth Rees
Jan 28 at 12:20
@GarethRees Ok, let me check what reverse lookup tables are and how to implement them!
â Nannan AV
Jan 30 at 18:12
@GarethRees Ok, let me check what reverse lookup tables are and how to implement them!
â Nannan AV
Jan 30 at 18:12
add a comment |Â
3 Answers
3
active
oldest
votes
up vote
6
down vote
I feel lazy to go through the details of your code, but here are few notes you should think about:
Your code, as it is, is not scalable. You should re-design it in terms of functions (I think functions are enough for your case, even in some other situations OOP may be a better choice). Functions allow also your code to be reused.
In terms of UX, I think you have some more work to do. For instance, when I run your code, I was waiting for something to happen, until I guessed to type in something, but then I had to check your code to see what it expects me to code. That is bad: imagine every software you use, you have to read its code to guess what you have to write or where to click.
You should throw a look to PEP 8 (for example, the naming conventions you use are camelCase, Python developers do not like that and you have to comply to the philosophy of Python)
Interesting if you throw a glance to: What does if __name__ == âÂÂ__main__âÂÂ: do?
That is the big picture I want to share with you for the moment ... maybe I can come back later to provide more useful help, or maybe other members may dive deeper into your code.
2
Well this is a problem on Spoj (I have added the hyperlink in the question now), so typing in comments would give me a wrong answer. I just started coding in Python (originally a JAVA dev) and hence the CamelCases. Will definitely change to Python philosophy when coding in Python though. Do agree with your other points. Thank you :)
â Nannan AV
Jan 28 at 9:04
Also it would be great if you could give the source for Point 4
â Nannan AV
Jan 28 at 10:00
2
It is okay to dofrom sys import stdin, stdout
, according to PEP8. It is discouraged to doimport sys, os
, though.
â Graipher
Jan 28 at 10:44
1
Also, hard-coded input formats like this are usually the only way to beat these competitions. Having a nice argument parser just adds runtime.
â Graipher
Jan 28 at 10:46
1
thank you very much for the note about theimport
@Graipher
â Billal BEGUERADJ
Jan 28 at 10:47
add a comment |Â
up vote
3
down vote
I have updated the code based on @Billal's suggestions and python code runs faster in a function
time: 0.12
memory: 33M
from sys import stdin, stdout
def find_woman(man_array, woman_array, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = woman_array[woman - 1]
if(hub == 0):
woman_array[woman - 1] = index
man_array[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
man_array[hub - 1] = 0
woman_array[woman - 1] = index
man_array[index - 1] = woman
return hub
def main():
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
man_array = [0 for _ in range(n)]
woman_array = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = find_woman(man_array, woman_array, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(man_array[k]) + 'n'
stdout.write(out)
if(__name__ == "__main__"):
main()
add a comment |Â
up vote
0
down vote
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
Actual algorithm
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)
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, '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)
I adapted my earlier version slightly
1 3
2 2
3 1
4 4
1 4
2 5
3 1
4 3
5 7
6 6
7 2
NB: for Python 3.6, you can use dict
instead of OrderedDict
, but that is an implementation detail
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
Since I''m relatively new to Python, theres a lot of new stuff in your answer for me. I will go through them slowly and give my update. Thank u :)
â Nannan AV
Jan 30 at 7:41
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
I feel lazy to go through the details of your code, but here are few notes you should think about:
Your code, as it is, is not scalable. You should re-design it in terms of functions (I think functions are enough for your case, even in some other situations OOP may be a better choice). Functions allow also your code to be reused.
In terms of UX, I think you have some more work to do. For instance, when I run your code, I was waiting for something to happen, until I guessed to type in something, but then I had to check your code to see what it expects me to code. That is bad: imagine every software you use, you have to read its code to guess what you have to write or where to click.
You should throw a look to PEP 8 (for example, the naming conventions you use are camelCase, Python developers do not like that and you have to comply to the philosophy of Python)
Interesting if you throw a glance to: What does if __name__ == âÂÂ__main__âÂÂ: do?
That is the big picture I want to share with you for the moment ... maybe I can come back later to provide more useful help, or maybe other members may dive deeper into your code.
2
Well this is a problem on Spoj (I have added the hyperlink in the question now), so typing in comments would give me a wrong answer. I just started coding in Python (originally a JAVA dev) and hence the CamelCases. Will definitely change to Python philosophy when coding in Python though. Do agree with your other points. Thank you :)
â Nannan AV
Jan 28 at 9:04
Also it would be great if you could give the source for Point 4
â Nannan AV
Jan 28 at 10:00
2
It is okay to dofrom sys import stdin, stdout
, according to PEP8. It is discouraged to doimport sys, os
, though.
â Graipher
Jan 28 at 10:44
1
Also, hard-coded input formats like this are usually the only way to beat these competitions. Having a nice argument parser just adds runtime.
â Graipher
Jan 28 at 10:46
1
thank you very much for the note about theimport
@Graipher
â Billal BEGUERADJ
Jan 28 at 10:47
add a comment |Â
up vote
6
down vote
I feel lazy to go through the details of your code, but here are few notes you should think about:
Your code, as it is, is not scalable. You should re-design it in terms of functions (I think functions are enough for your case, even in some other situations OOP may be a better choice). Functions allow also your code to be reused.
In terms of UX, I think you have some more work to do. For instance, when I run your code, I was waiting for something to happen, until I guessed to type in something, but then I had to check your code to see what it expects me to code. That is bad: imagine every software you use, you have to read its code to guess what you have to write or where to click.
You should throw a look to PEP 8 (for example, the naming conventions you use are camelCase, Python developers do not like that and you have to comply to the philosophy of Python)
Interesting if you throw a glance to: What does if __name__ == âÂÂ__main__âÂÂ: do?
That is the big picture I want to share with you for the moment ... maybe I can come back later to provide more useful help, or maybe other members may dive deeper into your code.
2
Well this is a problem on Spoj (I have added the hyperlink in the question now), so typing in comments would give me a wrong answer. I just started coding in Python (originally a JAVA dev) and hence the CamelCases. Will definitely change to Python philosophy when coding in Python though. Do agree with your other points. Thank you :)
â Nannan AV
Jan 28 at 9:04
Also it would be great if you could give the source for Point 4
â Nannan AV
Jan 28 at 10:00
2
It is okay to dofrom sys import stdin, stdout
, according to PEP8. It is discouraged to doimport sys, os
, though.
â Graipher
Jan 28 at 10:44
1
Also, hard-coded input formats like this are usually the only way to beat these competitions. Having a nice argument parser just adds runtime.
â Graipher
Jan 28 at 10:46
1
thank you very much for the note about theimport
@Graipher
â Billal BEGUERADJ
Jan 28 at 10:47
add a comment |Â
up vote
6
down vote
up vote
6
down vote
I feel lazy to go through the details of your code, but here are few notes you should think about:
Your code, as it is, is not scalable. You should re-design it in terms of functions (I think functions are enough for your case, even in some other situations OOP may be a better choice). Functions allow also your code to be reused.
In terms of UX, I think you have some more work to do. For instance, when I run your code, I was waiting for something to happen, until I guessed to type in something, but then I had to check your code to see what it expects me to code. That is bad: imagine every software you use, you have to read its code to guess what you have to write or where to click.
You should throw a look to PEP 8 (for example, the naming conventions you use are camelCase, Python developers do not like that and you have to comply to the philosophy of Python)
Interesting if you throw a glance to: What does if __name__ == âÂÂ__main__âÂÂ: do?
That is the big picture I want to share with you for the moment ... maybe I can come back later to provide more useful help, or maybe other members may dive deeper into your code.
I feel lazy to go through the details of your code, but here are few notes you should think about:
Your code, as it is, is not scalable. You should re-design it in terms of functions (I think functions are enough for your case, even in some other situations OOP may be a better choice). Functions allow also your code to be reused.
In terms of UX, I think you have some more work to do. For instance, when I run your code, I was waiting for something to happen, until I guessed to type in something, but then I had to check your code to see what it expects me to code. That is bad: imagine every software you use, you have to read its code to guess what you have to write or where to click.
You should throw a look to PEP 8 (for example, the naming conventions you use are camelCase, Python developers do not like that and you have to comply to the philosophy of Python)
Interesting if you throw a glance to: What does if __name__ == âÂÂ__main__âÂÂ: do?
That is the big picture I want to share with you for the moment ... maybe I can come back later to provide more useful help, or maybe other members may dive deeper into your code.
edited Jan 29 at 4:41
Anthony Sottile
1031
1031
answered Jan 28 at 8:49
Billal BEGUERADJ
1
1
2
Well this is a problem on Spoj (I have added the hyperlink in the question now), so typing in comments would give me a wrong answer. I just started coding in Python (originally a JAVA dev) and hence the CamelCases. Will definitely change to Python philosophy when coding in Python though. Do agree with your other points. Thank you :)
â Nannan AV
Jan 28 at 9:04
Also it would be great if you could give the source for Point 4
â Nannan AV
Jan 28 at 10:00
2
It is okay to dofrom sys import stdin, stdout
, according to PEP8. It is discouraged to doimport sys, os
, though.
â Graipher
Jan 28 at 10:44
1
Also, hard-coded input formats like this are usually the only way to beat these competitions. Having a nice argument parser just adds runtime.
â Graipher
Jan 28 at 10:46
1
thank you very much for the note about theimport
@Graipher
â Billal BEGUERADJ
Jan 28 at 10:47
add a comment |Â
2
Well this is a problem on Spoj (I have added the hyperlink in the question now), so typing in comments would give me a wrong answer. I just started coding in Python (originally a JAVA dev) and hence the CamelCases. Will definitely change to Python philosophy when coding in Python though. Do agree with your other points. Thank you :)
â Nannan AV
Jan 28 at 9:04
Also it would be great if you could give the source for Point 4
â Nannan AV
Jan 28 at 10:00
2
It is okay to dofrom sys import stdin, stdout
, according to PEP8. It is discouraged to doimport sys, os
, though.
â Graipher
Jan 28 at 10:44
1
Also, hard-coded input formats like this are usually the only way to beat these competitions. Having a nice argument parser just adds runtime.
â Graipher
Jan 28 at 10:46
1
thank you very much for the note about theimport
@Graipher
â Billal BEGUERADJ
Jan 28 at 10:47
2
2
Well this is a problem on Spoj (I have added the hyperlink in the question now), so typing in comments would give me a wrong answer. I just started coding in Python (originally a JAVA dev) and hence the CamelCases. Will definitely change to Python philosophy when coding in Python though. Do agree with your other points. Thank you :)
â Nannan AV
Jan 28 at 9:04
Well this is a problem on Spoj (I have added the hyperlink in the question now), so typing in comments would give me a wrong answer. I just started coding in Python (originally a JAVA dev) and hence the CamelCases. Will definitely change to Python philosophy when coding in Python though. Do agree with your other points. Thank you :)
â Nannan AV
Jan 28 at 9:04
Also it would be great if you could give the source for Point 4
â Nannan AV
Jan 28 at 10:00
Also it would be great if you could give the source for Point 4
â Nannan AV
Jan 28 at 10:00
2
2
It is okay to do
from sys import stdin, stdout
, according to PEP8. It is discouraged to do import sys, os
, though.â Graipher
Jan 28 at 10:44
It is okay to do
from sys import stdin, stdout
, according to PEP8. It is discouraged to do import sys, os
, though.â Graipher
Jan 28 at 10:44
1
1
Also, hard-coded input formats like this are usually the only way to beat these competitions. Having a nice argument parser just adds runtime.
â Graipher
Jan 28 at 10:46
Also, hard-coded input formats like this are usually the only way to beat these competitions. Having a nice argument parser just adds runtime.
â Graipher
Jan 28 at 10:46
1
1
thank you very much for the note about the
import
@Graipherâ Billal BEGUERADJ
Jan 28 at 10:47
thank you very much for the note about the
import
@Graipherâ Billal BEGUERADJ
Jan 28 at 10:47
add a comment |Â
up vote
3
down vote
I have updated the code based on @Billal's suggestions and python code runs faster in a function
time: 0.12
memory: 33M
from sys import stdin, stdout
def find_woman(man_array, woman_array, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = woman_array[woman - 1]
if(hub == 0):
woman_array[woman - 1] = index
man_array[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
man_array[hub - 1] = 0
woman_array[woman - 1] = index
man_array[index - 1] = woman
return hub
def main():
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
man_array = [0 for _ in range(n)]
woman_array = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = find_woman(man_array, woman_array, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(man_array[k]) + 'n'
stdout.write(out)
if(__name__ == "__main__"):
main()
add a comment |Â
up vote
3
down vote
I have updated the code based on @Billal's suggestions and python code runs faster in a function
time: 0.12
memory: 33M
from sys import stdin, stdout
def find_woman(man_array, woman_array, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = woman_array[woman - 1]
if(hub == 0):
woman_array[woman - 1] = index
man_array[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
man_array[hub - 1] = 0
woman_array[woman - 1] = index
man_array[index - 1] = woman
return hub
def main():
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
man_array = [0 for _ in range(n)]
woman_array = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = find_woman(man_array, woman_array, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(man_array[k]) + 'n'
stdout.write(out)
if(__name__ == "__main__"):
main()
add a comment |Â
up vote
3
down vote
up vote
3
down vote
I have updated the code based on @Billal's suggestions and python code runs faster in a function
time: 0.12
memory: 33M
from sys import stdin, stdout
def find_woman(man_array, woman_array, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = woman_array[woman - 1]
if(hub == 0):
woman_array[woman - 1] = index
man_array[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
man_array[hub - 1] = 0
woman_array[woman - 1] = index
man_array[index - 1] = woman
return hub
def main():
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
man_array = [0 for _ in range(n)]
woman_array = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = find_woman(man_array, woman_array, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(man_array[k]) + 'n'
stdout.write(out)
if(__name__ == "__main__"):
main()
I have updated the code based on @Billal's suggestions and python code runs faster in a function
time: 0.12
memory: 33M
from sys import stdin, stdout
def find_woman(man_array, woman_array, index, mpref, wpref):
for woman in mpref[index - 1]:
if(woman == 0):
continue
hub = woman_array[woman - 1]
if(hub == 0):
woman_array[woman - 1] = index
man_array[index - 1] = woman
return 0
elif(wpref[woman - 1].index(index) > wpref[woman - 1].index(hub)):
continue
else:
man_array[hub - 1] = 0
woman_array[woman - 1] = index
man_array[index - 1] = woman
return hub
def main():
out = ''
t = int(stdin.readline())
while(t > 0):
t -= 1
n = int(stdin.readline())
mpref =
wpref =
for _ in range(n):
w = list(map(int, stdin.readline().split()))
w[0] = 0
wpref.append(w)
for _ in range(n):
m = list(map(int, stdin.readline().split()))
m[0] = 0
mpref.append(m)
man_array = [0 for _ in range(n)]
woman_array = [0 for _ in range(n)]
for k in range(n):
hub = k + 1
while(hub != 0):
hub = find_woman(man_array, woman_array, hub, mpref, wpref)
for k in range(n):
out += str(k + 1) + ' ' + str(man_array[k]) + 'n'
stdout.write(out)
if(__name__ == "__main__"):
main()
answered Jan 28 at 11:53
Nannan AV
614
614
add a comment |Â
add a comment |Â
up vote
0
down vote
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
Actual algorithm
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)
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, '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)
I adapted my earlier version slightly
1 3
2 2
3 1
4 4
1 4
2 5
3 1
4 3
5 7
6 6
7 2
NB: for Python 3.6, you can use dict
instead of OrderedDict
, but that is an implementation detail
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
Since I''m relatively new to Python, theres a lot of new stuff in your answer for me. I will go through them slowly and give my update. Thank u :)
â Nannan AV
Jan 30 at 7:41
add a comment |Â
up vote
0
down vote
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
Actual algorithm
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)
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, '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)
I adapted my earlier version slightly
1 3
2 2
3 1
4 4
1 4
2 5
3 1
4 3
5 7
6 6
7 2
NB: for Python 3.6, you can use dict
instead of OrderedDict
, but that is an implementation detail
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
Since I''m relatively new to Python, theres a lot of new stuff in your answer for me. I will go through them slowly and give my update. Thank u :)
â Nannan AV
Jan 30 at 7:41
add a comment |Â
up vote
0
down vote
up vote
0
down vote
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
Actual algorithm
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)
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, '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)
I adapted my earlier version slightly
1 3
2 2
3 1
4 4
1 4
2 5
3 1
4 3
5 7
6 6
7 2
NB: for Python 3.6, you can use dict
instead of OrderedDict
, but that is an implementation detail
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
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
Actual algorithm
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)
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, '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)
I adapted my earlier version slightly
1 3
2 2
3 1
4 4
1 4
2 5
3 1
4 3
5 7
6 6
7 2
NB: for Python 3.6, you can use dict
instead of OrderedDict
, but that is an implementation detail
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
edited Jan 29 at 11:26
answered Jan 29 at 10:22
Maarten Fabré
3,284214
3,284214
Since I''m relatively new to Python, theres a lot of new stuff in your answer for me. I will go through them slowly and give my update. Thank u :)
â Nannan AV
Jan 30 at 7:41
add a comment |Â
Since I''m relatively new to Python, theres a lot of new stuff in your answer for me. I will go through them slowly and give my update. Thank u :)
â Nannan AV
Jan 30 at 7:41
Since I''m relatively new to Python, theres a lot of new stuff in your answer for me. I will go through them slowly and give my update. Thank u :)
â Nannan AV
Jan 30 at 7:41
Since I''m relatively new to Python, theres a lot of new stuff in your answer for me. I will go through them slowly and give my update. Thank u :)
â Nannan AV
Jan 30 at 7:41
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186181%2fperformance-of-stable-marriage-solution-in-python-3%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
@BillalBEGUERADJ Haha agreed :)
â Nannan AV
Jan 28 at 8:37
It looks like you're writing FORTRAN in Python. I'll try to find some time to write a more Pythonic solution.
â Eric Duminil
Jan 28 at 12:17
The obvious place where time is lost is in the the calls to the
index
method infindWoman
which take time proportional to the length of the list. These could be sped up by making reverse lookup tables.â Gareth Rees
Jan 28 at 12:20
@GarethRees Ok, let me check what reverse lookup tables are and how to implement them!
â Nannan AV
Jan 30 at 18:12