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

Orar pe o școală fără profesori (VI)

graf | limbajul R | orar şcolar
2024 dec

Cuplaje în tuplaje (reducerea simultanelor prin cuplare)

Pentru tuplajele cu număr de profesori mai mare ca numărul de clase, inventăm câte un cuplaj — reducându-le astfel la tuplaje standard, cu părți de aceeași dimensiune.
De exemplu, înlocuim tuplajul (Fr1 Fr2 Gr1) / (11C 11E) de trei profesori pe două clase, cu (Fr1Fr2 Gr1) / (11C 11E) în care cuplajul nou Fr1Fr2 este asociat clasei 11C (ca și cum aceasta ar fi despărțită în două grupe, la care intră respectiv Fr1 și Fr2, iar Gr1 intră în ora respectivă la 11E; în realitate, elevii celor trei clase sunt distribuiți cu abstracție de clasă în trei "grupe", câte una fiecărui profesor).

Aceasta impune anumite corecții în setul R12.RDS (al tuturor lecțiilor prof|cls|zl, repartizate deja pe zile), în listele Tw12.Rda asociate cuplajelor existente și desigur, în "orarele parțiale" tj{1,2}.RDS asociate tuplajelor.
De exemplu, în R12 clasa 11C apare la Fr2 (v. [1]):

    prof    Lu            Ma    Mi            Jo        Vi 
    Fr2     05C 09A 11C   10C   09A 10C 11C   07A 11A   05C 07A 11A

ori acum ea trebuie pasată profesorului fictiv "Fr1Fr2":

R12$prof[with(R12, prof=="Fr2" & cls=="11C" & 
                   zl %in% c("Lu", "Mi"))] <- "Fr1Fr2"

La fel avem de schimbat și la alte clase dintre cele implicate în tuplaje.
Deasemenea, avem de adăugat unele lecții, materializându-le pe cele care în [1] rămăseseră subînțelese:

R12 <- R12 %>% add_row(cls = c("12A", "10C"), 
                       prof = c("Fr1", "Fr3"), zl="Ma")

Avem apoi de adăugat unele chei și valori în dicționarele din "Tw12.Rda", de exemplu:

Tw1[["Fr1"]] <- c(Tw[["Fr1"]], "Fr1Fr3", "Fr1Fr2") 
Tw1[["Gr1"]] <- c("Fr1Gr1", "Fr2Gr1", "Gr1Gr2")
Tw2[["Gr1Gr2"]] <- c("Gr1", "Gr2", "Fr1Gr2", "Fr2Gr1", "Fr1Gr1")

Și în sfârșit, reformulăm într-o singură listă "tj12.RDS", orarele parțiale constituite în [1] pentru tuplaje, evidențiind noile cuplaje:

> readRDS("tj12.RDS")  # orarul parțial pe tuplaje
    $Lu  # ilustrare
           prof         cls ora
    Fr2 Fr3 Gr1 09A 09C 09D   0
     Fr3 Gr1Gr2     10A 10D   0
     Fr1Fr2 Gr1     11C 11E   0
     Fr1Fr3 Gr2     12C 12E   0
        Ds1 Mz1     09C 09D   0

Acum logica alocării pe ore a tuplajelor (folosind câmpul "ora" al acestora), în cursul procesului de alocare pe orele 1:7 a lecțiilor dintr-o aceeași zi, corespunde în principiu, celei descrise deja în [2] și deasemenea, în [4].
Doar că, în secvența din mount_h1.R care tratează cazul când profesorul curent face parte dintr-un tuplaj, trebuie să ținem seama de faptul că acum, se poate să fie vorba nu de un profesor propriu-zis, ci de unul fictiv (cuplaj, pentru care trebuie verificat ca membrii acestuia să fie și ei liberi, în ora propusă pentru alocarea lecției tuplajului).

Repartizarea lecțiilor zilei pe orele 1:7

