[1] Recunoașterea textului și extragerea datelor unui orar școlar format PDF (I - VIII)
[2] V.Bazon - De la seturi de date și limbajul R, la orare școlare (Google Books)
[3] V. Bazon - Orare școlare echilibrate și limbajul R (Google Books)
[4] Noul orar (partea a treia) (și "partea a patra")
În [3] și [2] am dezvoltat programe $\mathbf{R}$ prin care lecțiile prof|cls
sunt repartizate echilibrat pe zilele de lucru, apoi lecțiile prof|cls|zl
sunt alocate pe orele 1:7
ale zilei, iar în final se reduc ferestrele rezultate pe orarul astfel constituit.
Pentru a pune la punct programele respective, am analizat și orare postate pe site-urile unor școli reale — orare constituite "cap-coadă" prin aplicația de tip point-and-click de la asctimetables; singurul scop al postării orarelor respective era acela practic (tabelând elevilor și profesorilor școlii ce au de făcut în fiecare zi și oră) și totdeauna, a trebuit întâi să detectăm și să extragem datele de pe tabelele vizuale respective (confruntându-ne mereu cu lipsurile specifice formulării vizuale sau manuale; v. [1] etc.).
Vom considera acum și cazul cel mai simplu, scutit de conjuncturi: avem o școală fictivă "SF", care are suficiente săli de clasă (încât poate funcționa într-un singur schimb), destule săli de sport și laboratoare dedicate (încât nu avem restricții privind alocarea lecțiilor de "Sport" sau de "Informatică", sau de limbi străine, etc.); presupunem deasemenea că sunt disponibili pe piață destui profesori pentru fiecare disciplină, iar elevii școlii au fost astfel distribuiți pe clase încât să nu fie necesară invenția lecțiilor "pe grupe", la limbile străine (și nici la "Muzică" și "Desen"!…).
În aceste premise ideale, realizarea orarului pentru "SF" va deveni mult mai simplă decât în cazurile din realitate, fiindcă nu mai este necesar să inventăm și să ținem seama de cuplaje, tuplaje și simultane; dar… avem de văzut cât de simplă (adaptând programele noastre din [3]), fiindcă implicit (de vreme ce avem suficient spațiu și resurse umane), avem un număr foarte mare de clase și profesori (în plus, trebuie să vedem câți profesori sunt necesari pentru fiecare disciplină și să-i încadrăm echitabil pe clase).
Plecăm de la „planul-cadru de învățământ” (v. edu.ro), dar fiindcă vizăm orare și nu „politica educațională”, nu distingem între disciplinele "obligatorii" și cele "opționale". Deasemenea, nu distingem după "arii curriculare"; de fapt, separarea în arii disjuncte a disciplinelor este un act artificial cam meschin, de natură sindicală, care induce ideea complet falsă, contrară istoriei lucrurilor, că "una nu are de-a face cu alta" — de exemplu, cel mai frapant, că "Informatică" nu are de-a face cu "Matematică" (nici cu limbile și nici cu… "română"; pe tabelele elaborate de instituții încă se scrie "ROMANA").
Considerăm o încărcătură maximală, câte 8 clase pe fiecare nivel 5:12
(în total, 64 de clase); de la clasa a 9-a încolo, distingem după "PROFIL" și "SPECIALIZARE" (de care depinde numărul de ore alocat disciplinelor, la clase de același nivel):
CLS <- lapply(5:12, function(i) paste0(i, LETTERS[1:8])) %>% unlist() Profil Specializare Nr_clase (nivel 9:12) real matematică-informatică, intensiv informatică 2 (AB) real științe ale naturii, bilingv engleză 1 (C) real științe ale naturii 1 (D) uman științe sociale, bilingv engleză 1 (E) uman filologie, bilingv franceză 1 (F) real științe ale naturii, în limba germană 1 (G) uman filologie 1 (H)
Pentru "discipline" facem tot soiul de simplificări (profitabile pentru problema orarului); de exemplu, considerăm că "Educație muzicală | plastică | artistică | vizuală" cad la toate clasele unde apar, în seama unor profesori de "Muzică" ("Mz
") și de "Desen" ("Ds
"); desemnăm prin "ES
" (pentru "Educație socială") obiecte ca "Economie", "Educație antreprenorială", "Educație juridică", "Filosofie", "Logică", "Tehnici de argumentare", "Psihologie", "Sociologie" (care pe filiera "TEORETCĂ" au cel mult o oră/clasă); ignorăm "Istoria Franței", "Geografia Franței", "Istoria și tradițiile minorităților", "Repere geografice si culturale ale Germaniei", "Literatură universală", "Limbă și civilizație italiană" etc. — adăugând orele respective profesorilor care fac "Franceză" (Fr
), "Germană", sau "Latină", "Română" etc.
Până la urmă ne-am fixat aceste 17 discipline principale:
Dsp <- readRDS("discipline.RDS") disc cod disc cod 1 Biologie Bi 10 Istorie Is 2 Chimie Ch 11 Latină La 3 Desen Ds 12 Matematică Mt 4 Engleză En 13 Muzică Mz 5 Fizică Fi 14 Religie Rg 6 Franceză Fr 15 Română Ro 7 Geografie Ge 16 Socio-umane SU 8 Germană Gr 17 Sport Sp 9 Informatică In
În "frame_64.RDS
" avem încadrarea disciplinelor pe fiecare clasă:
SF <- readRDS("frame_64.RDS") data.frame: 916 obs. of 3 variables: $ cls: chr "10A" "10A" "10A" "10A" ... $ dsp: Factor w/ 17 levels "Bi" "Ch" "Ds" ..: 1 2 3 4 5 6 7 9 10 12 ... $ ore: num 2 2 1 2 3 2 1 5 1 4 ... > SF %>% filter(cls %in% c("10A", "12G")) # exemplificare cls dsp ore cls dsp ore 1 10A Bi 2 16 12G Bi 2 2 10A Ch 2 17 12G Ch 2 3 10A Ds 1 18 12G En 2 4 10A En 2 19 12G Fi 3 5 10A Fi 3 20 12G Ge 1 6 10A Fr 2 21 12G Gr 6 7 10A Ge 1 22 12G In 1 8 10A In 5 23 12G Is 1 9 10A Is 1 24 12G Mt 4 10 10A Mt 4 25 12G Rg 1 11 10A Mz 1 26 12G Ro 4 12 10A Rg 1 27 12G SU 1 13 10A Ro 3 28 12G Sp 1 14 10A SU 2 15 10A Sp 2
Avem această distribuție a numărului de ore pe clasă, pentru fiecare disciplină:
Tot <- addmargins(table(SF[c('dsp', 'ore')]), 1) dsp 1 2 3 4 5 6 7 Bi 30 32 0 0 0 0 0 # total: 30 + 32*2 = 94 ore de "Biologie" Ch 10 28 4 0 0 0 0 Ds 54 0 0 0 0 0 0 En 0 47 5 2 2 8 0 Fi 0 30 20 0 0 0 0 Fr 0 50 6 0 0 0 0 Ge 46 17 1 0 0 0 0 Gr 0 0 0 6 1 1 0 In 43 10 3 0 2 2 4 Is 34 24 6 0 0 0 0 La 19 3 0 0 0 0 0 Mt 0 8 1 47 4 0 0 Mz 50 0 0 0 0 0 0 Rg 64 0 0 0 0 0 0 Ro 0 0 15 45 1 1 2 SU 11 12 37 2 2 0 0 Sp 25 39 0 0 0 0 0 Sum 386 300 98 102 12 12 6 # total: 386+300*2+98*3+... = 1862 ore
Formulăm un dicționar "Dno
" cu numărul de ore pe fiecare disciplină:
dot_prod <- function(M) # produsul scalar as.vector(t(M[, 1]) %*% M[, 2]) tt <- Tot %>% as.data.frame() %>% mutate(ore = as.integer(ore)) total_ore <- function(D) tt %>% filter(dsp == D) %>% select(-dsp) %>% dot_prod() Ld <- levels(SF$dsp) Dno <- map(Ld, total_ore) %>% setNames(Ld) %>% unlist() %>% sort() La Gr Mz Ds Rg Ch Ge Bi Is Sp Fr Fi In SU En Mt Ro 25 35 50 54 64 78 83 94 100 103 118 120 122 164 175 227 250
Considerăm norma săptămânală de lucru de 16-18 ore. Pentru "Desen" de exemplu, sunt necesari Dno["Ds"]/18
=54/18
= 3 profesori (și îi indicăm prin Ds1
, Ds2
și Ds3
). Pentru "La
" ("Latină" și complemente ca "Limbă și civilizație italiană") ar fi suficient un profesor, dar acesta ar avea 7 ore suplimentare față de normă; vom vedea dacă am putea adăuga încă niște ore de "La
", la unele clase, încât să constituim două norme.
Să vedem câte ore pe săptămână are fiecare clasă:
tot_ore <- function(K) SF %>% filter(cls == K) %>% count(ore) %>% dot_prod() Kor <- map(CLS, tot_ore) %>% unlist() %>% setNames(CLS) 5A 5B 5C 5D 5E 5F 5G 5H 6A 6B 6C 6D 6E 6F 6G 6H 7A 7B 7C 7D 25 25 25 25 25 25 27 25 27 27 27 27 27 27 30 27 30 30 30 30 7E 7F 7G 7H 8A 8B 8C 8D 8E 8F 8G 8H 9A 9B 9C 9D 9E 9F 9G 9H 30 30 33 30 30 30 30 30 30 30 32 30 32 32 33 30 31 31 32 29 10A 10B 10C 10D 10E 10F 10G 10H 11A 11B 11C 11D 11E 11F 11G 11H 12A 12B 12C 12D 32 32 33 30 32 32 31 30 29 29 28 28 28 28 29 28 29 29 28 28 12E 12F 12G 12H 28 28 29 28
Am putea adăuga câte o oră de "La
" unor clase care acum au sub 30 de ore, anume claselor a 5-a și a 11-a:
La <- data.frame(cls = c(CLS[1:8], CLS[49:56]), dsp="La", ore=1L) SF <- rbind(SF, La); saveRDS(SF, "frame_64.RDS")
Acum pe "La
" avem 25+16=41 de ore, încât vor fi necesari doi profesori La1
și La2
(cu 20 și 21 de ore); le rămâne desigur, să găsească ceva complementar cu "Latină", util de făcut la clasele respective (să observăm că a 5-a fac deja câte două ore de limbi străine, "Engleză" și "Franceză" de obicei; să fie vreo contradicție cu "politica educațională", dacă am cere să facă și ceva complementar limbii române?).
Obs.1 Unele dintre clasele cărora le-am "adăugat" mai sus câte o oră de "La
" aveau deja una sau chiar două ore de "La
" și acum, în loc să avem cumulat două, respectiv trei ore, avem câte două linii cu "La
" la acele clase (cea veche și cea adăugată); acest defect s-a manifestat mai târziu (în funcția norming()
, redată mai jos) și l-am corectat ad-hoc.
Pentru câteva discipline ("Religie", "Desen"), numărul de ore nu variază de la o clasă la alta — încât nu prea are importanță care clase le repartizăm unuia sau altuia dintre profesorii de pe disciplina respectivă. Însă dacă numărul de ore pe o disciplină variază după clasă, atunci avem de văzut care clase le repartizăm profesorilor respectivi, încât aceștia să cumuleze echitabil, norme de lucru apropiate.
Pentru a repartiza clasele, pe o disciplină sau alta, profesorilor respectivi, avem nevoie de următoarea listă de dicționare, "Cdo
":
cls_ore <- function(D) { Cls <- SF %>% filter(dsp == D) Cls %>% pull(ore) %>% setNames(Cls %>% pull(cls)) } Cdo <- map(Ld, cls_ore) %>% setNames(Ld) > Cdo[["Gr"]] # exemplificare 10G 11G 12G 5G 6G 7G 8G 9G 4 4 6 4 4 4 4 5
Pentru fiecare disciplină, Cdo
indică numărul de ore pe acea disciplină, la fiecare clasă. Pentru cazul "Gr
" exemplificat mai sus, profesorii Gr1
și Gr2
vor primi clasa 12G
(cu 6 ore) și respectiv 9G
(cu 5 ore) și câte 3 clase a câte 4 ore (încadrându-i astfel pe 18 și respectiv 17 ore).
Dno[[D]]
ne dă numărul total de ore pe disciplina D
; raportându-l la "Norma
" de 18 ore (sau poate mai bine, 19 sau 20), obținem numărul np
de profesori necesari pentru disciplina respectivă. Am avea de partiționat mulțimea claselor la care se face disciplina D
în np
submulțimi, astfel încât suma orelor D
pe clasele dintr-o aceeași submulțime să fie în jurul valorii Norma
; probabil, am putea rezolva și folosind vreun pachet de "programare în numere întregi" — dar avem o soluție directă, mai simplă (și… similară cu "etichetarea lecțiilor cu Zile
" însușită deja în [3]).
Definim un "tabel" având drept nume de linie clasele care fac disciplina D
, cu două coloane h
| prof
, unde h
înregistrează numărul de ore pe disciplina D
pentru fiecare clasă, iar prof
etichetează liniile repetând consecutiv secvența celor np
profesori:
norming <- function(D, Norma=18) { S <- Cdo[[D]] # numărul de ore D, pe fiecare clasă P <- data.frame(h = as.integer(S), prof = "") rownames(P) <- names(S) np <- Dno[[D]] %/% Norma # estimează numărul de norme Prof <- paste0(D, 1:np) # profesorii pe disciplina D P %>% mutate(prof = rep_len(Prof, nrow(.))) %>% mutate(prof = factor(prof)) }
Obs.2 Pentru fiecare clasă trebuie să avem o singură linie pentru D
(nu două, cum am semnalat în Obs.1) — altfel norming()
eșuează, găsind linii distincte cu același "nume de linie" (și apoi, nu putem eticheta cu profesori distincți lecții pe aceeași disciplină, la o aceeași clasă).
Funcția următoare analizează setul de date produs de norming()
și listează pe fiecare profesor, clasele (și numărul de ore/clasă) care i-au fost repartizate:
frame_inf <- function(Pdf) { # Pdf este setul produs de norming() D <- substr(Pdf$prof[1], 1, 2) # disciplina pe care s-a aplicat norming() S <- Cdo[[D]] Prof <- levels(Pdf$prof) # etichetele montate de norming() map(Prof, function(X) { pp <- Pdf %>% filter(prof == X) S[rownames(pp)] # clasele cu aceeași etichetă (ale aceluiași profesor) }) %>% setNames(Prof) }
Ilustrăm pentru D
="Ro
", cum putem folosi aceste funcții:
ro <- norming("Ro", 20) # print(ro) iro <- frame_inf(ro) sapply(iro, sum) %>% print() Ro1 Ro10 Ro11 Ro12 Ro2 Ro3 Ro4 Ro5 Ro6 Ro7 Ro8 Ro9 22 22 19 22 25 22 24 19 19 18 20 18
Prin etichetarea întreprinsă în norming()
(cu Norma=20
) ne-au rezultat 12 profesori, cu cel puțin 18 și cel mult 25 de ore fiecare (cu Norma=18
rezultau 13 profesori, dintre care unul cu numai 15 ore). Observând clasele celor cu prea multe ore, putem echilibra (mai echitabil) încadrările respective:
> iro[["Ro2"]] # încadrarea inițială, pe 25 de ore, a lui Ro2 10B 11F 5B 6F 8B 9F 3 6 4 4 4 4 > iro[["Ro4"]] 10D 11H 5D 6H 8D 9H 3 5 4 4 4 4 ro["10D", 2] <- "Ro9" # mută 10D (cu 3 ore) de la Ro4 la Ro9 ro["9F", 2] <- "Ro7" # reetichetează 9F (cu 4 ore), din Ro2 în Ro7 iro <- frame_inf(ro) sapply(iro, sum) %>% print() Ro1 Ro10 Ro11 Ro12 Ro2 Ro3 Ro4 Ro5 Ro6 Ro7 Ro8 Ro9 22 22 19 22 21 22 21 19 19 22 20 21
Următoarea funcție înscrie în SF
profesorii disciplinei respective:
SF <- SF %>% mutate(prof="") # dacă n-avem deja, coloana SF$prof prof_setting <- function(iD) { # iD este lista returnată de frame_inf() D <- substr(names(iD)[[1]], 1, 2) walk(names(iD), function(P) { wh <- which(SF$dsp == D & SF$cls %in% names(iD[[P]])) SF[wh, "prof"] <<- P }) } prof_setting(iro) # înscrie profesorii de "Ro" saveRDS(SF, "frame_64.RDS") > slice_sample(SF %>% filter(dsp=="Ro"), n=4) # ilustrare cls dsp ore prof 1 5G Ro 4 Ro7 2 10G Ro 3 Ro7 3 10E Ro 4 Ro5 4 6D Ro 4 Ro12
Analog, pentru D
="Mt
" obținem o încadrare cu 11 profesori, care după o mică echilibrare arată (sintetic, eludând clasele repartizate) astfel:
Mt1 Mt10 Mt11 Mt2 Mt3 Mt4 Mt5 Mt6 Mt7 Mt8 Mt9 21 20 19 20 22 21 21 22 21 20 20
La fel rezultă încadrările pe toate celelalte discipline. În total, avem 95 de profesori:
> SF$prof %>% unique() %>% length() [1] 95 # profesori
Putem reda încadrarea profesorilor la clasa 9A
de exemplu, astfel:
a9 <- SF %>% filter(cls=="9A") a9 %>% pull(ore) %>% setNames(a9 %>% pull(prof)) Bi5 Ch3 Ds2 En3 Fi1 Fr2 Ge1 In3 Is2 Mt9 Mz1 Rg3 Ro9 SU5 Sp2 2 2 1 2 3 2 1 6 1 4 1 1 4 1 1
sum(SF$ore)
ne dă numărul tuturor lecțiilor care urmează să se desfășoare în cursul săptămânii, anume 1876 de lecții.
Avem de alocat pe zile (și apoi, pe orele zilei) fiecare lecție în parte; deci în loc de o linie ca "12F En 4 En4
", trebuie să vedem 4
linii (identice) "12F En4
" (putem omite disciplina, fiindcă aceasta se deduce totdeauna din numele profesorului), urmând să etichetăm prima linie, de exemplu, cu "Lu
", a doua cu "Ma
" ș.a.m.d. (însemnând că profesorul En4
face câte o lecție la clasa 12F
în zilele Lu
, Ma
etc.).
Multiplicând fiecare linie din SF
după valorile din coloana ore
(prin funcția uncount()
), obținem setul tuturor lecțiilor prof|cls
:
SF <- SF %>% uncount(ore) %>% select(prof, cls) 'data.frame': 1876 obs. of 2 variables: $ prof: chr "Bi1" "Bi1" "Ch1" "Ch1" ... $ cls : chr "10A" "10A" "10A" "10A" ... saveRDS(SF, "lessons.RDS")
Cele 1876 lecții sunt distribuite celor 95 de profesori pe câte cel puțin 16 ore (la un singur profesor) și cel mult 22 ore (la 9 profesori), astfel:
> table(SF$prof) %>% as.integer() %>% table() 16 17 18 19 20 21 22 1 3 15 18 30 19 9
82 de profesori au între 18 și 21 de ore, iar 30 de profesori au câte 20 de ore.
Teoretic, față de norma de 18 ore ar fi trebuit să avem 1876/18=104 profesori — dar aceștia n-ar putea fi încadrați toți, pe câte exact 18 ore; în loc să considerăm profesori cu mai puțin de 18 ore (doar 4 profesori, au 16 sau 17 ore), am preferat să adăugăm până la 4 ore suplimentare, de profesor.
Funcția mount_days_necup()
din programul "by_days.R
" (din [2]) operează cu eventuale reluări, pe lista claselor: pentru fiecare clasă, într-o ordine aleatorie, se consideră subsetul Q
al lecțiilor acesteia și prin labels_to_class(Q)
se încearcă o etichetare cu Zile
a liniilor respective care să mențină, față de etichetările pe clasele anterior întâlnite, omogenitatea distribuției pe zile a lecțiilor fiecăruia dintre profesorii implicați.
În cazul când prin etichetarea curentă, unul dintre profesorii implicați capătă o distribuție în care numărul de ore pe zi diferă cu mai mult de 2 de la o zi la alta — se încearcă (prin permutare) o altă etichetare a lecțiilor clasei curente; dacă oricum am ordona etichetele Zile
pe lecțiile din Q
, apare mereu câte o distribuție individuală neomogenă — atunci este semnalat eșecul către funcția apelantă mount_days_necup()
, iar aceasta va relua de la capăt, schimbând însă ordinea parcurgerii claselor.
În următorul program nu ne mulțumim cu primul rezultat, ci invocăm repetat mount_days_necup()
până când rezultatul este omogen (sau cvasi-omogen) și în privința numărului total de ore/zi:
library(tidyverse) LSS <- readRDS("lessons.RDS") # 1876 lecții prof|cls Zile <- c("Lu", "Ma", "Mi", "Jo", "Vi") perm_zile <- readRDS("lstPerm47.RDS")[[2]] # permutările de 1:5 (zile) source("by_days.R") # mount_days_necup() (v. [2] sau [3]) prnTime <- function(S="") # pentru a estima și afișa timpii de execuție cat(strftime(Sys.time(), format="%H:%M:%S"), S) while(TRUE) { prnTime() R1 <- mount_days_necup(LSS) # etichetează cu zile, pe clase prnTime("\n") s1 <- addmargins(table(R1[c('prof','zl')]))["Sum", 1:5] %>% as.vector() # total ore/zi print(s1) if(diff(range(s1)) <= 2) break; }
Redăm cursul execuției programului ('*' marchează clasa curentă; o secvență de 64 '*' corespunde parcurgerii cu succes a tuturor celor 64 de clase):
03:16:35 **************************************************************** 03:18:39 [1] 373 373 374 380 376 # totalul de lecții/zi este neomogen 03:18:39 **************************************************************** 03:19:51 [1] 379 373 373 375 376 03:19:51 **************************************************************** 03:21:54 [1] 379 375 377 375 370 03:21:54 ********************/ # eșec; se reia procesul **************************************************************** 03:24:49 [1] 372 379 375 373 377 03:24:49 **************************************************************** 03:25:59 [1] 375 379 372 373 377 03:25:59 **************************************************************** 03:27:01 [1] 376 376 375 374 375 # distribuție cvasi-omogenă a totalului de ore > saveRDS(R1, "R1.RDS")
În R1.RDS
avem o repartizare pe zile a tuturor celor 1876 de lecții, care este echilibrată: pentru fiecare profesor, distribuția pe zile a lecțiilor sale este cvasi-omogenă (cu diferență de cel mult două ore, între o zi și alta); pentru fiecare clasă, distribuția pe zile a lecțiilor ei este omogenă; iar cele 1876 de lecții sunt distribuite cvasi-omogen, pe zilele de lucru.
Ca exemplu, redăm distribuția pe zile a lecțiilor de Biologie
:
prof Lu Ma Mi Jo Vi Bi1 10A 12G 7C 7H 11C 12A 6A 6F 8E 10A 12G 6F 10F 11C 6A 9B 7C 7H 9B Bi2 11D 6B 7D 10B 11D 12H 10B 10G 9C 9H 10G 12B 6B 6G 8A 6G 7D 8F 9C Bi3 10C 12C 9D 12C 6C 7E 5D 5F 8B 8G 9D 10H 11F 6C 6H 10C 5A 6H 7E Bi4 10D 5B 6D 8C 10D 11G 5G 7A 7F 11G 7F 8H 12D 5E 7A 11A 12D 6D 9E Bi5 5C 7G 8D 9G 11H 9A 9F 10E 6E 9A 11B 7B 7G 9G 12F 5H 6E 7B
Dintre acești 5 profesori, Bi5
are o distribuție omogenă (cu 3 sau 4 ore/zi); ceilalți au distribuții cvasi-omogene (iar pentru totalul orelor de Bi
rezultă o distribuție suficient de echilibrată: 18 19 18 20 19).
În [4] am definitivat un set de funcții pentru ajustarea interactivă a distribuțiilor individuale; n-ar fi greu să le adaptăm (eliminând termenii care țin de cuplaje) și să le folosim acum pentru a omogeniza distribuțiile cvasi-omogene existente — dar nu ne mai ocupăm aici, de acest aspect.
Reluăm din [4]-IV programele "between.R
", "mount_h1.R
" și "bindHours.R
", eliminând însă toate referirile la cuplaje și tuplaje (iar aici evităm să mai prezentăm logica și demersurile specifice acestor programe).
După ce separăm lecțiile pe fiecare zi, lansăm:
> source("bindHours.R") 12:00:24 Lu 97 încercări 12:00:32 Ma 155 încercări 12:00:43 Mi 99 încercări 12:00:50 Jo 22 încercări 12:00:52 Vi 21 încercări 12:00:54 > saveRDS(ORR, "Orar1.RDS")
În lista ORR
ne-au rezultat (în vreo 30 sec., de această dată) orarele prof|cls|ora
pe fiecare zi; de exemplu, pentru lecțiile zilei Lu
avem:
> glimpse(ORR[["Lu"]]) Rows: 376 Columns: 3 $ prof Ds1, In1, Is5, Bi1, Fi1, Fr1, Mt1, Mt2, Ro2, Ch2, Ds2, En2, In2, ... $ cls "10A", "10A", "10A", "10A", "10A", "10A", "10A", "10B", "10B", ... $ ora 3, 1, 6, 2, 4, 5, 7, 2, 1, 3, 4, 5, 6, 7, 4, 1, 3, 5, 2, 6, 1, 2,...
Din tabelul sumar oferit de glimpse()
, vedem că 10A
are Lu
7 ore: prima cu In1
, a doua cu Bi1
, a treia cu Ds1
ș.a.m.d.
Dacă trecem orarele de la "forma normală" la "matricea-orară" (prin funcția hourly_matrix()
, v. [4]), putem constata (vizual) că pe fiecare zi avem foarte multe ferestre (în jur de 70); dar, conform condițiilor impuse în mount_h1()
, pe ziua curentă fiecare profesor are cel mult două ferestre.
Anterior (v. [2], [4]) am constituit un set de funcții care asigură eliminarea unor ferestre, prin mutări de clase între câte două coloane orare; eliminăm secvențele care vizează cuplajele și tuplajele și putem folosi funcțiile respective astfel:
# xgaps.R source("Gaps.R") # v. [2] ORR <- readRDS("Orar1.RDS") # lista orarelor zilnice, cu multe ferestre Kls <- ORR[[1]]$cls %>% unique() mORR <- map(ORR, hourly_matrix) # transformă în "matrice-orar" W <- list() # pentru orarele cu număr redus de ferestre for(zi in Zile) { # Zile[1] OZ <- mORR[[zi]] # matricea orară a zilei NG1 <- count_gaps(OZ) # numărul inițial de ferestre Cmb <- combn(ncol(OZ), 2) # combinările de 7 (6 sau 5) luate câte două prnTime(paste0(" (", NG1, " ferestre)\n")) orr <- search_better(OZ) # declanșează procesul de reducere a ferestrelor W[[zi]] <- orr[order(rownames(orr)), ] prnTime("\n") }
De exemplu, pentru ziua Lu
reducerea decurge cam așa (diferind de la o execuție la alta):
> source("xgaps.R") 06:23:17 (70 ferestre) 69 67 66 64 63 62 61 59 58 57 56 55 54 52 51 50 49 48 47 46 45 44 43 42 41 40 38 37 36 35 34 33 32 31 30 29 * 29 28 * 28 * 28 * 28 * 28 * 28 06:29:29
Dar ferestrele zilei curente n-au putut fi reduse la mai puțin de 27:
> sapply(W, count_gaps) Lu Ma Mi Jo Vi 28 29 27 27 28 # ferestre rămase
Anterior, pentru orare constituite pe încadrări ale unor școli reale, programul reducea numărul de ferestre sub 5%
din totalul orelor; dar acum ne-au rămas 139 de ferestre, adică 7.4% din totalul 1876 de ore (speram să fie numai vreo 0.05*1876=94 de ferestre).
Sunt două deosebiri, care ar explica situația: aveam cel mai mult 42 de clase (v. [3]), iar acum avem 64 de clase; aveam un număr important de profesori cu ore puține (sub 12 ore/săptămână), iar acum "puține" sunt 16 sau 17 ore și numai în 4 cazuri, toți ceilalți 91 de profesori având câte cel puțin 18 ore (altfel spus, "densitatea" încadrării este acum mult mai mare decât în cazurile "reale", astfel că avem mult mai puține posibilități de acoperire a ferestrelor).
În realitate, profesorii vor ore suplimentare (plătite); iar existența orelor suplimentare (la majoritatea profesorilor, în cazul de față) compensează într-o anumită măsură, suportarea unor ferestre (cel mult două pe zi, pentru unii profesori, în general diferiți de la o zi la alta).
vezi Cărţile mele (de programare)