momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Încă un experiment, pe orarul unei școli (III)

limbajul R | orar şcolar
2023 oct

[1] Problema orarului școlar echilibrat și limbajul R

[2] V. Bazon - Orare școlare echilibrate și limbajul R https://books.google.ro/books?id=aWrDEAAAQBAJ

[3] Transformarea fișierului PGN în obiect de date (I)

Dicționarele dependențelor

Existența cuplajelor complică foarte mult „problema orarului”… Alocarea pe zile și ore a lecțiilor cuplajelor și profesorilor care sunt angajați în cuplaje, va depinde mereu de alocările făcute celor cu care sunt astfel conexați; la fel, ferestrele din programul zilnic al fiecăruia.

Împărțim lecțiile din setul CDL în trei părți, după lungimea codului (3, 6 sau 9) din câmpul prof și degajăm profesorii din fiecare parte:

S3 <- CDL %>% split(nchar(.$prof))
P1 <- S3[[1]] %>% pull(prof) %>% unique() %>% sort()  # care au (și) ore proprii
P2 <- S3[[2]] %>% pull(prof) %>% unique() %>% sort()  # cuplaje cu 2 profesori
P3 <- S3[[3]] %>% pull(prof) %>% unique() %>% sort()  # cuplaje cu 3 profesori

Următorul dicționar are drept chei profesorii din setul P1 care intră măcar într-un cuplaj (având și ore proprii), iar drept valori – vectorii care conțin profesorii de care depind aceștia, la alocarea pe zile și ore:

Tw1 <- map(P1, function(P) union(P2[grepl(P, P2)], P3[grepl(P, P3)])) %>%
       setNames(P1) %>% compact()

Dacă P este una dintre cheile lui Tw1, ora din zi care poate fi alocată uneia dintre lecțiile lui P trebuie să nu coincidă cu vreuna dintre orele alocate deja cuplajelor din vectorul Tw1[[P]]:

  P     Tw1[[P]]
 $Ds1  "Ds1Mz1"
 $LE3  "LE3Mz1"
 $LG1  "LG1LG4" "LG2LG1" "LG3LG1LG4"
 $LG2  "LG2LG1" "LG2LG3"
 $LG3  "LG2LG3" "LG3LG4" "LG3LG1LG4"
 $LG4  "LG1LG4" "LG3LG4" "LG3LG1LG4"
 $LI1  "LI1LI3" "LI2LI1" "Tc2LI1"
 $LI2  "LI2LI1" "LI2LI3"
 $LI3  "LI1LI3" "LI2LI3"
 $Mz1  "Ds1Mz1" "LE3Mz1"
 $Tc2  "Tc2LI1"

Constituim și un dicționar cumva invers, în care cheile sunt cuplajele existente, iar valorile sunt vectori care conțin profesorii (propriu-ziși, sau „fictivi” adică în cuplaje) de care depinde alocarea orelor cheii respective:

Tw2 <- map(P2, function(PP) {
           p1 <- substr(PP, 1, 3)
           p2 <- substr(PP, 4, 6)
           setdiff(c(p1, p2, Tw1[[p1]], Tw1[[p2]]), PP) %>% unique()
       }) %>% setNames(P2) %>% compact()

La alocarea lecțiilor unui cuplaj PP (cheie din Tw2), va trebui să ținem seama de alocările făcute deja profesorilor și cuplajelor din vectorul Tw2[[PP]]:

  PP       Tw2[[PP]]
 $Ds1Mz1  "Ds1" "Mz1" "LE3Mz1"
 $LE3Mz1  "LE3" "Mz1" "Ds1Mz1"
 $LG1LG4  "LG1" "LG4" "LG2LG1" "LG3LG1LG4" "LG3LG4"   
 $LG2LG1  "LG2" "LG1" "LG2LG3" "LG1LG4" "LG3LG1LG4"
 $LG2LG3  "LG2" "LG3" "LG2LG1" "LG3LG4" "LG3LG1LG4"
 $LG3LG4  "LG3" "LG4" "LG2LG3" "LG3LG1LG4" "LG1LG4"   
 $LI1LI3  "LI1" "LI3" "LI2LI1" "Tc2LI1" "LI2LI3"
 $LI2LI1  "LI2" "LI1" "LI2LI3" "LI1LI3" "Tc2LI1"
 $LI2LI3  "LI2" "LI3" "LI2LI1" "LI1LI3"
 $Tc2LI1  "Tc2" "LI1" "LI1LI3" "LI2LI1"

De exemplu, LI1LI3 nu se va putea afla într-o aceeași oră a zilei ca și vreunul dintre cei 5 profesori (sau cuplaje) din vectorul Tw2$"LI1LI3".

Am văzut deja anterior și un cuplaj de trei profesori (și este unic):

Tw3 <- map(P3, function(PPP) {
           p1 <- substr(PPP, 1, 3)
           p2 <- substr(PPP, 4, 6)
           p3 <- substr(PPP, 7, 9)
           setdiff(c(p1,p2,p3,Tw1[[p1]],Tw1[[p2]],Tw1[[p3]]), PPP) %>% unique()
       }) %>% setNames(P3) %>% compact()
#  $LG3LG1LG4  "LG3" "LG1" "LG4" "LG2LG3" "LG3LG4" "LG1LG4" "LG2LG1"

Repartizarea pe zile și pe orele zilei a lecțiilor prof|cls, precum și reducerea ferestrelor din orarele obținute (deasemenea, calculul numărului de ferestre dintr-un orar final dat), se bazează pe dicționarele de dependențe constituite mai sus; le salvăm împreună:

save(Tw1, Tw2, Tw3, file="Tw123.Rda")