Procedăm exact ca în [4] (omitem aici, modificarea făcută ad-hoc deocamdată, în mount_h1.R); dar simplificăm (definitiv) funcția "actual_tpl()" din bindHours.R:

P23i <- readRDS("tj12.RDS")
actual_tupl <- function() {
    tupl <- P23i  
    Ora <- list()
    for(zi in Zile) {
        Prof <- tupl[[zi]]$prof %>% paste(., collapse=" ") %>% 
                strsplit(., " ") %>% unlist()
        Cls <- tupl[[zi]]$cls %>% paste(., collapse=" ") %>% 
               strsplit(., " ") %>% unlist()
        Ora[[zi]] <- ORR[[zi]] %>%
                     filter(prof %in% Prof, cls %in% Cls)
    }
    Ora
}

Obținem (și repede) lista ORR, conținând repartizarea pe ore a lecțiilor din fiecare zi:

> source("bindHours.R")
    18:22:43 
    Lu 973 încercări 18:24:52  
    Ma 525 încercări 18:25:57  
    Mi 52 încercări 18:26:02   
    Jo 182 încercări 18:26:26  
    Vi 9 încercări 18:26:27  

Redăm aici orarul rezultat pe Lu, în formatul asigurat de funcțiile actual_tupl() (prin care se extrage orarul tuplajelor) și hourly_matrix() (v. [4]):

[1] "Lu"
         prof cls ora  # orarul tuplajelor
    1     Fr2 09A   4  # Fr2, Fr3 și Gr1 în ora 4, la 9A+9C+9D
    2     Fr3 09C   4
    3     Ds1 09C   6  # Ds1 și Mz1 în ora 5, la 9C și 9D
    4     Gr1 09D   4
    5     Mz1 09D   6
    6     Fr3 10A   2  # Fr3, Gr1 și Gr2 în ora 2, la 10A+10D (trei grupe)
    7  Gr1Gr2 10D   2
    8  Fr1Fr2 11C   1  # Fr1, Fr2 și Gr1 în ora 1, la 11C+11E (trei grupe)
    9     Gr1 11E   1
    10 Fr1Fr3 12C   5  # Fr1, Fr3 și Gr2 în ora 5, la 12C+12E (trei grupe)
    11    Gr2 12E   5

         1   2   3   4   5   6   7            1   2   3   4   5   6   7
