Commit aa5295a3 authored by Pierre-antoine Comby's avatar Pierre-antoine Comby

le dépot est propre

parents
__pycache__/
*.csv
""" This script contains both the standard and extended Gale-Shapley algorithms
for solving matching games.
A matching game is defined by two sets called suitors and reviewers. Each suitor
(and reviewer) has associated with it an ordered preference of the elements of
the corresponding set. A solution to a matching game is any mapping between the
set of suitors and reviewers.
"""
from collections import Counter
from copy import deepcopy
import numpy as np
def galeshapley(suitor_pref_dict, reviewer_pref_dict):
""" The Gale-Shapley algorithm. This is known to provide a unique, stable
suitor-optimal matching. The algorithm is as follows:
(1) Assign all suitors and reviewers to be unmatched.
(2) Take any unmatched suitor, s, and their most preferred reviewer, r.
- If r is unmatched, match s to r.
- Else, if r is matched, consider their current partner, r_partner.
- If r prefers s to r_partner, unmatch r_partner from r and
match s to r.
- Else, leave s unmatched and remove r from their preference
list.
(3) Go to (2) until all suitors are matched, then end.
Parameters
----------
suitor_pref_dict : dict
A dictionary with suitors as keys and their respective preference lists
as values
review_pref_dict : dict
A dictionary with reviewers as keys and their respective preference
lists as values
Returns
-------
matching : dict
The suitor-optimal (stable) matching with suitors as keys and the
reviewer they are matched with as values
"""
suitors = [s for s in suitor_pref_dict]
matching = {s: None for s in suitors}
while suitors != []:
s = suitors.pop(0)
r = suitor_pref_dict[s][0]
if r not in matching.values():
matching[s] = r
else:
for suitr, revwr in matching.items():
if revwr == r:
r_partner = suitr
if reviewer_pref_dict[r].index(s) < reviewer_pref_dict[r].index(
r_partner
):
matching[r_partner] = None
matching[s] = r
suitors.append(r_partner)
else:
suitor_pref_dict[s].remove(r)
suitors.append(s)
return matching
def get_free_residents(resident_prefs, matching):
""" Return a list of all residents who are currently unmatched but have a
non-empty preference list. """
return [
resident
for resident in resident_prefs
if resident_prefs[resident]
and not any([resident in match for match in matching.values()])
]
def get_worst_idx(hospital, hospital_prefs, matching):
""" Find the index of the worst resident currently assigned to `hospital`
according to their preferences. """
return max(
[
hospital_prefs[hospital].index(resident)
for resident in hospital_prefs[hospital]
if resident in matching[hospital]
]
)
def resident_hospital(resident_prefs, hospital_prefs, capacities):
""" Provide a stable, resident-optimal matching for the given instance of
HR using the algorithm set out in [Gale, Shapley 1962]. """
matching = {hospital: [] for hospital in hospital_prefs}
free_residents = get_free_residents(resident_prefs, matching)
while free_residents:
resident = free_residents[0]
hospital = resident_prefs[resident][0]
i = 0
while capacities[hospital] <= 0:
i += 1
hospital = resident_prefs[resident][i]
<<<<<<< HEAD
=======
>>>>>>> 7158617afbb760d3a35b62a2705688d11b9d0b1b
matching[hospital].append(resident)
if len(matching[hospital]) > capacities[hospital]:
worst = get_worst_idx(hospital, hospital_prefs, matching)
resident = hospital_prefs[hospital][worst]
matching[hospital].remove(resident)
if len(matching[hospital]) == capacities[hospital]:
worst = get_worst_idx(hospital, hospital_prefs, matching)
successors = hospital_prefs[hospital][worst + 1 :]
if successors:
for resident in successors:
hospital_prefs[hospital].remove(resident)
if hospital in resident_prefs[resident]:
resident_prefs[resident].remove(hospital)
free_residents = get_free_residents(resident_prefs, matching)
for hospital, matches in matching.items():
sorted_matches = sorted(matches, key=hospital_prefs[hospital].index)
matching[hospital] = sorted_matches
return matching
def hrt_super_res(resident_prefs, hospital_prefs, capacities):
""" Determine whether a super-stable, resident-optimal matching exists for
the given instance of HR. If so, return the matching. """
# ==================================
# Needs adjusting for ties in prefs.
# ==================================
matching = {h: [] for h in hospital_prefs.keys()}
fulls = {h: False for h in hospital_prefs.keys()}
free_residents = [r for r in resident_prefs.keys()]
while [r for r in free_residents if resident_prefs[r]]:
r = free_residents.pop(0)
r_prefs = resident_prefs[r]
h_best = r_prefs[0]
matching[h_best] += [r]
if len(matching[h_best]) > capacities[h_best]:
r_worst = hospital_prefs[h_best][-1]
if r_worst in matching[h_best]:
matching[h_best].remove(r_worst)
resident_prefs[r_worst].remove(h_best)
hospital_prefs[h_best].remove(r_worst)
if len(matching[h_best]) == capacities[h_best]:
fulls[h_best] = True
worst_idx = np.max(
[
hospital_prefs[h_best].index(resident)
for resident in hospital_prefs[h_best]
if resident in matching[h_best]
]
)
successors = hospital_prefs[h_best][worst_idx + 1 :]
if successors:
for resident in successors:
hospital_prefs[h_best].remove(resident)
resident_prefs[resident].remove(h_best)
resident_match_counts = Counter([tuple(res) for res in matching.values()])
if np.any(
[count > 1 for count in resident_match_counts.values()]
) or np.any(fulls.values()):
raise ValueError("No super-stable matching exists.")
return matching
#! /usr/bin/env python3
BUS = ['Nausi[car] de la vallee du [van]',
'Au [bus]cher',
'La grosse [caisse]',
'Ha[van]a Club',
'l houm[bus]',
'[Van]ted',
'Pernod-Ri[Car]d']
NB_BUS=8
class Bus(object):
"""Un bus contient des équipes , remplis par des 1A en fonction de leur réponse à un questionnaire
"""
def __init__(self, nom, places, places_2a,reponse_criteres,size_equipe):
self.nom = nom # nom du bus
self.criteres = reponse_criteres # critère et pondération
self.note = dict() # note de tous les 1A par rapport à ce bus.
self.rank_unA = []
self.passengers = []
self.old_passengers = []
self.size_team = [int(s) for s in size_equipe]
self.teams = []
self.places = places
self.places_2a = places_2a
self.places_1a = places-places_2a
def __repr__(self):
ret = self.nom
return"Bus_"+ret
def __str__(self):
return "Bus {}".format(self.nom)
def __str__(self):
return "Bus {}".format(self.nom)
def gen_note(self, list_unA):
for unA in list_unA:
score_unA = unA.scores_bus[self]
rank_unA = unA.rank_bus.index(self)
if rank_unA == NB_BUS-1:
note_bus = score_unA/rank_unA
else:
#note_bus = (score_unA-unA.scores_bus[unA.rank_bus[rank_unA+1]] + 2)/(rank_unA+1)
p = NB_BUS - rank_unA
nextu = unA.scores_bus[unA.rank_bus[rank_unA+1]]
tmp = ((NB_BUS - rank_unA) * score_unA - (NB_BUS - rank_unA - 1) * nextu) / (rank_unA + 1)
note_bus = tmp
#note_bus = (score_unA**p - next**(p - 1))
# note_bus = (score_unA-unA.scores_bus[unA.rank_bus[rank_unA+1]])
self.note[unA] = note_bus
def gen_rank(self):
self.rank_unA = list(self.note.items())
self.rank_unA = sorted(self.rank_unA,key=lambda x:x[1],reverse = True)
self.rank_unA = [r[0] for r in self.rank_unA]
def partial_rank(self, students):
return [student for student in self.rank_unA if student in students]
def make_teams(self):
print(self.nom)
n_chef_tot = sum(self.size_team)
n_equipe = len(self.size_team)
N = len(self.passengers)
size_goal = [0]*len(self.size_team)
for i in range(len(self.size_team)):
size_goal[i] = int(self.size_team[i]*N/n_chef_tot)+1
i = 0
places_equipe=sum(size_goal)
girls = [p for p in self.passengers
if p.infos["genre"] == 'F' or p.infos["genre"] == 'N']
not_girls = [p for p in self.passengers
if p.infos["genre"] == 'M']
#
# for e in size_goal:
# equipe = []
# for k in range(int(len(girls)*e/places_equipe)):
# if girls:
# equipe.append(girls.pop())
# for k in range(int(len(not_girls)*e/places_equipe)):
# if not_girls:
# equipe.append(not_girls.pop())
# print(len(equipe))
# self.teams.append(equipe)
i=0
self.teams=[[None]*s for s in size_goal]
while girls :
if None in self.teams[i]:
self.teams[i].append(girls.pop())
self.teams[i].remove(None)
i +=1; i %= n_equipe;
for i in range(len(self.size_team)):
if len(self.teams[i])-self.teams[i].count(None) == 1:
#Si une fille est seule on l'enlève.
g_alone=self.teams[i].pop()
self.teams[i].append(None)
next_ind = (i+1)%n_equipe
self.teams[next_ind].remove(None)
self.teams[next_ind].append(g_alone)
remain =True
while not_girls and remain:
if None in self.teams[i]:
self.teams[i].append(not_girls.pop())
self.teams[i].remove(None)
i +=1; i %= n_equipe;
#cleaned unused places
for l in self.teams:
while None in l:
l.remove(None)
#affichage
for k in range(len(self.size_team)):
print(self.size_team[k],size_goal[k],len(self.teams[k]),self.teams[k])
print(len(self.passengers),sum(size_goal))
def partial_rank(self, students):
return [student for student in self.rank_unA if student in students]
{
"aspique":[978,1004,1112,1042,1092,1083,1145,963,975,1059,934,931,1082,1046,1084,961,1097,1195,944,1106,952,942,1071,1134],
"chytem":[1002],
"fanfare":[947,997,929,930, 1066],
"hooligens":[1050,1006,962,1013],
"med":[951,956],
"saphire":[1118,1111],
"satelist":[1016,1114],
"vieux":[974, 1066,943]
}
#!/usr/bin/env python3
# -*- coding:utf8 -*
"""
Script pour le remplissages des 1A dans les bus du WEI
TODO :
- adapter parse_wiki
- géneration structure donnée
- implémentation algorithme
"""
import sys
import csv
from collections import OrderedDict
import copy
from unA import *
from bus import *
import parsewiki
import algorithms
import random
import json
def get_1A_from_csv(database):
list_unA = []
with open(database, newline='') as csvfile:
table = csv.DictReader(csvfile, delimiter=',')
for people in table:
info = {i: people[i] for i in INFOS}
if info["dept"] == 'A"2': # on aime pas les chimiste
info["dept"] = "A''2"
criteres = {c: int(people[c]) for c in CRITERES}
list_unA.append(UnA(info, criteres))
return list_unA
def get_bus_from_csv(database):
list_bus = []
with open(database, newline='') as csvfile:
table = csv.DictReader(csvfile, delimiter=',')
for bus in table:
nom = bus["bus"]
places_2a = int(bus["places_2a"])
places = int(bus["places"])
size_equipe=list(bus["equipes"])
criteres = {c: int(bus[c]) for c in CRITERES}
list_bus.append(Bus(nom,places,places_2a, criteres,size_equipe))
return list_bus
def rang_moyen(bus, bus_match):
s = 0
for eleve in bus_match:
s += eleve.rank_bus.index(bus) + 1
if len(bus_match) == 0:
return 0
return s / len(bus_match)
def note_moyenne(bus, bus_match):
s = 0
for eleve in bus_match:
s += eleve.scores_bus[bus]
if len(bus_match) == 0:
return 0
return s / len(bus_match)
def rang_moyen(bus, bus_match):
s = 0
for eleve in bus_match:
s += eleve.rank_bus.index(bus) + 1
if len(bus_match) == 0:
return 0
return s / len(bus_match)
def note_moyenne(bus, bus_match):
s = 0
for eleve in bus_match:
s += eleve.scores_bus[bus]
if len(bus_match) == 0:
return 0
return s / len(bus_match)
def main(unA_file, bus_file):
list_unA = get_1A_from_csv(unA_file)
#list_unA = []
#for x in list_unAB:
# if x.infos['genre'] == 'F':
# list_unA.append(x)
list_bus = get_bus_from_csv(bus_file)
for unA in list_unA:
unA.gen_score(list_bus)
unA.gen_rank()
for bus in list_bus:
random.shuffle(list_unA)
bus.gen_note(list_unA)
bus.gen_rank()
return list_bus, list_unA
def split_students(students, bus_places):
<<<<<<< HEAD
"""On remplis les bus au fur et à mesure """
=======
>>>>>>> 7158617afbb760d3a35b62a2705688d11b9d0b1b
bus_ranks = {bus: bus.partial_rank(students) for bus in list_bus}
students_ranks = {student: student.rank_bus for student in students}
return algorithms.resident_hospital(students_ranks, bus_ranks, bus_places)
def merge_bus(bus_matches_by_dpt):
final_match = {bus: [] for bus in list_bus}
for dpt in bus_matches_by_dpt:
for bus in bus_matches_by_dpt[dpt]:
match = bus_matches_by_dpt[dpt][bus]
final_match[bus].extend(match)
return final_match
<<<<<<< HEAD
def get_unA(idwei, students):
for s in students:
if int(s.infos["idwei"]) == idwei:
return s
def hardcode(file, bus_by_name,students):
with open(file) as config:
assigned =json.load(config)
# assignment = {bus_by_name[b]: list(map(lambda x:get_unA(x,students),assigned[b])) for b in assigned}
for b in assigned:
bus = bus_by_name[b]
bus.places_1a -= len(assigned[b])
to_place=[]
for idwei in assigned[b]:
u = get_unA(idwei,students)
if u != None:
to_place.append(u)
bus.passengers.extend(to_place)
for s in to_place:
try:
students.remove(s)
except:
print(s)
def make_repartition(list_bus,students):
list_dpt = set([s.infos["dept"] for s in students])
N = len(students)
places_by_bus = {bus:bus.places_1a for bus in list_bus}
part_students = [f for f in students if f.infos['genre'] == 'F']
n_filles = len(part_students)
# pas de fille dans le bus aspique cette année.
bus_places= {bus: int((bus.places_1a*n_filles)/N)+1 for bus in list_bus}
bus_matches_by_dpt = dict()
# on commence par répartir les filles qui sont considéré comme un département
bus_matches_by_dpt["filles"] = split_students(part_students, bus_places)
for bus in bus_matches_by_dpt["filles"]:
places_by_bus[bus] -= len(bus_matches_by_dpt["filles"][bus])
# pour le reste des départements.
for dpt in list_dpt:
random.shuffle(list_bus)
students = [s for s in students if s not in part_students]
part_students = [s for s in students if s.infos['dept'] == dpt]
places_need = len(part_students)
bus_places = {bus: 0 for bus in list_bus}
working_places = {bus: places_by_bus[bus] for bus in list_bus}
while places_need > 0:
for bus in list_bus:
if places_need > 0 and working_places[bus] > 0:
bus_places[bus] += 1
working_places[bus] -= 1
places_need -= 1
for bus in list_bus:
if bus_places[bus] == 0 and working_places[bus] > 0:
bus_places[bus] += 1
bus_matches_by_dpt[dpt] = split_students(part_students, bus_places)
for bus in bus_matches_by_dpt[dpt]:
places_by_bus[bus] -= len(bus_matches_by_dpt[dpt][bus])
final_match = merge_bus(bus_matches_by_dpt)
return final_match
def csv_bus_output(list_bus):
fieldnames= [
"nom",
"prenom",
"genre",
"dept",
"tel",
"urgence_nom",
"urgence_tel",
"mail",
"infos"
]
for bus in match:
with open("bus/"+bus.nom+".csv",'w+',newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames,delimiter=',')
writer.writeheader()
for s in bus.passengers:
to_write = {i:s.infos[i] for i in fieldnames}
# to_write.update({"rank_bus":s.rank_bus.index(bus),"fav_bus":s.rank_bus[0]})
writer.writerow(to_write)
def csv_team_output(list_bus):
for bus in list_bus:
infos = [
"nom",
"prenom",
"genre",
"dept",
"tel",
]
fieldnames = infos + ["equipe"]
with open("bus_team/"+bus.nom+".csv","w+",newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames,delimiter=',')
writer.writeheader()
for t in bus.teams:
for s in t:
to_write = {i:s.infos[i] for i in infos}
to_write["equipe"] = bus.teams.index(t)+1
writer.writerow(to_write)
list_bus, students = main("unA.csv", "bus.csv")
bus_by_name={bus.nom:bus for bus in list_bus} # pour harcoder sans se prendre la tete
print(len(students), [bus.places_1a for bus in list_bus], sum([bus.places_1a for bus in list_bus]))
hardcode("hardcode.json",bus_by_name,students)
print([len(bus.passengers) for bus in list_bus])
match = make_repartition(list_bus,students)
for bus in match:
bus.passengers.extend(match[bus])
bus.make_teams()
csv_bus_output(list_bus)
csv_team_output(list_bus)
n=0
m=0
for bus in list_bus:
n += bus.places
m += len(bus.passengers)+bus.places_2a
print(n,m)
=======
def latex_output(final_match):
print("\\documentclass[fontsize=12pt,twoside=false,parskip=half, french]{scrartcl}")
print("\\usepackage[utf8]{inputenc}")
print("\\usepackage[T1]{fontenc}")
print("\\usepackage{ltablex}")
print("\\usepackage{babel}")
print("\\usepackage{palatino}\n")
print("\\renewcommand{\\arraystretch}{1.5}\n")
print("\\begin{document}")
print("\\setlength{\\tabcolsep}{0.2cm}")
for bus in final_match:
print(" \\section{{{}}}".format(bus))
match = final_match[bus]
print(" \\begin{center}")
print(" \\begin{tabularx}{\\textwidth}{|l|l|} \\hline")
i = 0
for student in match:
if i % 2 == 0:
print(" {} &".format(student))
else:
print(" {} \\\\ \\hline".format(student))
i += 1
if i % 2 == 1:
print(" \\\\ \\hline")
print(" \\end{tabularx}")
print(" \\end{center}\n")
print("\\newpage")
print("\\end{document}")
#for dpt in bus_matches_by_dpt:
# print("\n\n\n#{}".format(dpt.upper()))
# for bus in bus_matches_by_dpt[dpt]:
# match = bus_matches_by_dpt[dpt][bus]
# print("\n {} {} {}\n".format(bus, rang_moyen(bus, match), note_moyenne(bus, match)))
# for student in match:
# print(" {}".format(student))
list_bus, students = main("test.csv", "bus_test.csv")
list_dpt = set([s.infos["dept"] for s in students])
places_by_bus = {bus: 43 for bus in list_bus}
bus_matches_by_dpt = {}
part_students = [f for f in students if f.infos['genre'] == 'F']
bus_places = {bus:int(2 + len(part_students)/7) for bus in list_bus}
bus_matches_by_dpt["filles"] = split_students(part_students, bus_places)
for bus in bus_matches_by_dpt["filles"]:
places_by_bus[bus] -= len(bus_matches_by_dpt["filles"][bus])
for dpt in list_dpt:
random.shuffle(list_bus)
students = [s for s in students if s not in part_students]
part_students = [s for s in students if s.infos['dept'] == dpt]
places_need = len(part_students)
bus_places = {bus: 0 for bus in list_bus}
working_places = {bus: places_by_bus[bus] for bus in list_bus}
while places_need > 0:
#student_by_bus = int(places_need / 7)
#for bus in list_bus:
# if places_need == 0:
# bus_places[bus] += 0
# if places_by_bus[bus] <= 0:
# bus_places[bus] += 0
# else:
# bus_places[bus] += min(places_by_bus[bus], student_by_bus)
# places_need -= bus_places[bus]
for bus in list_bus:
if places_need > 0 and working_places[bus] > 0:
bus_places[bus] += 1
working_places[bus] -= 1
places_need -= 1
for bus in list_bus:
if bus_places[bus] == 0 and working_places[bus] > 0:
bus_places[bus] += 1
bus_matches_by_dpt[dpt] = split_students(part_students, bus_places)
for bus in bus_matches_by_dpt[dpt]:
places_by_bus[bus] -= len(bus_matches_by_dpt[dpt][bus])
final_match = merge_bus(bus_matches_by_dpt)
latex_output(final_match)
#print("BUS RESULTS")
#for dpt in bus_matches_by_dpt:
# print("\n\n\n#{}".format(dpt.upper()))
# for bus in bus_matches_by_dpt[dpt]:
# match = bus_matches_by_dpt[dpt][bus]
# print("\n {} {} {}\n".format(bus, rang_moyen(bus, match), note_moyenne(bus, match)))
# for student in match:
# print(" {}".format(student))
#print("Name, average rank, avarage mark")
#for bus in final_match:
# match = final_match[bus]
# print("{} {} {}".format(bus, rang_moyen(bus, match), note_moyenne(bus, match)))
#print("\n\n\nStudent by dpt and bus\n\n")
#for dpt in bus_matches_by_dpt:
# print("\n\n\n#{}".format(dpt.upper()))
# for bus in bus_matches_by_dpt[dpt]:
# match = bus_matches_by_dpt[dpt][bus]
# print("\n {} {} {}".format(bus, rang_moyen(bus, match), note_moyenne(bus, match)))
# for student in match:
# print(" {}".format(student))
>>>>>>> 7158617afbb760d3a35b62a2705688d11b9d0b1b
#!/usr/bin/env python3
# -*- coding:utf-8 -*
"""Pour récupérer des infos à partir du wiki."""
import time
import re
#import urllib.request
import pprint
#: URL de la page wiki des bus
URL = "https://wiki.crans.org/OrganisationWei{}/Bus?action=raw".format(
time.localtime()[0])
FILE = "rawpage"
def get_raw():
"""Récupère le contenu de la page."""
return open(FILE).read()
# page = urllib.request.urlopen(URL)
# text = page.read().decode("utf-8")
return text
def parse_names(raw):
"""Récupère les tableaux de Bus (= un tableau après un titre commençant par '== Bus')."""
names = []
l = re.findall("== (?:Bi)?Bus.*==[^|]*\n((?:\|\|.*\|\|\n)+)", raw)
for table in l:
obj = re.search("Nom de l'équipe[^|]*\|\|", table)
after = table[obj.end():]
end = after.index("\n")
after = after[:end]
teams = re.findall("([^|]+)\|\|", after)
teams = [t.strip() for t in teams]
names.append(teams)
return names
def get_team_names():
raw = get_raw()
teams = parse_names(raw)
return teams
if __name__ == "__main__":
for t in get_team_names():
print(t)
##acl +GroupeBde:admin
#acl +GroupeBde:admin GroupeWei:read,write GroupeAdmin:read,write,revert,delete,admin All:
## Ajouter un # au début de la 1ère ligne et
## Retirer le 1er # de la ligne précédente pour rendre la page inaccessible aux 1As
= Les Bus du WEI 2018 =
Deadline : 30 juin 2018
* Lol. -- [[Wiki20-100|Un vieux con de GC WEI]] <<DateTime(2018-06-01T09:13:09+0200)>>
* Merci pour ta sagesse, nous sommes jeunes et naïfs -- WikiPagnyx <<DateTime(2018-06-02T11:00:41Z)>>
{{{#!wiki caution
L'accès de cette page est verrouillé pour ne pas spoiler le WEI aux 1As.
Ajoutez vos amis bloqués dehors au GroupeWei pour qu'ils puissent revenir !<