Am observat anterior (când am listat lecțiile cuplajului LG3LG1LG4), că există și clase cuplate – clase care depind una de alta, în sensul că la o anumită disciplină, elevii din aceste clase sunt repartizați în grupe care sunt alocate într-o aceeași oră a zilei, câte unui profesor.
Condiţia ca un cuplaj de profesori să lucreze nu pe câte o grupă a unei clase, ci pe clase întregi, rezultate prin combinarea ad_hoc a grupelor a două sau mai multor clase – este apariţia de două sau mai multe ori, într-o aceeaşi zi şi oră, a acelui cuplaj:

Twc <- CDL %>% 
       filter(nchar(prof) > 3) %>% 
       group_by(prof, zi, ora) %>% 
       count() %>% filter(n > 1)  # reţinem numai apariţiile duble (sau triple)
# print(Twc)
    #   prof      zi      ora     n
    # 1 Ds1Mz1    Jo        5     2  # 2 clase în aceeași zi și oră
    # 2 Ds1Mz1    Ma        7     2
    ## ETC.
# adăugăm coloana claselor:
Twc$Cls <- map(1:nrow(Twc), function(i)
               CDL %>%
               filter(prof == Twc$prof[i], 
                      zi == Twc$zi[i], 
                      ora == Twc$ora[i]) %>%
               pull(cls) %>% as.character()) %>%
           sapply(., toString)    
Twc1 <- Twc %>% ungroup() %>% select(prof, Cls) %>% distinct()
print(Twc1, n=Inf)  # tabloul claselor cuplate
       prof      Cls
     1 Ds1Mz1    9C, 9D  # Ds1: 9C și simultan, Mz1: 9D (probabil)     
     2 Ds1Mz1    9A, 9B       
     3 LG1LG4    10E, 10F     
     4 LG2LG1    12E, 12F     
     5 LG2LG1    10A, 10B     
     6 LG2LG1    10C, 10D     
     7 LG2LG1    9A, 9B       
     8 LG2LG3    9E, 9F       
     9 LG2LG3    11E, 11F     
    10 LG3LG1LG4 11A, 11B, 11D
    11 LG3LG1LG4 12A, 12B, 12D
    12 LG3LG4    10E, 10F

Clasele 9A, 9B, 9C și 9D sunt cuplate câte două, pentru lecțiile de "Desen" și "Muzică" (se știe că acestea au alocate câte o jumătate de oră); fie (într-o aceeași zi și oră) Ds1 intră la 9A și Mz1 intră la 9B în săptămânile impare și invers în cele pare – fie, Ds1 intră la clasa constituită din „grupele 1” ale celor două clase și Mz1 intră la clasa constituită din „grupele 2”, în săptămânile impare și invers, în cele pare.
Analog (dar mai complicat), pentru lecții de "Germană"…

Va fi greu de asigurat cuplarea claselor respective, în câte o aceeași zi și oră; o „soluție” care evită dificultățile de analiză ulterioară a cazurilor posibile, ar consta în convenirea din start a unui orar pentru lecțiile cuplate respective (de alipit cumva în final, orarului generat pentru restul lecțiilor).
Pentru orarul CDL existent, acest orar „prefabricat” îl vedem în setul Twc constituit mai sus:

print(Twc[-4], n=Inf)
       prof      zi      ora      Cls
     1 Ds1Mz1    Jo        5      9C, 9D       
     2 Ds1Mz1    Ma        7      9A, 9B       
     3 LG1LG4    Jo        5      10E, 10F     
     4 LG1LG4    Mi        4      10E, 10F 
     ...

9C și 9D sunt cuplate Jo ora a 5-a, iar 9A și 9B sunt cuplate Ma ora a 7-a; dar fiindcă "Desen" și "Muzică" au câte o oră la două săptămâni, nu este necesar să fixăm din start zilele și orele zilei în care să se desfășoare: în orice zi și oră am aloca clasa 9A (de exemplu) profesorului „fictiv” Ds1Mz1, fiecare dintre profesorii „reali” Ds1 și Mz1 (aflați în vectorul de dependențe Tw2$"Ds1Mz1") va fi liber în acea zi și oră – încât este asigurată cerința ca 9A să facă "Desen" în săptămânile impare și "Muzică" în cele pare.

Însă celelalte lecții (diferite de "Desen" și "Muzică") au alocate câte o oră pe săptămână; deci clasele 10E și 10F trebuie să aibă "Germană" simultan, neapărat într-o aceeași zi și oră – pentru a putea repartiza elevii lor în câte două grupe, alocând clasa formată din grupele 1 lui LG1 și pe cea formată din grupele 2 lui LG4.
Prin urmare, pentru clasele cuplate pentru lecții de "Germană" se cuvine să fixăm din start, o alocare convenabilă pe zile și ore a acestora (vom căuta să evităm „înghesuirea” lecțiilor respective). După ce vom fixa un orar pentru lecțiile cuplate de "Germană", evidențiate mai sus în tabelul Twc – vom elimina din CDL lecțiile respective și păstrând doar coloanele prof și cls, vom genera prin programele din [2], un orar echilibrat pentru lecțiile rămase (completându-l apoi, cu orarul parțial pe care-l fixasem din start).

Dar… până să eliminăm din CDL coloanele zi și ora, s-ar cuveni să facem niște statistici simple, asupra datelor existente (pentru a putea compara orarul existent în setul CDL, cu cel pe care urmează să-l generăm); de exemplu, ne-ar interesa distribuția curentă pe zile, pe clase, pe profesori a numărului de ore (și de ferestre).

Statistici pe orarul curent

—v. Partea a IV-a—

vezi Cărţile mele (de programare)

docerpro | Prev | Next