Bi1    12D   - 10D 06C   -   -   -      In2   -   - 12A 09E 11E 11B   -
Bi2    10A 06A 11E 10B   -   -   -      In3 11A 05A 09D   -   -   -   -
Bi3      - 07D   - 06B 12A 07A   -      Is1   -   - 07A 07C 10C 10E   -
Ch1    11D   - 11B   - 09D 12B   -      Is2   - 12A 11D 11E   - 08D   -
Ch2    10B   - 12C 10C 09C 11C   -      Is3 06A   -   - 11A 08B 12E   -
Ch3    08C 08D   -   -   -   -   -      LL1 09E 07C   -   -   -   -   -
Ds1      -   -   - 07D 06A 09C 08D      Mt1 11B 12B 08D   -   -   -   -
Ec1    10D 11E   -   -   -   -   -      Mt2   -   - 06A 12C 07A 08B   -
En1      -   - 10B 06A 09A 11E 09D      Mt3   - 10B 07D   - 07C 10D   -
En2    07C   -   - 07A 10E 08A 08B      Mt4   - 09A   - 12D 05C 06B   -
En3      - 12D   - 11D 11A 09E   -      Mt5 06C 10C   - 10A 11D   -   -
En4    08D   - 12B 08C 05A   -   -      Mt6 09D   - 08A   - 08C   -   -
En5      -   - 06B 05B 11B 09B   -      Mt7   - 09B 07B 12A 05B 11A   -
ES1      - 08C 05A   -   - 07D   -      Mz1   - 06B   - 08A 06C 09D   -
Ff1    12E 09C   -   -   -   -   -      Ph1 10E 12C   -   -   -   -   -
Fi1    12C   - 10A 11B   -   -   -      Rg1   -   - 09A 08B 08A 12A   -
Fi2      - 07A 09E   - 07D 12D   -      Rg2   - 11C 08C 05A   -   -   -
Fi3    10C   -   - 09B 10D 11D   -      Ro1 07A   -   - 10D 07B   -   -
Fi4      -   - 08B 11C 06B 10B   -      Ro2 09A 12E 09B   -   -   -   -
Fi5    09C   -   -   -   -   -   -      Ro3   -   - 06C   - 08D 06A   -
Fr1      - 05B   -   -   -   -   -      Ro4   - 08A 05B 10E   -   -   -
Fr1Fr2 11C   -   -   -   -   -   -      Ro5 05C 09D 10C   -   -   -   -
Fr1Fr3   -   -   -   - 12C   -   -      Ro6   -   -   - 12B 09E 12C   -
Fr1Gr1   -   - 12D   -   -   -   -      Ro7   -   -   -   - 12D 10A 08C
Fr2      - 05C   - 09A   -   -   -      Ro8 08B 11B   -   -   -   -   -
Fr3      - 10A   - 09C   - 07B   -      Ro9 05A   - 07C   -   -   -   -
Gg1    12A 11D   - 12E 12B   -   -      Sp1   - 10E 12E   -   -   -   -
Gg2      -   - 11A 05C 11C 08C   -      SP1 07D 09E 10E   -   - 07C   -
Gg3    06B 07B   -   -   -   -   -      SP2 09B 08B   -   - 10A 10C   -
Gr1    11E   -   - 09D   -   -   -      SP3 07B 11A 11C   - 10B   -   -
Gr1Gr2   - 10D   -   -   -   -   -      Th1 05B   - 05C 07B   -   -   -
Gr2      -   -   - 08D 12E 05A   -      TI1 08A 06C   -   -   -   -   -
In1    12B   - 09C   - 09B 09A   -                                     

Pentru unele investigații, să listăm toate lecțiile pe "Fr/Gr" (nu numai pe cele din tuplaje, ca mai sus, ci și lecțiile proprii sau în cuplaj):

> ORR[["Lu"]] %>% filter(grepl("Fr|Gr", prof)) %>% arrange(ora)
       prof   cls     ora
     1 Fr1Fr2 11C       1
     2 Gr1    11E       1
     3 Fr1    05B       2  # lecție proprie
     4 Fr2    05C       2
     5 Fr3    10A       2
     6 Gr1Gr2 10D       2
     7 Fr1Gr1 12D       3  # cuplaj pe 12D
     8 Gr2    08D       4
     9 Fr2    09A       4
    10 Fr3    09C       4
    11 Gr1    09D       4
    12 Fr1Fr3 12C       5
    13 Gr2    12E       5
    14 Gr2    05A       6
    15 Fr3    07B       6

Să observăm că Fr1 are orele 1, 2, 3 și 5 — deci are o fereastră, în a 4-a oră; în a 3-a oră avem câte o fereastră la Fr2 și Fr3, iar Gr2 are două ferestre. Deci în total, la profesorii angajați în tuplaje și cuplaje pe ziua de Lu, avem 5 ferestre — ceea ce este cam mult, având în vedere că procedura de reducere a numărului de ferestre pe care o vom aplica mai departe nu mută lecțiile tuplate, din locul stabilit lor; însă ar fi de refuzat să ne ocupăm de vreo „îndreptare” a lucrurilor — dacă inventezi tuplaje așa de întortocheate (în loc să ai grijă cum repartizezi elevii pe clase, la începutul anului școlar), atunci trebuie să prevezi și să-ți asumi consecințele…

Reducerea numărului de ferestre

