[1] Vlad Bazon - Problema orarului școlar, pe tărâmul limbajului R (Google Play/Books, 2022)
Funcția move_cls()
din [1] asigură mutarea unei clase pe orarul unei zile fixate (urmărind lanțul Kempe care conține clasa, din graful asociat lecțiilor din cele două coloane orare între care se face schimbarea) – scopul fiind dat de căutarea unei reduceri a numărului de ferestre din orarul zilei respective; în [2] avansasem ideea unei variante de move_cls()
, prin care să schimbăm (între doi profesori) o clasă din orarul unei zile pe orarul unei alte zile – de fapt o idee cam simplistă, de folosit ca atare cel mult într-o ultimă instanță…
Schimbarea de clase între zile ține de „repartizarea pe zile” a lecțiilor săptămânii, care la noi (în [1]) precede stabilirea orarului pe fiecare zi în parte. Iar reduce_gaps.R
produce la fiecare execuție un alt orar (cu altă distribuție a ferestrelor) și n-ar putea fi vorba în program, de inspirația necesară pentru indicarea unor lecții prof|cls|ora
a căror mutare dintr-o zi în alta ar conduce la micșorarea numărului de ferestre pe orarele celor două zile.
Deci nu prea are sens, să ne gândim la „variante” de move_cls()
.
Mai bine să ne gândim la motivele pentru care nu mai putem reduce numărul de ferestre din orarul obținut pe o zi sau alta… Dacă aceste motive ar ține numai de modul în care procedăm (urmărind consecvent o repartizare echilibrată a lecțiilor), atunci repetând „la nesfârșit” reduce_gaps.R
, am nimeri la un moment dat și un orar cu număr micșorat de ferestre. Motivele reale pornesc totuși, în cea mai mare măsură, de la specificul încadrării inițiale a profesorilor și cuplajelor; dacă s-au prevăzut cuplaje întortocheate, acoperind în total prea multe ore pe zi – atunci ferestrele vor fi greu de evitat (la fel, dacă școala funcționează în două schimburi și sunt mulți profesori încadrați pe clase din ambele schimburi).
Ei, dar tot vizualizând (ca în [2]) datele din orar_mai.RDS
, am observat o „greșală” de repartizare: la doi profesori, câte o clasă apare într-o aceeași zi de două ori – fără ca aceasta să fi fost necesar (de exemplu – v. [2] – clasa 12A
apare de două ori în ziua Ma
la profesorul IP1
, deși acesta are doar 4 ore pe săptămână la 12A
și deci, i s-ar fi cuvenit câte o singură intrare pe zi, în 4 zile). Ca urmare, fără să ne mai gândim la ferestre – am reluat repartizarea pe zile a lecțiilor, corectând interactiv defectul respectiv și chiar, mai schimbând pentru echilibrare, alte câteva repartiții individuale.
Am actualizat apoi, corespunzător noii repartiții pe zile, listele de coeficienți "betweenness" ai profesorilor și claselor pe fiecare zi, precum și listele de dependențe între profesori și cuplaje, pe fiecare zi; deasemenea, am determinat pentru fiecare zi, profesorii „externi” – aceia care intră în măcar două cuplaje, dar în ziua respectivă nu au și ore proprii (și atunci, orice fereastră a cuplajelor în care este implicat devine fereastră „ascunsă” a profesorului extern respectiv; în funcția emph_gaps()
din [2] ne-a scăpat acest aspect!).
Folosind apoi mount_hours()
, am obținut noile orare zilnice (iarăși, foarte repede – dat fiind că nu ne-a păsat deocamdată, de ferestre); cam ca în [2] (dar acum trebuie să folosim și prof_matrix()
din [1]) – afișăm numărul curent de ferestre (inițial, foarte mare):
Orr <- readRDS("orarMai.RDS") #> str(Orr[["Lu"]]) ## structura orarului unei zile #tibble [204 × 3] (S3: tbl_df/tbl/data.frame) # $ prof: Ord.factor w/ 69 levels "Is3"<"En6"<"Ch3Bi1"<..: 30 38 48 57 ... # $ cls : chr [1:204] "10A" "10A" "10A" "10A" ... # $ ora : int [1:204] 1 2 3 5 4 6 1 2 4 5 ... Mai <- lapply(Zile, function(zi) prof_matrix(Orr[[zi]]) # v. [1] ) %>% setNames(Zile) sapply(Zile, function(zi) { set_globals(zi) count_gaps(Mai[[zi]]) }) |> print() # numărul curent de ferestre, pe fiecare zi # Lu Ma Mi Jo Vi # 38 35 43 38 37
Mai departe, prin programul reduce_gaps.R
am obținut ca și în [2], orare cu mai puține ferestre – pe care apoi, nu le-am mai putut reduce:
Mai <- readRDS("orar_mai_12.RDS") # după reducerea de ferestre sapply(Zile, function(zi) { set_globals(zi) count_gaps(Mai[[zi]]) }) |> print() Lu Ma Mi Jo Vi 6 12 9 5 4 # a compara cu [2] (5 10 9 6 5)
Ne-au rămas aceleași două zile ca și în [2], Ma
și Mi
, pe care numărul de ferestre este sensibil mai mare decât în celelalte zile (și n-a mai putut fi redus)…
Dacă vrem să evidențiem (corect) repartiția ferestrelor pe zile și profesori – trebuie să completăm emph_gaps()
din [2], cu o funcție prin care să depistăm profesorii „externi”:
emph_gaps <- function(ORR) { Oz <- ORR %>% as.data.frame() %>% mutate(prof = rownames(.), .before=1) %>% mutate(SB = "") rownames(Oz) <- NULL gaps_ext <- function() { l36 <- Oz %>% split(nchar(.$prof)) k3 <- l36[[1]] %>% pull(prof) k6 <- l36[[2]] %>% pull(prof) exz <- map_chr(k6, function(K) { P1 <- substr(K, 1, 3) P2 <- substr(K, 4, 6) if_else(!P1 %in% k3, P1, if_else(!P2 %in% k3, P2, ""))}) unique(exz[exz != ""]) } # profesorii "externi" pe ziua respectivă for(P in Oz$prof) { if(nchar(P) > 3) next MP <- Oz %>% filter(grepl(P, prof)) if(nrow(MP) == 1) { patt <- sum(h2bin[which(! MP[1, 2:8] %in% "-")]) %>% byte_as_line() if(grepl(".*\\*-{1,2}\\*.*", patt)) Oz[Oz$prof == P, "SB"] <- patt } else { patt <- 0L for(i in 1:nrow(MP)) patt <- patt + sum(h2bin[which(! MP[i, 2:8] %in% "-")]) Oz[Oz$prof == P, "SB"] <- byte_as_line(patt) } } Oz <- Oz %>% arrange(prof) for(P in gaps_ext()) { MP <- Oz %>% filter(grepl(P, prof)) patt <- 0L for(i in 1:nrow(MP)) patt <- patt + sum(h2bin[which(! MP[i, 2:8] %in% "-")]) Oz <- Oz %>% add_row(prof = P, SB = byte_as_line(patt)) } Oz[is.na(Oz)] <- "=" # extern (numai în cuplaj, fără ore proprii) Oz }
De exemplu, cele 9 ferestre din ziua Mi
sunt repartizate astfel:
> Mai[["Mi"]] |> emph_gaps() |> per_gaps() # v. [2] prof 1 2 3 4 5 6 7 SB 1 En1 12D 9E - 10C 9B 10B - **-***- 2 En2 6D 11B 9D - 8C 10D - ***-**- 3 ES1 6C 7B - - 6D - - **-**-- 4 Fi1 6B 12C 11A - 10B - - ***-*-- 5 Is1 8B 8A - 6C 10D 10E - **-***- 6 Re1 7A 9A - - 11C 6A - **--**- 7 TI1 9B 9D - 10D 12B - - **-**-- 8 GF3 = = = = = = = -**-**-
GF3
(adăugat la sfârșit, prin add_row()
) are 4 ore, toate în cuplaj cu alți profesori – și i-a rezultat o fereastră „ascunsă”, anume în ora a 4-a a zilei.
Găsim ca explicație principală a „imposibilității” de a mai reduce numărul de ferestre, faptul că pe ziua respectivă avem multe ore cuplate (și întortocheat), la un număr mare de clase.
Folosindu-ne de listele lTz1
și lTz2
(care indică pentru fiecare zi, profesorii angajați în cuplaje și cuplajele respective – v. [1]), afișăm situația orelor cuplate pentru ziua Mi
(adăugând și coloana șabloanelor orare ale celor implicați în cuplaje):
> Mai[["Mi"]][rownames(Mai[["Mi"]]) %in% union(names(lTz2[["Mi"]]), names(lTz1[["Mi"]])), ]) 1 2 3 4 5 6 7 GF1 5C - - - - - - ******- GF4 5A - - - - - - *****-- La1ES1 - - - 12E - - - Fr1 - - - - 8B - - ---***- GF2Fr1 - - - - - 12C - Mu1De1 - - 10D 9B - - - GF2GF3 - 10B - - 12D - - La1 - - - - 7C 11E - ---***- De1 7B 5D - - 6A - - *****-- GF1Fr1 - - - 9A - - - GF1GF3 - - 12B - - 10A - ES1 6C 7B - - 6D - - **-**-- GF1GF4 - 12A - - 9D - - GF2GF4 - - 9B 11E - - - Mu1 - 8C - - 5B 6C - -*****- GF3 = = = = = = = -**-**- # extern
Numai doi dintre profesorii angajați în cuplaje (ES1
și GF3
) au câte o (singură) fereastră (iar celelalte 7 ferestre sunt repartizate la profesori neimplicați în cuplaje).
Transformând matricea redată în data.frame, afișând valorile diferite de '-
' și folosind unique()
– constatăm că pe Mi
avem 27 de ore „pe grupe”, la 23 de clase distincte (dintre cele 34 de clase existente).
Numărul de clase angajate în aceste cuplaje este chiar mare (23 din 34), iar pe de altă parte, avem și cuplaje „întortocheate” – de exemplu, GF1
are o oră în care intră singur și pe încă 5 ore, intră în cuplaj cu Fr1
, GF3
și GF4
(ocupând 6 ore dintre cele 7 ale zilei, în condițiile în care numai două-trei clase au și ora 7); cam la fel stau lucrurile și pentru GF2
și GF4
.
Nu prea se poate să asigurăm (păstrând echilibrele) că lecțiile astfel cuplate – și întortocheat și pe așa de multe clase – decurg fără suprapuneri și fără prea multe ferestre și pe de altă parte, că nu apar prea multe ferestre (7 în cazul zilei Mi
) la „ceilalți” profesori!
vezi Cărţile mele (de programare)