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

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

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)

Retușarea distribuțiilor individuale de sub 10 ore

Prin vct_neunif() obțineam distribuțiile rămase de omogenizat la profesorii (neimplicați în cuplaje) cu măcar 9 ore, iar prin swap_list() puteam stabili ce interschimburi de clase (dintr-o zi în alta) ar fi de făcut între aceștia (dar numai între cei din vectorul returnat de vct_neunif()); prin aceste interschimburi (sub regia interactivă oferită de recast_by_swap()) am reușit anterior, să omogenizăm majoritatea acestor distribuții.
Dar sunt de „cizelat” și distribuții ale profesorilor încadrați pe mai puțin de 9 ore.

Se poate ca în R12 un total de 5 ore de exemplu, să fi fost distribuit câte una pe zi – ceea ce este corect în principiu, dacă lecțiile respective vizează o singură clasă; dar dacă este vorba de două clase de exemplu, cu 2 ore și respectiv 3 ore – atunci parcă mai principial este ca acele 5 ore să fie distribuite în doar trei zile (două zile cu câte 2 ore, una la prima clasă și cealaltă la a doua și a treia zi cu ora rămasă); iar 5 ore la clase diferite ar fi de repartizat numai în două zile, de exemplu (2 0 0 3 0).

Dacă pentru cei câțiva profesori încadrați pe câte un număr mic de ore, s-ar fi stabilit din start care zile să fie libere, după caz – atunci puteam constitui pentru ei un „orar prealabil” (analog celui constituit anterior în cls23_days.RDS pentru lecțiile pe clase cuplate); noi n-am prevăzut un orar al zilelor libere, așa că ne-a rămas să vedem ce interschimbări facem pe distribuțiile individuale cu totaluri mai mici ca 9, încât să asigurăm desfășurarea lecțiilor respective, după caz, în cât mai puține zile.

Mai întâi adaptăm funcția vct_neunif() pentru a obține tabelul acestor distribuții, existente în setul R12 curent (și includem noua funcție în brush.R, unde avem și vectorul 'twins'):

vct_lt9 <- function() { 
    tpz <- table(R12[c('prof', 'zl')])
    NU <- apply(tpz, 1, function(H) if(sum(H) < 9) H) %>%
          compact() %>% list2DF()
    Prof <- setdiff(names(NU), twins)  # restrânge la neangajați în cuplaje
    NU %>% select(all_of(Prof))
} # neangajați în cuplaje, cu sub 9 ore fiecare

Încărcând brush.R într-o nouă sesiune de lucru și invocând funcția de mai sus, am avut brusc surpriza că este afișată (cu 3 ore, câte una pe zi) și distribuția cuplajului LI1LI3
Primul lucru de verificat este conținutul vectorului 'twins' (exclus mai mai sus din vectorul 'Prof') și am constatat că LI1LI3 nu există în 'twins'; apoi am afișat Tw2 – constatând că într-adevăr, nu conține cuplajul respectiv!

Mai important decât să ne uităm în urmă pentru a descoperi unde și când și din ce motiv s-a pierdut cheia LI1LI3 din dicționarul cuplajelor Tw2 – mai important este să ne dăm seama de consecințele actuale ale acestei scăpări. Investigând lecțiile prof|cls|zl pentru profesorii din contextul acestui cuplaj, am constatat că din fericire, consecințele negative sunt minore: cele 3 lecții ale cuplajului LI1LI3 lipsesc din R2, dar apar în R1 (încât dimensiunile notate anterior ale acestor seturi ar trebui corectate cu ±3) – cu alte cuvinte, ele au fost repartizate pe zile ca și lecțiile celor neangajați în cuplaje, prin mount_days_necup(), în loc să fi fost ca și celelalte cuplaje, repartizate de mount_days_cup(); fiind însă numai 3 lecții (la o aceeași clasă), nu s-au produs dereglări, de nici un fel (pentru niciunul dintre membrii cuplajelor).

Putem zice că am avut noroc, descoperind încă la timp, această scăpare – fiindcă dicționarele Tw12.Rda vor juca un rol esențial când ne vom ocupa de repartizarea lecțiilor pe orele zilei; să reconstituim deci, Tw12.Rda (adăugând și cheia care lipsea):

> Tw2$LI1LI3 <- c("LI1","LI3","LI2LI1","LI2LI3","Tc2LI1")
> save(Tw1,Tw2,file="Tw12.Rda")