În orarele rezultate mai sus avem pe fiecare zi cam câte 40 ferestre; procedând exact ca în [4]-V, am reușit (în timpi de ordinul a 4-5 minute/zi) să reducem la câte 12-14 ferestre/zi. De exemplu, pentru Lu (al cărei orar inițial l-am redat mai sus) ne-a rezultat un orar cu 12 ferestre (dintre care 5 sunt cele moștenite din orarul parțial pe care-l stabilisem pentru tupaje):

         1   2   3   4   5   6   7            1   2   3   4   5   6   7
Bi1    12D 06C 10D   -   -   -   -      In2   -   - 09E 11E 12A 11B   -
Bi2      -   - 10A 06A 11E 10B   -      In3 11A 09D 05A   -   -   -   -
Bi3    07D 12A   -   - 06B 07A   -      Is1   -   - 07A 07C 10C 10E   -
Ch1      -   -   - 12B 11B 11D 09D      Is2   -   - 12A 08D 11D 11E   -
Ch2    12C 10B 10C   - 09C 11C   -      Is3 06A 11A 08B 12E   -   -   -
Ch3      -   -   -   - 08C 08D   -      LL1 09E 07C   -   -   -   -   -
Ds1      -   -   - 07D 06A 09C 08D      Mt1 12B 08D 11B   -   -   -   -
Ec1    10D 11E   -   -   -   -   -      Mt2   -   - 06A 07A 08B 12C   -
En1    09A 06A 11E 10B 09D   -   -      Mt3   -   - 10B 10D 07C 07D   -
En2    08B 07A 10E   - 08A 07C   -      Mt4 05C 06B 09A 12D   -   -   -
En3    11D 09E   -   - 11A 12D   -      Mt5 10C 11D 06C 10A   -   -   -
En4    08C 12B 08D 05A   -   -   -      Mt6 08A 08C 09D   -   -   -   -
En5      -   - 05B 11B 09B 06B   -      Mt7   - 09B 07B 11A 05B 12A   -
ES1    05A 07D 08C   -   -   -   -      Mz1   -   - 06B 08A 06C 09D   -
Ff1    12E 09C   -   -   -   -   -      Ph1 10E 12C   -   -   -   -   -
Fi1    10A 11B 12C   -   -   -   -      Rg1   -   -   - 12A 09A 08A 08B
Fi2    07A 12D 07D 09E   -   -   -      Rg2   -   -   - 11C 05A 08C   -
Fi3      -   - 11D 10C 10D 09B   -      Ro1   -   -   - 07B 07A 10D   -
Fi4      -   - 11C 06B 10B 08B   -      Ro2 09B 09A 12E   -   -   -   -
Fi5    09C   -   -   -   -   -   -      Ro3   -   -   - 06C 08D 06A   -
Fr1      - 05B   -   -   -   -   -      Ro4 05B 10E 08A   -   -   -   -
Fr1Fr2 11C   -   -   -   -   -   -      Ro5 09D 10C 05C   -   -   -   -
Fr1Fr3   -   -   -   - 12C   -   -      Ro6   -   -   - 12C 09E 12B   -
Fr1Gr1   -   - 12D   -   -   -   -      Ro7   -   -   - 08C 12D 10A   -
Fr2      - 05C   - 09A   -   -   -      Ro8 11B 08B   -   -   -   -   -
Fr3    07B 10A   - 09C   -   -   -      Ro9 07C 05A   -   -   -   -   -
Gg1    12A 12E 12B 11D   -   -   -      Sp1   -   -   -   - 10E 12E   -
Gg2      -   -   - 05C 11C 11A 08C      SP1   -   - 07C 10E 07D 09E   -
Gg3    06B 07B   -   -   -   -   -      SP2   -   - 09B 08B 10A 10C   -
Gr1    11E   -   - 09D   -   -   -      SP3 10B 11C 11A   - 07B   -   -
Gr1Gr2   - 10D   -   -   -   -   -      Th1   -   -   - 05B 05C 07B   -
Gr2    08D   -   -   - 12E 05A   -      TI1 06C 08A   -   -   -   -   -
In1      -   - 09C 09B 12B 09A   -                                     

