[1] Problema orarului școlar echilibrat și limbajul R
[2] V. Bazon - Orare școlare echilibrate și limbajul R https://books.google.ro/books?id=aWrDEAAAQBAJ
Prin vct_neunif()
obțineam distribuțiile rămase de omogenizat la profesorii (neimplicați în cuplaje) cu măcar 9 ore, iar prin swap_list()
puteam stabili ce interschimburi de clase (dintr-o zi în alta) ar fi de făcut între aceștia (dar numai între cei din vectorul returnat de vct_neunif()
); prin aceste interschimburi (sub regia interactivă oferită de recast_by_swap()
) am reușit anterior, să omogenizăm majoritatea acestor distribuții.
Dar sunt de „cizelat” și distribuții ale profesorilor încadrați pe mai puțin de 9 ore.
Se poate ca în R12
un total de 5 ore de exemplu, să fi fost distribuit câte una pe zi – ceea ce este corect în principiu, dacă lecțiile respective vizează o singură clasă; dar dacă este vorba de două clase de exemplu, cu 2 ore și respectiv 3 ore – atunci parcă mai principial este ca acele 5 ore să fie distribuite în doar trei zile (două zile cu câte 2 ore, una la prima clasă și cealaltă la a doua și a treia zi cu ora rămasă); iar 5 ore la clase diferite ar fi de repartizat numai în două zile, de exemplu (2 0 0 3 0)
.
Dacă pentru cei câțiva profesori încadrați pe câte un număr mic de ore, s-ar fi stabilit din start care zile să fie libere, după caz – atunci puteam constitui pentru ei un „orar prealabil” (analog celui constituit anterior în cls23_days.RDS
pentru lecțiile pe clase cuplate); noi n-am prevăzut un orar al zilelor libere, așa că ne-a rămas să vedem ce interschimbări facem pe distribuțiile individuale cu totaluri mai mici ca 9, încât să asigurăm desfășurarea lecțiilor respective, după caz, în cât mai puține zile.
Mai întâi adaptăm funcția vct_neunif()
pentru a obține tabelul acestor distribuții, existente în setul R12
curent (și includem noua funcție în brush.R
, unde avem și vectorul 'twins
'):
vct_lt9 <- function() { tpz <- table(R12[c('prof', 'zl')]) NU <- apply(tpz, 1, function(H) if(sum(H) < 9) H) %>% compact() %>% list2DF() Prof <- setdiff(names(NU), twins) # restrânge la neangajați în cuplaje NU %>% select(all_of(Prof)) } # neangajați în cuplaje, cu sub 9 ore fiecare
Încărcând brush.R
într-o nouă sesiune de lucru și invocând funcția de mai sus, am avut brusc surpriza că este afișată (cu 3 ore, câte una pe zi) și distribuția cuplajului LI1LI3
…
Primul lucru de verificat este conținutul vectorului 'twins
' (exclus mai mai sus din vectorul 'Prof
') și am constatat că LI1LI3
nu există în 'twins
'; apoi am afișat Tw2
– constatând că într-adevăr, nu conține cuplajul respectiv!
Mai important decât să ne uităm în urmă pentru a descoperi unde și când și din ce motiv s-a pierdut cheia LI1LI3
din dicționarul cuplajelor Tw2
– mai important este să ne dăm seama de consecințele actuale ale acestei scăpări. Investigând lecțiile prof|cls|zl
pentru profesorii din contextul acestui cuplaj, am constatat că din fericire, consecințele negative sunt minore: cele 3 lecții ale cuplajului LI1LI3
lipsesc din R2
, dar apar în R1
(încât dimensiunile notate anterior ale acestor seturi ar trebui corectate cu ±3) – cu alte cuvinte, ele au fost repartizate pe zile ca și lecțiile celor neangajați în cuplaje, prin mount_days_necup()
, în loc să fi fost ca și celelalte cuplaje, repartizate de mount_days_cup()
; fiind însă numai 3 lecții (la o aceeași clasă), nu s-au produs dereglări, de nici un fel (pentru niciunul dintre membrii cuplajelor).
Putem zice că am avut noroc, descoperind încă la timp, această scăpare – fiindcă dicționarele Tw12.Rda
vor juca un rol esențial când ne vom ocupa de repartizarea lecțiilor pe orele zilei; să reconstituim deci, Tw12.Rda
(adăugând și cheia care lipsea):
> Tw2$LI1LI3 <- c("LI1","LI3","LI2LI1","LI2LI3","Tc2LI1") > save(Tw1,Tw2,file="Tw12.Rda")
Reîncărcăm brush.R
și (… ca și cum nu s-ar fi întâmplat nimic) afișăm distribuțiile cu mai puțin de 9 ore, pentru profesori neangajați în cuplaje (nu și pentru LI1LI3
):
> vct_lt9() Ch3 Ch4 Fz4 Fz5 Gg3 IH1 Is4 LE4 LR8 Lt1 Mt7 Ps2 Sp3 Tc3 1 2 1 0 1 0 2 1 0 0 2 1 1 1 0 2 2 1 0 1 0 2 1 1 1 1 0 1 1 0 3 1 0 2 0 0 1 2 1 0 2 1 1 0 0 4 2 0 2 1 1 2 0 0 1 2 0 0 2 1 5 0 1 2 1 1 0 0 2 2 1 0 1 1 0
Vom avea de retușat distribuțiile unora dintre acești 14 profesori (de exemplu, nu vom retușa la Fz4
: distribuția celor 6 ore ale sale în 3 zile este de acceptat).
Formulăm o variantă mai generală a funcției swap_list()
; dat un profesor P
neangajat în cuplaje, căruia vrem să-i retușăm distribuția pe zile – căutăm profesorii P1
neangajați în cuplaje (dar fără alte condiționări, spre deosebire de swap_list()
) cu care P
ar putea schimba o clasă și o zi fără ca prin aceasta, distribuția lui P1
să fie „degradată” (să devină neomogenă, sau neconvenabilă acestuia):
Profs <- setdiff(unique(R1$prof), twins) # toți cei neangajați în cuplaje swap_list_all <- function(P, z1, z2) { qPz1 <- R12[R12$prof==P & R12$zl==z1, 2] # clasele lui P în z1 map(setdiff(Profs, P), function(P1) { ds2 <- R12 %>% filter(prof == P1) qP1z2 <- ds2[ds2$zl==z2, 2] # clasele lui P1 în z2 qP1z1 <- ds2[ds2$zl==z1, 2] # și clasele lui P1 în z1 if(length(qP1z2) < length(qP1z1)) # cu '<' în loc de '<=' return(NULL) c12 <- intersect(qPz1, qP1z2) if(length(c12) && length(intersect(c12, qP1z1)) < length(c12)) return(framedist(P1)) # distribuția pe zile a lecțiilor }) %>% compact() }
Bineînțeles că am plasat în afara funcției vectorul Profs
, al profesorilor neangajați în cuplaje (pentru a nu-l recalcula în fiecare apel al ei).
Anterior, ne rămăseseră 4 profesori P
cu distribuții neomogene (pe mai mult de 9 ore) pentru care swap_list()
nu găsea niciun P1
– fiindcă P1
era căutat numai între cei din vectorul returnat de vct_neunif()
; acum putem omogeniza și cele 4 distribuții: de exemplu, pentru LR1
distribuția curentă este (5 4 4 4 3)
iar swap_list_all("LR1", "Lu", "Vi")
produce o listă nevidă, cu 8 profesori P1
, din care putem alege de exemplu:
[[5]] Fz2 "12D 9A 9B" "11A 12D 9A" "10E 11A 12C 9B" "10E 12C 9B" "11A 12C 12D 9A"
Interschimbând 11A
(nu 9A
, deja existentă Lu
la Fz2
) între zilele 1 și 5 la cei doi profesori, distribuția lui LR1
devine uniformă, iar cea a lui Fz2
rămâne omogenă.
Desigur, a trebuit întâi să afișăm prin framedist()
distribuția pe zile și clase a lui LR1
, pentru a vedea între care zile să facem schimbul de clase – pentru a putea specifica parametrii necesari apelului swap_list_all()
și apoi, pentru a specifica în change_zl()
clasa de interschimbat.
Dar în loc de câte o secvență cu aceste trei comenzi (de fapt patru, fiindcă change_zl()
trebuie invocat pentru ambii profesori), este mult mai comod să folosim direct recast_by_swap()
, adăugându-i însă un parametru pentru specificarea funcției de utilizat:
recast_by_swap <- function(P, sw = FALSE) { swap <- ifelse(sw, swap_list_all, swap_list) # ... # # W <- swap_list(P, z1, z2) W <- swap(P, z1, z2) # ... # }
recast_by_swap(P)
(cu un singur argument) este exact funcția anterioară (care implica swap_list()
), iar recast_by_swap(P, 1)
va invoca varianta „generală” swap_list_all()
.
Folosind (pe repartiția curentă R12
) varianta generală, ne-a reușit (în 1-2 minute) să omogenizăm pe rând și cele 4 distribuții din vectorul dat de vct_neunif()
.
Redăm un exemplu de retușare a distribuțiilor pe mai puțin de 9 ore din vectorul returnat de vct_lt9()
– pentru Ch3
, care are 7 ore în 4 zile, la 3 clase (și am dori doar 3 zile):
> recast_by_swap("Ch3", 1) Ch3 11C 12D 11B 11C 12D 11C 12D # liber în ziua 5 Mutăm o clasă - din care Z1 în care Z2: 3 2 Interschimbăm cu unul dintre profesorii: 1: Sp1 10B 12B 6B 8B 10C 11E 12D 5A 9B 11C 5A 7B 8B 10B 10C 6B 9E 11A 12B 7B 9C 2: Mt4 11C 11D 9E 9F 11C 12D 9E 9F 11C 11D 12C 11D 12C 12D 11D 12A 12C 12D Mutăm una dintre clasele 12D din Ma în Mi la al câtelea profesor (0 to Cancel): 1 Clasa de interschimbat între Ch3 și Sp1: 12D După interschimbare: zl prof Lu Ma Mi Jo Vi Ch3 2 3 0 2 0 # cu două zile libere Sp1 4 4 5 4 4
Redăm, într-un format adecvat, distribuțiile ajustate (când a fost cazul) după modelul de lucru de mai sus, pentru profesorii cu mai puțin de 9 ore:
P9 <- names(vct_lt9()) lP9 <- map(P9, framedist) %>% setNames(p9) %>% list2DF() %>% t() %>% print.table() [,1] [,2] [,3] [,4] [,5] Ch3 11C 12D 11B 11C 12D 11C 12D Ch4 12C 12C 12C # 3 ore, dar la o aceeași clasă Fz4 7A 7B 7A 8B 7B 8B Fz5 6A 6B 6A 6B Gg3 12E 12E IH1 11B 11C 11C 11E 11A 11D 11F Is4 11F 11F 11D 11F # 3 ore la o aceeași clasă LE4 12A 12C 12A 12C LR8 11F 6B 10F 12E Lt1 10E 9E 11F 12F 7A 9F 11F 7B Mt7 12E 12E Ps2 11E 11F 12E 12F Sp3 12F 10E 12E 9F 11F # "Sport" ar cere teren liber Tc3 10C
Ch4
are 3 ore la o aceeași clasă, deci nu i-am ajustat distribuția; analog, la Is4
, etc.
Sp3
este al 3-lea în ordinea numărului de ore, pe disciplina "Sport" și cele 5 ore ale sale sunt la clase diferite între ele; totuși nu i-am ajustat distribuția (deși putea rezulta și o distribuție cu două sau trei zile libere), fiindcă se pare că pentru "Sport" nu prea încap trei profesori pe un același teren (și mărind numărul de ore pe zi la unul dintre ei, suprapunerea pe teren va fi probabil, greu de evitat).
În R12i.RDS
avem acum o repartizare prof|cls|zl
a tuturor lecțiilor, care este omogenă pe zile, pe clase și pe profesorii cu măcar 9 ore, este relativ omogenă pe cuplajele existente și este acceptabilă (ca număr de zile libere) pe profesorii cu mai puțin de 9 ore.
Mai departe, avem de repartizat lecțiile dintr-o aceeași zi, pe orele 1:7 ale zilei; ne așteptăm să fie (mult) mai complicat decât în [2], fiindcă acum avem de-a face și cu perechi sau triplete de clase cuplate (de plasat de fiecare dată într-o aceeași oră a zilei, la câte doi sau trei profesori – evitând bineînțeles, suprapunerea cu alte lecții ale acestora)…
vezi Cărţile mele (de programare)