Reîncărcăm brush.R și (… ca și cum nu s-ar fi întâmplat nimic) afișăm distribuțiile cu mai puțin de 9 ore, pentru profesori neangajați în cuplaje (nu și pentru LI1LI3):

> vct_lt9()
      Ch3 Ch4 Fz4 Fz5 Gg3 IH1 Is4 LE4 LR8 Lt1 Mt7 Ps2 Sp3 Tc3
    1   2   1   0   1   0   2   1   0   0   2   1   1   1   0
    2   2   1   0   1   0   2   1   1   1   1   0   1   1   0
    3   1   0   2   0   0   1   2   1   0   2   1   1   0   0
    4   2   0   2   1   1   2   0   0   1   2   0   0   2   1
    5   0   1   2   1   1   0   0   2   2   1   0   1   1   0

Vom avea de retușat distribuțiile unora dintre acești 14 profesori (de exemplu, nu vom retușa la Fz4: distribuția celor 6 ore ale sale în 3 zile este de acceptat).

Formulăm o variantă mai generală a funcției swap_list(); dat un profesor P neangajat în cuplaje, căruia vrem să-i retușăm distribuția pe zile – căutăm profesorii P1 neangajați în cuplaje (dar fără alte condiționări, spre deosebire de swap_list()) cu care P ar putea schimba o clasă și o zi fără ca prin aceasta, distribuția lui P1 să fie „degradată” (să devină neomogenă, sau neconvenabilă acestuia):

Profs <- setdiff(unique(R1$prof), twins)  # toți cei neangajați în cuplaje

swap_list_all <- function(P, z1, z2) {
    qPz1 <-  R12[R12$prof==P & R12$zl==z1, 2]  # clasele lui P în z1
    map(setdiff(Profs, P), function(P1) {
        ds2 <- R12 %>% filter(prof == P1) 
        qP1z2 <- ds2[ds2$zl==z2, 2]  # clasele lui P1 în z2 
        qP1z1 <- ds2[ds2$zl==z1, 2]  # și clasele lui P1 în z1 
        if(length(qP1z2) < length(qP1z1))  # cu '<' în loc de '<='
            return(NULL)  
        c12 <- intersect(qPz1, qP1z2)
        if(length(c12) && length(intersect(c12, qP1z1)) < length(c12))
            return(framedist(P1))  # distribuția pe zile a lecțiilor
    }) %>% compact()
}

Bineînțeles că am plasat în afara funcției vectorul Profs, al profesorilor neangajați în cuplaje (pentru a nu-l recalcula în fiecare apel al ei).

Anterior, ne rămăseseră 4 profesori P cu distribuții neomogene (pe mai mult de 9 ore) pentru care swap_list() nu găsea niciun P1 – fiindcă P1 era căutat numai între cei din vectorul returnat de vct_neunif(); acum putem omogeniza și cele 4 distribuții: de exemplu, pentru LR1 distribuția curentă este (5 4 4 4 3) iar swap_list_all("LR1", "Lu", "Vi") produce o listă nevidă, cu 8 profesori P1, din care putem alege de exemplu:

[[5]]
 Fz2             
 "12D 9A 9B"     
 "11A 12D 9A"    
 "10E 11A 12C 9B"
 "10E 12C 9B"    
 "11A 12C 12D 9A"

Interschimbând 11A (nu 9A, deja existentă Lu la Fz2) între zilele 1 și 5 la cei doi profesori, distribuția lui LR1 devine uniformă, iar cea a lui Fz2 rămâne omogenă.

Desigur, a trebuit întâi să afișăm prin framedist() distribuția pe zile și clase a lui LR1, pentru a vedea între care zile să facem schimbul de clase – pentru a putea specifica parametrii necesari apelului swap_list_all() și apoi, pentru a specifica în change_zl() clasa de interschimbat.
Dar în loc de câte o secvență cu aceste trei comenzi (de fapt patru, fiindcă change_zl() trebuie invocat pentru ambii profesori), este mult mai comod să folosim direct recast_by_swap(), adăugându-i însă un parametru pentru specificarea funcției de utilizat:

recast_by_swap <- function(P, sw = FALSE) {
    swap <- ifelse(sw, swap_list_all, swap_list)
    # ... #
#    W <- swap_list(P, z1, z2)
    W <- swap(P, z1, z2)
    # ... #
}

