[1] V.Bazon - Orare școlare echilibrate și limbajul R (Google Books, 2022)
https://cran.r-project.org/package=days2lessons (repartizarea lecțiilor pe zile)
https://cran.r-project.org/package=recastlessons (omogenizarea distribuțiilor)
https://cran.r-project.org/package=hours2lessons (montarea orelor)
https://cran.r-project.org/package=refitgaps (reducerea ferestrelor)
Folosind cele 4 pachete R referite mai sus, vom obține aici un orar pentru setul de lecții considerat acum vreo trei ani în [1].
Recuperăm întâi setul tuturor lecțiilor, proprii sau în cuplaj:
LSS <- readRDS("lessons.RDS") # adus din ~/22oct/ (v. [1]) str(LSS) 'data.frame': 1242 obs. of 2 variables: $ prof: chr "Bi2" "Bi2" "Bi2" "Bi2" ... $ cls : chr "10E" "10E" "11B" "12E" ...
Am convenit să desemnăm profesorii prin disciplina principală pe care sunt încadrați în școală —abreviată pe două litere, sau uneori numai una— și un număr de ordine între cei încadrați pe o aceeași disciplină; de exemplu, "Bi2
" desemnează unul dintre cei trei profesori de "Biologie", iar "N13
" desemnează unul dintre cei peste 10 profesori de "Informatică". Astfel, profesorilor care au ore proprii le sunt asociate coduri de o aceeași lungime, anume de câte 3 caractere (iar din codul respectiv se va putea deduce disciplina principală a fiecăruia).
Unele dintre aceste 1242 de lecții sunt lecții cuplate, în care doi profesori ar trebui să intre simultan la câte o grupă de elevi ai unei aceleiași clase; desemnăm cuplajele prin coduri de lungime 6, rezultate prin alipirea codurilor celor doi profesori. De exemplu:
dplyr::slice_sample(dplyr::filter(LSS, nchar(prof) == 6), n = 4) prof cls 1 En2En4 10B 2 Ge1Ge3 10A 3 N10N13 12F 4 Ge1X04 12A
"En2En4
" este un profesor fictiv, iar lecția sa la clasa 10B
se va desfășura astfel: într-o anumită zi și oră, elevii clasei respective vor fi despărțiți ad-hoc în două grupe (să zicem, "avansați" și "începători"), care vor face lecția de "Engleză" respectiv cu profesorii "En2
" și "En4
".
De observat că "Ge1
" intră în două cuplaje, cu "Ge3
" (pe clasa 10A
), respectiv cu X04
; dar unii profesori nu au ore proprii, ci doar în cuplaje:
dplyr::filter(LSS, grepl("X04", prof)) prof cls 1 Ge1X04 12A 2 Ge1X04 12A 3 Ge1X04 12A 4 Ge1X04 12A 5 Ge1X04 12A
Rezultă că "X04
" este unul dintre profesorii externi: are 5 ore, dar toate sunt în cuplaj (cu "Ge1
", pe clasa 12A
); profesorii externi nu sunt înregistrați ca atare (prin coduri de lungime 3), ci vor trebui deduși din cuplajele în care apar.
În LSS
, numărul de ore proprii (când codul prof
are lungimea 3) este cam de 10 ori mai mare, decât cel de lecții cuplate:
dplyr::count(LSS, cod = nchar(prof)) cod n 1 3 1127 # ore proprii (83 profesori) 2 6 115 # lecții cuplate (25 profesori fictivi)
Pe lângă setul LSS
prezentat mai sus, avem și un set de lecții tuplate, când profesorii indicați ar trebui să intre simultan la câte una dintre clasele asociate lor:
TPL <- read.csv("TPL.csv") prof cls prof cls 1 Em1 Ev1 9A 10A 6 Em1 Ev1 9F 10F 2 Em1 Ev1 9B 10B 7 Em1 Ev1 9G 10G 3 Em1 Ev1 9C 10C 8 Em1 Ev1 9H 10H 4 Em1 Ev1 9D 10D 9 Em1 Ev1 9I 10I 5 Em1 Ev1 9E 10E
De exemplu, tuplajul reprezentat pe linia 1 are această semnificație: profesorii Em1
și Ev1
ar trebui să intre într-o aceeași zi și oră, respectiv la clasele 9A
și 10A
; de fapt, apărând la unul și la celălalt într-o aceeași zi și oră, cele două clase vor putea fi alternate între ei, după rangul săptămânii curente — ceea ce este avantajos, față de cuplajele obișnuite Em1Ev1/9A
și Em1Ev1/10A
.
În TPL
nu este înregistrată neapărat, ziua alocată fiecărui tuplaj — fiindcă repartizarea pe zile a tuplajelor va decurge în manieră dinamică, pe parcursul procesului de repartizare pe zile a tuturor lecțiilor (încât ea va trebui dedusă dacă este cazul, după încheierea acestui proces).
Pachetul days2lessons
exportă funcția mount_days()
, care întâi, generează o repartizare pe zile a lecțiilor din afara cuplajelor, suficient de echilibrată pe profesori; apoi, generează în mod repetat câte o repartizare pe zile pentru lecțiile cuplate și pentru tuplaje (păstrând pe cât se poate, omogenitatea distribuțiilor cumulate ale profesorilor) — până când, îmbinând cele trei repartiții, fiecare clasă capătă o distribuție cât se poate de echilibrată, a numărului de ore/zi:
install.packages("days2lessons") library(days2lessons) LSS <- readRDS("lessons.RDS") TPL <- read.csv("TPL.csv") prnTime <- function(NL = " ") cat(strftime(Sys.time(), format="%H:%M:%S"), NL) prnTime(" ") DZ <- mount_days(LSS, TPL, Dfh = 3) prnTime("\n") # ... 05:53:18 05:53:29 # durata execuției curente: cam 11 secunde > str(DZ) 'data.frame': 1260 obs. of 3 variables: $ prof: Ord.factor w/ 108 levels "Bi1"<"Bi2"<"Bi3"<..: 12 12 51 35 25 25 ... $ cls : chr "10A" "10A" "10A" "10A" ... $ zl : Factor w/ 5 levels "Lu","Ma","Mi",..: 3 1 5 2 4 3 1 5 2 4 ...
Repartizând pe zile și îmbinând cele 1127 lecții din afara cuplajelor, cele 115 lecții cuplate și cele 18 lecții corespunzătoare celor 9 tuplaje, au rezultat în DZ
1260 de lecții.
mount_days()
asigură următoarele caracteristici, pentru repartiția pe zile DZ
: pentru fiecare profesor, distribuția numărului de ore/zi este fie omogenă (cu diferență maximă 1, de la o zi la alta), fie cvasi-omogenă (cu diferență maximă 2, de la o zi la alta); pentru fiecare clasă, diferența maximă între cel mai mare și cel mai mic număr de ore/zi este 3 (ar fi fost numai 2, dacă apelam cu "Dfh=2
" — numai că atunci, dacă s-ar încheia, execuția ar dura probabil foarte mult timp).
Iar funcția următoare produce distribuția pe zile a numărului total de ore:
dis_total <- function(D) addmargins(table(D[c('cls', 'zl')]))["Sum", ] > dis_total(DZ) Lu Ma Mi Jo Vi Sum 251 254 253 250 252 1260 # distribuție acceptabilă, ca omogenitate
Subliniem că re-executarea funcției mount_days()
va produce o repartizare DZ
diferită de cea precedentă (păstrând însă, caracteristicile menționate).
În acest moment, pachetul recastlessons
încă așteaptă aprobarea "on its way to cran
", așa că îl instalăm din arhiva locală:
install.packages("recastlessons_0.1.0.tar.gz") library(recastlessons)
Listăm întâi conținutul (funcții și date) exportat de acest pachet:
> ls(pos = "package:recastlessons") [1] "%>%" "change_day" "cls_2days" "cls_dis" "cls_last" [6] "cls_quasihom" "cls2prof" "coupled_dis" "coupled_list" "DZ" [11] "keep_toupled" "prof_2days" "prof_bad_dis" "prof_swap" "R123" [16] "tidy2tsv" "toupled_dis" "TPL" "Zile"
Am schimbat de vreo două ori, numele funcțiilor oferite (v. Omogenizarea interactivă a distribuției lecțiilor) și până la urmă, căznind denumirile pentru a fi scurte și suficient de sugestive, am ajuns și să le încurcăm…
Așa că ni se pare potrivit să furnizăm o descriere concisă a scopului fiecăreia dintre funcțiile respective; pentru aceasta, ținem cont de faptul că pachetele sunt instalate în câte un subdirector de pe una dintre căile indicate de .libPaths()
, iar în subdirectorul respectiv găsim de regulă un "tabel" Meta/Rd.rds
conținând între altele, numele funcțiilor din pachet și "titlurile" menite să precizeze scopurile acestora (iar câmpul "Keywords
" caracterizează fiecare titlu: este "data", sau funcție internă, sau o funcție exportată obișnuită):
Rd <- readRDS(file.path(.libPaths()[1], "recastlessons/Meta/Rd.rds")) %>% dplyr::filter(sapply(Keywords, length) == 0) %>% dplyr::select(Name, Title) for(i in 1:nrow(Rd)) cat(paste0(Rd[i, 1],"():\n "), Rd[i,2], "\n\n")
Adăugând și argumentele fiecărei funcții, avem după ceva editare suplimentară, aceste referințe, utile mai încolo, asupra funcțiilor pachetului:
change_day(DZ, P, Q, Z, new_zl): Pe repartiția pe zile curentă, schimbă într-o altă zi, lecția indicată cls2prof(DZ, P): La care clase și zile intră profesorul, singur sau în cuplaje cls_2days(DZ, z1, z2): Distribuția numărului de ore pe două zile, la anumite clase cls_dis(DZ, K): Distribuția numărului de ore/zi la o clasă cls_last(DZ, P): Existența vreunei clase care să ajungă la a 6-a sau a 7-a oră a unei zile, pentru un membru al unor cuplaje cls_quasihom(DZ, d = 2): Distribuția numărului de ore/zi la clase, cu o anumită diferență între zile coupled_dis(DZ): Distribuția pe zile a lecțiilor cuplajelor și membrilor acestora coupled_list(DZ): Distribuțiile pe zile, pentru membrii cuplajelor keep_toupled(DZ, TP): Distribuția pe zile curentă trebuie să păstreze alocarea tuplajelor prof_2days(DZ, K, z1, z2): Profesorii (și distribuțiile de lecții) care la o anumită clasă au ore într-o anumită zi și nu au ore într-o altă anumită zi. prof_bad_dis(DZ, at_least = 8): Profesorii din afara cuplajelor, cărora le-au rămas distribuții neomogene prof_swap(DZ, P, z1, z2): mapează 'cls2prof()' pe lista acelor profesori P1 din afara cuplajelor cu care profesorul indicat ar putea schimba o clasă și o zi, fără ca prin aceasta distribuția lui P1 să fie "degradată" (să devină neomogenă) tidy2tsv(D): Formatul TSV a unui (sub)set de lecții repartizate pe zile toupled_dis(DZ, TP): Distribuția pe zile a numărului de lecții ale celor angajați în tuplaje
DZ1.RDS
este una dintre repartizările pe zile obținute anterior (prin pachetul days2lessons
) și avem clase cu 3 ore (sau cu 2 ore) mai mult într-o zi, față de alta:
> DZ <- readRDS("DZ1.RDS") > cls_quasihom(DZ, d = 3) Lu Ma Mi Jo Vi 11A 4 5 7 6 7 11D 8 5 6 5 5 ## alte câteva clase cu distribuții neomogene ##
Pentru a omogeniza distribuția pe zile a lecțiilor clasei 11A
, căutăm profesori ai clasei care au ore Vi
, respectiv Mi
, dar nu și Lu
sau Ma
:
> prof_2days(DZ, "11A", "Vi", "Ma") zl prof Lu Ma Mi Jo Vi Gg1 5 4 5 5 6 R07 2 1 2 1 3 Bi3 3 3 2 4 4 Ge1Ge2 2 1 3 2 2 En4 3 5 2 4 1
Profesorii afișați au Vi
, dar nu și Ma
, câte o lecție la 11A
; cel mai firesc este să mutăm lecția lui Gg1
(fiindcă astfel, acesta capătă distribuția omogenă (5 5 5 5 5), iar distribuția la 11A
devine mai convenabilă, (4 6 7 6 6)):
> DZ <- change_day(DZ, "Gg1", "11A", "Vi", "Ma")
Căutăm acum profesori cu ore Mi
, dar nu și Lu
, la 11A
:
> prof_2days(DZ, "11A", "Mi", "Lu") zl prof Lu Ma Mi Jo Vi M03 3 4 4 4 3 Ec1 4 3 3 4 4 En4 3 5 2 4 1 N05 0 2 2 1 3 > DZ <- change_day(DZ, "M03", "11A", "Mi", "Lu")
Prin mutarea efectuată, M03
păstrează omogenitatea distribuției lecțiilor sale, iar distribuția lecțiilor clasei 11A
devine omogenă:
> cls_dis(DZ, "11A") Lu Ma Mi Jo Vi 5 6 6 6 6
Analog, folosind iarăși prof_2days()
și change_day()
, omogenizăm distribuțiile celorlalte clase indicate curent de cls_quasihom()
(iar pe parcurs, reușim să omogenizăm și distribuții găsite quasi-omogene, la unii profesori). Când cls_quasihom(DZ, d=2)
va returna NULL
— aflăm că la fiecare clasă avem distribuție omogenă a lecțiilor sale și trecem să ne ocupăm de omogenizarea pe cât posibil, a distribuțiilor rămase cvasi-omogene, ale profesorilor.
Listăm întâi distribuțiile individuale care la momentul curent, sunt cvasi-omogene (și care totalizează peste "at_least=8
" ore):
> Bad <- prof_bad_dis(DZ) # 24 coloane (profesori cu distribuții cvasi-omogene) > Bad[, 1:16] Bi4 Ch1 Ch2 Ch3 Ef1 Em1 Ev1 Fi1 Fi2 Fr4 Fr5 Gg2 Is1 Is2 M01 M08 Lu 3 2 3 3 6 4 2 5 4 3 1 3 3 4 4 4 Ma 2 3 4 3 5 4 3 5 5 3 1 4 4 3 5 3 Mi 1 4 2 2 4 4 4 4 4 1 2 4 4 3 3 2 Jo 2 3 4 4 5 4 4 4 5 2 2 4 5 2 3 4 Vi 2 4 3 3 4 2 2 3 3 3 3 5 4 4 5 3
Să vedem cu care profesor ar putea interschimba o lecție Bi4
, din ziua Lu
în ziua Mi
:
> cls2prof(DZ, "Bi4") # lecțiile lui Bi4 Bi4 Lu "10I 11C 9I" Ma "10B 10F" Mi "10B" Jo "10F 10I" Vi "11D 9I" > prof_swap(DZ, "Bi4", "Lu", "Mi") # ... [[6]] Ev1 Lu "10C 10H" Ma "8A 10A 10F" Mi "6B 8B 10D 10I" Jo "6A 7A 10B 10G" Vi "5A 10E"
Observăm pe datele afișate, că putem muta 10I
din ziua Lu
în ziua Mi
la Bi4
și invers, la Ev1
(fără ca prin aceasta, vreunul să capete două ore într-o aceeași zi, la 10I
):
> DZ <- change_day(DZ, "Bi4", "10I", "Lu", "Mi") > DZ <- change_day(DZ, "Ev1", "10I", "Mi", "Lu")
Acum, în repartizarea curentă DZ
, Bi4
are câte două ore în fiecare zi, iar Ev1
a căpătat distribuția (3 3 3 4 2), mai bună decât cea inițială…
Dar se întâmplă că am uitat de faptul că Ev1
este membru al unor tuplaje pe clase a 10-a și a 9-a! keep_toupled()
ar trebui să returneze TRUE
, dacă repartiția curentă DZ
păstrează încă, distribuția pe zile existentă în TPL
:
> keep_toupled(DZ, TPL) prof cls zl 9 Em1 Ev1 9I 10I Mi
Constatăm însă că tuplajul "Em1 Ev1
" / "9I 10I
" a fost alocat Mi
— prin urmare am greșit, mutând 10I
în ziua Lu
la Ev1
; anulăm deci, mutarea respectivă:
> DZ <- change_day(DZ, "Ev1", "10I", "Lu", "Mi") > keep_toupled(DZ, TPL) [1] TRUE
Pentru a omogeniza distribuția la Ev1
, fără a afecta repartizarea tuplajelor în care este implicat, ar trebui să mutăm numai clase ale sale din afara tuplajelor; găsim până la urmă, prin prof_swap()
, profesori cu care să interschimbe o asemenea clasă, obținând pentru Ev1
o distribuție cu câte 3 ore/zi.
Procedând ca mai sus, am reușit fără cine știe ce efort, să omogenizăm distribuțiile la aproape toți profesorii cu peste 8 ore, care nu sunt implicați în cuplaje.
Pentru cei care fac parte din cuplaje, avem de analizat următoarea situație:
> (coupled_list(DZ))$N01 zl prof Lu Ma Mi Jo Vi N01N10 0 0 0 2 1 N01N05 1 1 1 0 0 N01N06 1 0 1 1 0 N01X03 0 1 1 1 0 N01 4 2 4 2 5 N01X02 0 2 0 1 0 Sum 6 6 7 7 6
Sunt puține clase care au zile cu 7 ore și trebuie să vedem dacă măcar una dintre clasele alocate curent lui N01
(singur sau în cuplaje), ajunge la a 7-a oră în zilele Mi
și respectiv, Jo
(și analog, pentru a 6-a oră din celelalte zile):
> cls_last(DZ, "N01") named list()
Fiindcă s-a afișat o listă vidă, deducem că pentru fiecare zi există o clasă a lui N01
care ajunge la a 6-a și respectiv, la a 7-a oră (ne putem convinge cu cls_dis()
, că 10I
și 10D
ajung Mi
și respectiv Jo
, la a 7-a oră).
Putem evidenția și direct, repartizarea lecțiilor unui profesor care face parte din cuplaje, în scopul de a vedea cum am putea omogeniza distribuția cumulată:
> DZ %>% dplyr::filter(grepl("N08", prof)) %>% dplyr::arrange(zl) prof cls zl prof cls zl 1 N05N08 9C Lu 12 N08N11 11B Mi 2 N08N11 11B Lu 13 N08 11B Mi 3 N08N11 9B Lu 14 N08 12A Mi 4 N08 10A Lu 15 N05N08 9C Jo 5 N08 12A Lu 16 N08 10A Jo 6 N05N08 9C Ma 17 N08 12A Jo 7 N08N11 11B Ma 18 N08 9C Jo 8 N08N11 9B Ma 19 N08N11 9B Vi 9 N08 11B Ma 20 N08 11B Vi 10 N08 12A Ma 21 N08 11B Vi 11 N08 9B Ma
N08
are Ma
3 ore în cuplaj cu N05
sau cu N11
și 3 lecții proprii; observăm că putem muta ora la 9B
în ziua Mi
(în care nu are 9B
) și deasemenea, ora 10A
de Lu
, în ziua Vi
— distribuția cumulată devenind astfel omogenă, (4 5 4 4 4):
> DZ <- change_day(DZ, "N08", "9B", "Ma", "Mi") > DZ <- change_day(DZ, "N08", "10A", "Lu", "Vi")
Am procedat la fel și cu distribuțiile cumulate ale altor membri ai cuplajelor, după care au rămas cu distribuții cvasi-omogene doar 5 profesori (dintre care, 3 cuplaje), cu câte cel mult 10 ore fiecare:
> prof_bad_dis(DZ) Ps1 R07 Ge1Ge2 N02N07 N03X01 Lu 2 2 2 1 2 Ma 2 1 1 2 3 Mi 1 2 3 2 1 Jo 3 1 2 1 1 Vi 1 3 2 3 2
Iar distribuția pe zile a totalului orelor este suficient de echilibrată:
> dis_total(DZ) Lu Ma Mi Jo Vi Sum 253 254 252 251 250 1260
Am putea eventual, să omogenizăm distribuția totalului de ore, mutând lecții ale profesorilor încadrați pe câte un număr mic de ore (plecând de la prof_bad_dis(DZ, at_least=1)
) — dar va trebui să ne îngrijim ca, mutând în altă zi lecția unei clase, să nu "stricăm" omogenitatea distribuției lecțiilor acesteia…
Bineînțeles că în final, salvăm repartizarea curentă DZ
, de exemplu în "DZ4.RDS
" (păstrând pentru orice eventualitate, repartizarea de la care am plecat, "DZ1.RDS
").
Pentru fiecare zi, avem de montat orele 1:7
, pe lecțiile și tuplajele din ziua respectivă; constituim o listă care asociază fiecărei zile, lecțiile prof|cls
repartizate în acea zi:
DZ <- readRDS("DZ4.RDS") lDZ <- lapply(split(DZ, ~ zl), function(D) D[, 1:2]) # fără câmpul 'zl'
Având în vedere că am avut grijă să adăugăm în "TPL.RDS
" câmpul 'zl
', corespunzător zilelor în care au fost alocate tuplajele în cadrul repartizării curente "DZ4.RDS
", putem edita comanda de mai sus, constituind și lista pe zile a tuplajelor:
TPL <- readRDS("TPL.RDS") lTP <- lapply(split(TPL, ~ zl), function(D) D[, 1:2]) # fără câmpul 'zl'
Pentru fiecare zi, tuplajele din lTP[[zi]]
vor trebui alocate în câte o aceeași oră a zilei.
Instalăm pachetul hours2lessons
și deocamdată, încercăm funcția mount_hours()
:
install.packages("hours2lessons") library(hours2lessons) zi <- "Lu" Orr <- mount_hours(lDZ[[zi]], lTP[[zi]])
dar vai… obținem imediat acest mesaj de eroare:
Error in mount_hours(lDZ[[zi]], lTP[[zi]]) : you have a Class (cls) with more than 7 hours
În days2lessons
(și apoi în recastlessons
) lecțiile tuplate erau încorporate în repartizarea finală pe zile DZ
, fiindcă trebuiau socotite (și apoi, omogenizate) distribuțiile individuale; însă în hours2lessons
tuplajele sunt altfel văzute, luând în seamă și tuplaje mai complicate, de exemplu 4 profesori pe 3 clase, când se înființează intern un nou cuplaj (încât să avem 3 profesori pe 3 clase) care este apoi adăugat în setul lecțiilor. Deci, până la viitoarea updatare, hours2lessons
pretinde ca lecțiile tuplate să fie doar indicate în setul TPL
, nu și încorporate în setul lecțiilor zilei (de încorporare se ocupă intern, după ce înființează eventual anumite noi cuplaje).
Prin urmare, până ce vom updata pachetul, nu avem altă soluție decât să eliminăm lecțiile tuplate, din seturile de lecții ale zilelor (ceea ce devine un bun exercițiu de folosire a funcțiilor pmap()
, anti_join()
și Reduce()
):
for(zi in names(lDZ)) { DL <- data.frame() for(i in 1:nrow(lTP[[zi]])) { Pr <- strsplit(lTP[[zi]][i, 1], " ")[[1]] Cl <- strsplit(lTP[[zi]][i, 2], " ")[[1]] LL <- purrr::pmap(list(Pr, Cl), function(P, K) lDZ[[zi]] %>% dplyr::filter(prof == P & cls == K) %>% dplyr::distinct()) DL <- rbind(DL, Reduce(rbind, LL)) } lDZ[[zi]] <- dplyr::anti_join(lDZ[[zi]], DL, by = c("prof", "cls")) %>% dplyr::mutate(prof = as.character(prof)) } saveRDS(lDZ, "lDZ.RDS") saveRDS(lTP, "lTP.RDS")
În final, am ținut seama de faptul că hours2lessons
așteaptă ca pe lecțiile zilei, câmpul prof
să fie de tip character și nu factor.
Acum putem aplica hours2lessons::mount_hours()
, pe fiecare zi:
W <- list() # pregătește lista orarelor zilnice prnTime() # 15:48:19 for(zi in names(lDZ)) W[[zi]] <- mount_hours(lDZ[[zi]], lTP[[zi]]) prnTime("\n") # 15:48:24 saveRDS(W, "W1.RDS")
Lista W
a orarelor zilnice prof|cls|ora
(tot în format lung), rezultă în câteva secunde (în cazul de față, în 5 sec.); prin funcția long2matrix()
putem reformula orarul unei zile, ca matrice cu liniile numite după profesori, conținând clasele alocate acestora în fiecare oră a zilei. Să facem o mică verificare, vizând alocarea pe ore a lecțiilor celor angajați în tuplaje:
> Lu <- long2matrix(W[["Lu"]]) > Lu[rownames(Lu) %in% c("Em1", "Ev1"), ] %>% print(quote = FALSE) 1 2 3 4 5 6 7 Ev1 10H 10C - - - 8B - Em1 9H 9C 6A - - 8A -
Constatăm că într-adevăr, s-au respectat tuplajele zilei Lu
(anume, "(Em1 Ev1)/(9H 10H)
" și "(Em1 Ev1)/(9C 10C)
"), alocându-le în prima și respectiv, a doua oră.
De observat că Ev1
are trei "ferestre" (orele 3, 4 și 5); într-o versiune updatată a pachetului (netransmisă încă, la CRAN), am impus condiția ca pe orizontală, la profesorii neimplicați în cuplaje să nu apară mai mult de două ferestre (ceea ce crește puțin, durata execuției — în loc de 5 sec. am avea probabil vreo 8 sec.).
Pentru încă un exemplu, să vedem alocarea pe ore pentru profesori implicați în cuplaje:
> Lu[grepl("N03", rownames(Lu)), ] %>% print(quote = FALSE) 1 2 3 4 5 6 7 N03X01 - 9H - - - 11I - N03 9E - 10H 9H 10B - -
Deci N03
are Lu
6 ore, dintre care a doua și a 6-a în cuplaj cu X01
.
Înlocuind în ultima comandă de mai sus, "N03
" cu "En
" — vedem alocarea pe ore (pentru ziua Lu
) a lecțiilor de "Engleză":
1 2 3 4 5 6 7 En2En4 - - - 10B 12B - - En5 - - - - 9A 9B - En2 11B 8A 5A - - 6A - En3 - 11G - - 11F 9G - En4 9D 7A 11C - - - - En1 9F 10A 12G - 11I 12C -
Observăm că En2
și En4
au lecții proprii în primele 3 ore, apoi fac împreună orele 4 și 5.
Subliniem că hours2lessons
nu se interesează de ferestre (decât, într-o versiune updatată, să nu fie mai mult de două, la oricare profesor din afara cuplajelor) — ci doar de respectarea condiției de bază pentru un orar, aceea ca oricare două lecții să nu se suprapună într-o aceeași oră. Subliniem și faptul că fiecare nouă execuție a funcției mount_hours()
produce un nou orar al zilei, diferit de cele anterioare.
De reducerea numărului de ferestre apărute în orarul zilei se ocupă pachetul refitgaps
— numai că acesta așteaptă orarul zilei în format matriceal, așa că înainte de a detașa hours2lessons
din sesiunea curentă, transformăm orarele din W
, din formatul lung în formatul de "matrice-orar":
> Wm <- lapply(W, long2matrix) > saveRDS(Wm, "Wmo.RDS")
Subliniem că într-o matrice-orar (de pe care avem mai sus unele exemplificări) fiecare clasă apare câte o singură dată pe fiecare coloană orară 1:n
, unde n
(≤ 7) este numărul de ore/zi al clasei respective.
Mai este necesară o pregătire, fiindcă pachetul refitgaps
pretinde un câmp ora
inițializat cu 0
, pe setul tuplajelor zilei:
install.packages("refitgaps") library(refitgaps) Wmo <- readRDS("Wmo.RDS") lTP <- readRDS("lTP.RDS") for(zi in names(lTP)) lTP[[zi]] <- lTP[[zi]] %>% dplyr::select(prof, cls) %>% dplyr::mutate(ora = 0L)
Prin refitgaps::recast()
reducem numărul de ferestre, pe fiecare zi (precizăm că recast()
returnează o listă, conținând matricea-orar cu număr redus de ferestre și respectiv, numărul de ferestre rezultat):
W <- list() # pregătește lista orarelor zilnice for(zi in names(Wmo)) { cat(zi, ": ", sep=""); prnTime() Orr <- recast(Wmo[[zi]], lTP[[zi]]) # Niter = 3000 (implicit) prnTime(" "); cat(Orr[[2]], "gaps\n") W[[zi]] <- Orr[[1]] } Lu: 06:33:46 06:34:45 9 gaps Ma: 06:34:45 06:35:40 9 gaps Mi: 06:35:40 06:36:36 13 gaps Jo: 06:36:36 06:37:35 13 gaps Vi: 06:37:35 06:38:30 13 gaps
În această primă execuție ne-au rezultat în total 2×9 + 3×13 = 57 de ferestre (4.52% față de totalul 1260 al lecțiilor), într-un timp total de aproape 5 minute.
Re-executând de câteva ori, am obținut pe diverse zile orare cu numai 6 sau 9 ferestre — așa că am modificat programul, repetând recast()
pe fiecare zi (dar cu Niter
mai mare decât cel implicit), până ce numărul de ferestre pe ziua respectivă este 6 sau 9:
W <- list() # pregătește lista orarelor zilnice End <- c(6, 6, 6, 9, 9) %>% setNames(names(Wmo)) for(zi in names(Wmo)) { cat(zi, ": ", sep=""); prnTime() repeat { Orr <- recast(Wmo[[zi]], lTP[[zi]], Niter = 6000) prnTime(" "); cat(Orr[[2]], "gaps\n") if(Orr[[2]] <= End[zi]) break } W[[zi]] <- Orr[[1]] } saveRDS(W, "Wrc5.RDS")
Bineînțeles că acum, execuția a luat mai mult timp:
Lu: 07:31:36 07:33:26 9 gaps 07:35:21 13 gaps 07:37:05 7 gaps 07:38:53 7 gaps 07:40:41 9 gaps 07:42:34 12 gaps 07:44:20 6 gaps Ma: 07:44:20 07:46:11 13 gaps 07:48:04 9 gaps 07:49:51 6 gaps Mi: 07:49:51 07:51:27 7 gaps 07:53:16 9 gaps 07:55:02 5 gaps ## ETC.
în total, aproape o oră. Orarele zilnice rezultate au respectiv (6 6 5 9 9) ferestre, adică în total 35 de ferestre (deci 2.77% din totalul 1260 al lecțiilor).
Să vedem cum sunt repartizate ferestrele, de exemplu pe ziua Lu
:
> have_gaps(W[["Lu"]]) prof 1 2 3 4 5 6 7 ore 1 N08 - - - - 12A - - ***-*-- 2 N09 10F 12B - - - - - **-*--- 3 Fr4 - - 9I - 12H 11I - --*-**- 4 Em1 8A 6A - 9C 9H - - **-**-- 5 Fr3 - 12I 9G - 11D 10F - -**-**- 6 Re2 - 10C - 10F 9A 9C - -*-***-
În câmpul ore
avem "șablonul orar" al tuturor lecțiilor profesorului (singur sau în cuplaje). N08
are lecție proprie în ora a 5-a, dar are și lecții în cuplaje, în primele 3 ore — deci are o fereastră în ora a 4-a. N09
are lecții proprii în primele 2 ore și o lecție în cuplaj în ora a 4-a, deci are o fereastră în ora a 3-a. Ceilalți 4 profesori au câte trei sau patru ore, cu câte o singură fereastră (în ora a 3-a sau a 4-a).
Profesorii de "Biologie" de exemplu, au Lu
următorul orar (fără ferestre):
> W[["Lu"]][grepl("Bi", rownames(W[["Lu"]])), ] %>% print(quote=FALSE) 1 2 3 4 5 6 7 Bi2 5A 10E 6B - - - - Bi4 - - - - 11C 9I - Bi3 - - - 11F 12C 11H - Bi1 - - 9H 10A 11I 9E 9C
Subliniem că repetând execuția, am obține (cu alți timpi) o altă listă W
de orare, de regulă cu alte repartiții ale ferestrelor.
vezi Cărţile mele (de programare)