Prin funcția următoare putem transforma matricele-orar înapoi, în forma normală prof|ora|cls, din care apoi putem formula ușor orarele claselor sau profesorilor:

mop_to_normal <- function(Mop) { # matrice-orar pentru o zi
    as.data.frame(Mop) %>%
    mutate(prof = rownames(Mop), .before = 1) %>%
    pivot_longer(cols = 2:ncol(.), 
                 names_to = "ora", values_to = "cls") %>%
    filter(cls != "-")
} # set de date prof|ora|cls pentru ziua respectivă

De exemplu, să extragem din matricea redată mai sus orarul (pe Lu) al clasei 08B, redându-l pe discipline:

ORR <- readRDS("W1.RDS") # matricele orare ale zilelor
Obj <- readRDS("../24nov/objabb.RDS")  # setul discip|cod, constituit anterior
Dsp <- Obj$discip %>% setNames(Obj$cod)  # dicționar cod -> discip
Lu <- mop_to_normal(ORR[["Lu"]])  # orarul zilei, în forma normală
orar_cls <- function(Q, MZ) {  # extrage orarul clasei din matricea-orar a zilei
   orr <- MZ %>% filter(cls==Q) %>% arrange(ora) %>% select(prof) %>%
          mutate(obj = Dsp[substr(prof, 1,2)])
   orr$obj %>% as.vector()
}
print(orar_cls("08B", Lu), quote=FALSE)
#[1] Engleză  Română  Istorie  Sport  Matematică  Fizică  Religie

Aici am dedus disciplina doar pe baza primelor două caractere din prof; dacă vrem să avem în vedere și disciplinele secundare, precum și tuplajele existente, atunci sunt necesare unele adaptări. Iterând orar_cls() pe zile și pe clase, putem obține într-un fișier-text orarele claselor, pe discipline (… ca pentru o școală fară profesori).

Sublinieri

Studiul în șase părți prezentat aici a plecat acum vreo două luni, de la un "Orar general clase" (cum îl numește aplicația "aSc Orare", larg folosită în școlile noastre) postat pe Internet ca fișier PDF, în care fiecare pagină conține într-un "tabel Excel" orarul câte unei clase, specificând însă numai disciplinele (nu și profesorii, cu excepția cazurilor de lecții "pe grupe"). Ne-am pus problema de a "inventa" profesorii omiși, în număr minim pe fiecare disciplină, încât încadrarea acestora pe clase să corespundă cât mai bine, orarului pe discipline existent.
Pentru a rezolva chestiunea, întâi am constituit un set de date cls|obj|zl|ora corespunzător paginilor PDF ale claselor; apoi am observat că încadrarea pe clase a profesorilor de pe o aceeași disciplină, corespunde unei colorări pe vârfuri a grafului claselor care nu pot fi atribuite unui aceluiași profesor (fiindcă s-ar suprapune valorile zl|ora existente).

Odată ce am obținut o încadrare acceptabilă prof|cls, compatibilă ca repartizare pe zile și ore, cu orarele "fără profesori" originale — firește că am vrut să îi asociem un nou orar; în acest scop am folosit iarăși, procedurile de lucru și funcțiile puse la punct în [2] și [3] — dar acum a apărut și o situație nouă; pe lângă cuplaje și tuplaje, avem de considerat și simultane: situații în care mai mult de doi profesori fac lecții într-un același timp, cu grupe de elevi proveniți din mai multe clase de bază. Deocamdată am tratat simultanul prin reducere la tuplaj (în care părțile au aceeași dimensiune): am înlocuit doi profesori cu un cuplaj nou, pe una dintre clasele simultanului; pare a fi cea mai bună idee, dar probabil… ar fi momentul de a rescrie [2] sau [3].

vezi Cărţile mele (de programare)

docerpro | Prev | Next