recast_by_swap(P) (cu un singur argument) este exact funcția anterioară (care implica swap_list()), iar recast_by_swap(P, 1) va invoca varianta „generală” swap_list_all().

Folosind (pe repartiția curentă R12) varianta generală, ne-a reușit (în 1-2 minute) să omogenizăm pe rând și cele 4 distribuții din vectorul dat de vct_neunif().

Redăm un exemplu de retușare a distribuțiilor pe mai puțin de 9 ore din vectorul returnat de vct_lt9() – pentru Ch3, care are 7 ore în 4 zile, la 3 clase (și am dori doar 3 zile):

> recast_by_swap("Ch3", 1)
 Ch3
 11C 12D
 11B 11C
 12D    
 11C 12D
          # liber în ziua 5
Mutăm o clasă - din care Z1 în care Z2: 3 2
      Interschimbăm cu unul dintre profesorii:
1: Sp1
 10B 12B 6B 8B
 10C 11E 12D 5A 9B
 11C 5A 7B 8B
 10B 10C 6B 9E
 11A 12B 7B 9C
2: Mt4
 11C 11D 9E 9F
 11C 12D 9E 9F
 11C 11D 12C
 11D 12C 12D
 11D 12A 12C 12D

 Mutăm una dintre clasele  12D  din  Ma  în  Mi 
       la al câtelea profesor (0 to Cancel): 1
Clasa de interschimbat între Ch3 și Sp1: 12D
După interschimbare:
     zl
prof  Lu Ma Mi Jo Vi
  Ch3  2  3  0  2  0  # cu două zile libere
  Sp1  4  4  5  4  4

Redăm, într-un format adecvat, distribuțiile ajustate (când a fost cazul) după modelul de lucru de mai sus, pentru profesorii cu mai puțin de 9 ore:

P9 <- names(vct_lt9())
lP9 <- map(P9, framedist) %>% setNames(p9) %>% 
       list2DF() %>% t() %>% print.table()
    [,1]     [,2]         [,3]     [,4]         [,5]   
Ch3 11C 12D  11B 11C 12D           11C 12D            
Ch4 12C      12C                                12C      # 3 ore, dar la o aceeași clasă
Fz4                       7A 7B    7A 8B        7B 8B  
Fz5 6A 6B    6A 6B                                    
Gg3                       12E      12E                
IH1 11B 11C  11C 11E               11A 11D 11F        
Is4 11F      11F          11D 11F                        # 3 ore la o aceeași clasă
LE4                       12A 12C               12A 12C
LR8                                11F 6B       10F 12E
Lt1 10E 9E                11F 12F  7A 9F        11F 7B 
Mt7 12E                   12E                         
Ps2 11E 11F                                     12E 12F
Sp3 12F      10E                   12E 9F       11F     # "Sport" ar cere teren liber
Tc3                                10C                

Ch4 are 3 ore la o aceeași clasă, deci nu i-am ajustat distribuția; analog, la Is4, etc.
Sp3 este al 3-lea în ordinea numărului de ore, pe disciplina "Sport" și cele 5 ore ale sale sunt la clase diferite între ele; totuși nu i-am ajustat distribuția (deși putea rezulta și o distribuție cu două sau trei zile libere), fiindcă se pare că pentru "Sport" nu prea încap trei profesori pe un același teren (și mărind numărul de ore pe zi la unul dintre ei, suprapunerea pe teren va fi probabil, greu de evitat).

În R12i.RDS avem acum o repartizare prof|cls|zl a tuturor lecțiilor, care este omogenă pe zile, pe clase și pe profesorii cu măcar 9 ore, este relativ omogenă pe cuplajele existente și este acceptabilă (ca număr de zile libere) pe profesorii cu mai puțin de 9 ore.
Mai departe, avem de repartizat lecțiile dintr-o aceeași zi, pe orele 1:7 ale zilei; ne așteptăm să fie (mult) mai complicat decât în [2], fiindcă acum avem de-a face și cu perechi sau triplete de clase cuplate (de plasat de fiecare dată într-o aceeași oră a zilei, la câte doi sau trei profesori – evitând bineînțeles, suprapunerea cu alte lecții ale acestora)…

Montarea orelor 1:7 pe lecțiile dintr-o aceeași zi

vezi Cărţile mele (de programare)

docerpro | Prev | Next