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

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

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)

Cizelarea interactivă a repartiției pe zile a lecțiilor

Întâi, echilibrăm distribuțiile claselor care în setul R12 rezultat mai sus, au ajuns să aibă peste 7 ore în vreo zi; apoi, trecem pe rând la cele pentru care diferența între cel mai mare și cel mai mic număr de ore pe zi a rămas mai mare ca 2, respectiv este egală cu 2 (caz în care îi mai zicem „cvasi-omogenă”; ideal ar fi să fie cel mult 1 – adică distribuția respectivă să fie omogenă, sau uniformă).
Ocupându-ne astfel de clase, urmărim mereu să menținem echilibrarea pe zile a distribuției globale (a tuturor lecțiilor din R12) și deasemenea, urmărim să echilibrăm măcar unele distribuții individuale pentru profesori ai claselor respective.
În final, vedem care profesori au rămas cu distribuții neechilibrate și dacă este cazul, le echilibrăm (fără a dezechilibra altele) prin schimbarea uneia dintre lecții (evitând pe cele din P23, pe clase cuplate), din ziua în care sunt multe ore, într-o zi în care sunt mai puține ore.

Probabil că vom realiza planul schițat mai sus în mai multe sesiuni de lucru cu R; ne vom baza de fiecare dată, pe un program "brush.R" prin care – pe lângă pachetul tidyverse – încărcăm setul (curent) R12i.RDS și funcția anterioară change_zl() (plus pentru orice eventualitate, R1.RDS, R2.RDS, Tw12.Rda, vectorul Zile și funcția decompose23()).
În brush.R vom copia când va fi cazul (redând și aici, când avem ceva modificări) unele funcții din [2] care servesc vizualizării corelate a lecțiilor și modificării interactive a setului R12.

1

Având în vedere cum am obținut în R12 repartiția pe zile a lecțiilor (folosind funcțiile mount_days() din [2]), avem puține clase cu mai mult de 7 ore într-o zi:

> cz <- table(R12[c('cls','zl')]) %>%
        apply(., 1, function(row) ifelse(any(row > 7), row, 0))
> names(cz[cz > 0]) %>% print()
[1] "11B"

Pentru o clasă dată putem vizualiza distribuția, pe zile și pe categorii de lecții, prin funcția următoare (față de [2], avem acum și „categoria” dP23 – prin decompose23(), din cls23_days.RDS; în plus, am atribuit nume de linie, prin list2DF() %>% `rownames<-`):

cls_hours <- function(cls_name) 
    map(list(R1, R2, dP23), function(RC)
        RC %>% filter(cls == cls_name) %>% 
        count(zl, .drop=FALSE) %>% pull(n)
    ) %>% setNames(c("R1", "R2", "dP23")) %>% 
    list2DF() %>% `rownames<-`(Zile)
> t(cls_hours("11B")) %>% print()  # t() transpune liniile pe coloane
         Lu Ma Mi Jo Vi
    R1    4  4  5  5  5
    R2    1  0  0  1  2
    dP23  1  0  0  0  1

Așadar, 11B are 8 ore Vi și 4 ore Ma (și este singura clasă cu mai mult de 7 ore într-o zi); care lecție s-o mutăm din ziua Vi, în ziua Ma? În principiu, nu una care există deja în ziua Ma – caz în care profesorul respectiv ar căpăta Ma două ore la o aceeași clasă; nu acceptăm fără motiv serios (principial), ca un profesor să aibă două sau mai multe lecții „identice” într-o aceeași zi, la o aceeași clasă (în particular… dacă are pe o disciplină exact 3 ore în școală, dar la o aceeași clasă – atunci va trebui să vină în trei zile și nu într-una singură).
mount_days() nu repartizează două lecții prof|cls identice într-o aceeași zi, decât dacă disciplina implicată are mai mult de 5 ore pe săptămână; singurii cărora li s-a repartizat aceeași clasă de două ori într-o zi, ar fi eventual unii profesori implicați în cuplaje (din R2, sau din P23), în situația când au multe lecții, incluzând și lecții proprii (în R1).

Următoarea funcție preia – cu o mică îmbunătățire și sub un nume parcă mai potrivit – funcția "prof_in_one_day" din [2], furnizând distribuția lecțiilor acelor profesori care la o clasă dată, au lecții într-o zi dar nu și în cealaltă:

diff_prof <- function(cls_name, z1, z2, RC) {  # RC = R1 | R2 | R12
    Q <- RC %>% filter(cls == cls_name & zl %in% c(z1, z2)) %>%
         split(.$zl)
    P1 <- Q[[z1]]$prof 
    P2 <- Q[[z2]]$prof
    Pr <- setdiff(P1, P2)  # o mică simplificare, față de [2]
    NH <- table(RC[c('prof', 'zl')])[Pr, ]
    if(length(Pr) == 1) {  # când table() ar returna un 'array' 1-dimensional
        dim(NH) <- c(1, 5)
        dimnames(NH) <- list(Pr, Zile)
    }
    NH
} # cei cu ore (în RC) la clasa cls_name în ziua z1, dar NU și în z2

Dar subliniem acest aspect: în RC, câmpul prof trebuie să fie de tip character și nu factor; altfel – fiindcă table() ar angaja indecșii numerici din levels() – în NH ar rezulta alte nume decât cele corecte (noi am avut grijă din timp, ca R2$prof și R12$prof să fie character).

Acum putem folosi funcția recast12() din [2] (copiind-o în brush.R și schimbând numele de funcție menționat mai sus); redăm de pe ecran interacțiunea respectivă:

 >  recast12("11B")
11B 6 4 5 6 8 
index Z1 și index Z2: 5 2
Vi --> Ma     zl
prof  Lu Ma Mi Jo Vi  # au ore Vi, dar nu și Ma
  LR1  4  4  5  4  3
  Rl2  2  2  4  4  3
  Is2  3  4  4  2  3
  LI3  3  1  2  2  2
  LG1  4  3  6  4  4
index-prof, de mutat  (0 = Cancel): 2
	 6 5 5 6 7   # noua distribuție pentru 11B (încă neuniformă)

>  recast12("11B")
11B 6 5 5 6 7 
index Z1 și index Z2: 5 2
Vi --> Ma     zl
prof  Lu Ma Mi Jo Vi  # rămași cu ore Vi, dar nu și Ma
  LR1  4  4  5  4  3
  Is2  3  4  4  2  3
  LI3  3  1  2  2  2
  LG1  4  3  6  4  4
index-prof, de mutat  (0 = Cancel): 3
	 6 6 5 6 6  # 11B are acum o distribuție omogenă  

>  addmargins(table(R12$zl))  # efectul asupra distribuției globale
 Lu  Ma  Mi  Jo  Vi Sum 
185 187 187 184 185 928 

Cele două schimbări de lecții echilibrează distribuția la 11B, dar nu afectează caracterul de "cvasi-omogen" al distribuțiilor individuale pentru profesorii implicați (Rl2 și LI3) și nu afectează prea mult, distribuția pe zile din R12.

După asemenea secvențe de modificări, se cuvine să salvăm în R12i.RDS noua distribuție R12 – încât într-o sesiune de lucru ulterioară, brush.R să încarce ”ultimul” set R12; dar dacă vrem totuși să abandonăm toate modificările făcute între timp și „s-o luăm de la capăt” – atunci putem reinițializa R12i.RDS din seturile R1.RDS, R2.RDS și P23 (acestea, pe tot parcursul, nu sunt afectate de modificări).

2

În setul curent R12, au rămas clase la care diferența de ore pe zi este mai mare ca 2:

dh <- table(R12[c('cls','zl')]) %>%
      apply(., 1, function(row) ifelse(diff(range(row)) > 2, row, 0))
>  names(dh[dh > 0]) %>% print
[1] "10A" "12B"

Să echilibrăm distribuția lecțiilor la clasa 10A:

 >  recast12("10A")
10A 7 4 6 7 7 
index Z1 și index Z2: 1 2  # o primă încercare...
Lu --> Ma     zl
    prof     Lu Ma Mi Jo Vi  # au ore (la 10A) Lu, dar nu și Ma
      LE3     4  4  4  4  3
      Fz1     6  5  5  5  7
      LI2LI1  1  0  1  1  0
      LG2     4  5  4  6  4
index-prof, de mutat  (0 = Cancel): 0  # abandonează

Nu ne-a convenit să schimbăm din Lu în Ma: fie am „strica” echilibrul (la LE3, sau la LG2), fie ar fi nepotrivit în acest moment (la Fz1, la care potrivit ar fi să alegem pentru schimbare lecția de Vi, când are 7 ore), fie ar trebui să verificăm și distribuția altui profesor (în cazul cuplajului LI2LI1, ca și în cazul lui LG2 care are și lecții în P23).

Dacă am schimba de Jo pe Ma, atunci Jo ar rămâne în total 183 de ore; preferăm să schimbăm de Vi (rămânând cu 184 de ore) pe Ma, cu echilibrarea distribuției la Fz1:

 >  recast12("10A")
10A 7 4 6 7 7 
index Z1 și index Z2: 5 2
Vi --> Ma     zl
    prof  Lu Ma Mi Jo Vi
      LE3  4  4  4  4  3
      Fz1  6  5  5  5  7
      Bl1  3  4  4  3  5
      Ps1  3  2  1  1  2
      Tc2  2  3  2  3  3
      LG2  4  5  4  6  4
index-prof, de mutat  (0 = Cancel): 2  
	 7 5 6 7 6  # încă neechilibrat

Până la urmă, pentru a echilibra… trebuie să facem și o schimbare dintre cele două pe care le-am evitat mai sus (fie de Lu fie de Jo, pe Ma):

 >  recast12("10A")
10A 7 5 6 7 6 
index Z1 și index Z2: 4 2
Jo --> Ma     zl
    prof     Lu Ma Mi Jo Vi
      Ch1     4  2  4  4  4
      Is3     4  3  3  3  3
      Bl1     3  4  4  3  5
      Rl2     2  3  4  4  2
      LI2LI1  1  0  1  1  0
      Ds1     4  4  4  4  3
index-prof, de mutat  (0 = Cancel): 1  
	 7 6 6 6 6  

Deocamdată, distribuția globală s-a deteriorat (185 189 187 183 184); dar măcar, am echilibrat și distribuția lui Ch1 (pe lângă cea a clasei 10A).

Procedăm la fel, pentru 12B:

 >  recast12("12B")
12B 5 7 7 5 4 
index Z1 și index Z2: 2 5
Ma --> Vi     zl
    prof  Lu Ma Mi Jo Vi
      Fz3  2  3  3  3  2
      Fl1  4  4  4  3  3
      Tc2  2  3  2  3  3
      LG1  4  3  6  4  4
index-prof, de mutat  (0 = Cancel): 2  
	 5 6 7 5 5  # încă neechilibrat

 >  recast12("12B")
12B 5 6 7 5 5 
index Z1 și index Z2: 3 4
Mi --> Jo     zl
    prof  Lu Ma Mi Jo Vi
      Fz3  2  3  3  3  2
      LR1  4  4  5  4  3
      Bl1  3  4  4  3  5
      Mt3  4  5  5  3  4
      Ch1  4  3  4  3  4
      LG1  4  3  6  4  4
index-prof, de mutat  (0 = Cancel): 4  
	 5 6 6 6 5  
	 
>  table(R12$zl)
         Lu  Ma  Mi  Jo  Vi 
        185 188 186 184 185 
>  saveRDS(R12, "R12i.RDS")    

Echilibrând astfel distribuția la 12B, cea pentru Fl1 rămâne omogenă, iar cea a lui Mt3 devine una omogenă; distribuția globală încă rămâne, ușor „defectuoasă”.

3

Refăcând calculul de mai sus al diferenței dh, dar acum în condiția diff(range(row)) == 2, vedem că există 17 clase la care într-o zi sunt cu două ore mai mult ca în alte zile:

> ndh <- names(dh[dh > 0])
> table(R12[c('cls', 'zl')])[ndh, ]
         zl
    cls   Lu Ma Mi Jo Vi
      10B  7  5  5  5  7  # a muta de Lu și de Vi, în alte două zile 
      10D  5  6  7  6  5  # a muta de Mi, pe Lu sau Vi 
      10E  5  5  6  6  7
      10F  5  5  6  6  7
      11A  7  6  6  6  5  # Lu --> Vi
      11D  7  5  6  5  6
      11E  6  5  5  6  7
      11F  6  5  5  6  7
      12A  6  7  6  5  5  # Ma (188) --> Jo (184)
      12D  5  7  6  5  5
      6A   6  5  7  5  5
      9A   6  7  6  5  7
      9B   6  7  6  5  5
      9C   5  7  7  5  5
      9D   5  7  5  7  5
      9E   5  6  7  6  5
      9F   5  6  6  7  5

Numai la 11A, cele două zile între care ar trebui să schimbăm o lecție pentru a echilibra distribuția existentă, sunt „obligatorii” (din Lu în Vi):

 >  recast12("11A")
11A 7 6 6 6 5 
index Z1 și index Z2: 1 5
Lu --> Vi     zl
    prof  Lu Ma Mi Jo Vi
      Gg1  3  4  3  5  5
      Ch1  4  3  4  3  4
      Rl2  2  3  4  4  2
      LI3  3  2  2  2  1
index-prof, de mutat  (0 = Cancel): 4  
	 6 6 6 6 6

Aparent, am echilibrat și distribuția lui LI3; de fapt, ar trebui să verificăm, fiindcă LI3 are la 11A și ore în cuplajul LI1LI3:

> R12 %>% filter(grepl("LI3", prof)) %>% arrange(zl, cls)
         prof cls zl
    1     LI3 11B Lu
    2     LI3 11D Lu  # Lu: 2 ore
    3  LI1LI3 11A Ma
    4     LI3 11A Ma
    5     LI3 11B Ma
    6  LI2LI3  9A Ma  # Ma: 4 ore
    7  LI1LI3 11A Mi
    8     LI3 11A Mi
    9     LI3 11D Mi  # Mi: 3 ore
    10 LI1LI3 11A Jo
    11    LI3 11A Jo
    12    LI3 11B Jo
    13 LI2LI3  9A Jo  # Jo: 4 ore
    14    LI3 11A Vi
    15    LI3 11B Vi
    16 LI2LI3  9A Vi  # Vi: 3 ore

Deci distribuția orelor lui LI3 rămâne deocamdată cvasi-omogenă (cum era și înainte de a-i muta 11A din ziua 1 în a 5-a).

Încercăm acum să trecem 12A de la Ma la Jo, pentru a reduce totalul de ore pe ziua Ma:

 >  recast12("12A")
12A 6 7 6 5 5 
index Z1 și index Z2: 2 4
Ma --> Jo     zl
    prof  Lu Ma Mi Jo Vi
      Is3  4  3  3  3  3
      Mt2  5  5  3  3  5
      LI1  2  3  2  1  2
      LG3  4  5  3  3  4
index-prof, de mutat  (0 = Cancel): 2  
	 6 6 6 6 5
	 
>  table(R12$zl)
         Lu  Ma  Mi  Jo  Vi 
        184 187 186 185 186 
>  saveRDS(R12, "R12i.RDS") 	 

Distribuția globală s-a apropiat de „uniform” (ar mai fi de mutat o oră Ma --> Lu, pentru uniformizare); iar Mt2 a căpătat o distribuție mai omogenă ca înainte, pentru orelor sale.

O mutare Ma --> Lu s-ar putea face la clasa 12D (există și riscul de a regreta mai târziu…):

 >  recast12("12D")
12D 5 7 6 5 5 
index Z1 și index Z2: 2 1
Ma --> Lu     zl
    prof  Lu Ma Mi Jo Vi
      Tc1  4  5  4  5  3
      Mt4  4  4  3  3  4
      LE1  4  4  4  4  2
      Sp1  4  4  4  5  4
      LG4  4  4  5  4  4
index-prof, de mutat  (0 = Cancel): 1  
	 6 6 6 5 5

Distribuția s-a omogenizat la 12D, a rămas cvasi-omogenă la Tc1 și a devenit omogenă (185 186 186 185 186) pe totalul orelor.

Ne-au rămas 14 clase, la care avem de uniformizat distribuțiile:

> table(R12[c('cls', 'zl')])[setdiff(ndh, c("11A", "12A", "12D")), ]
    cls   Lu Ma Mi Jo Vi
      10B  7  5  5  5  7
      10D  5  6  7  6  5
      10E  5  5  6  6  7
      10F  5  5  6  6  7
      11D  7  5  6  5  6
      11E  6  5  5  6  7
      11F  6  5  5  6  7
      6A   6  5  7  5  5
      9A   6  7  6  5  7
      9B   6  7  6  5  5
      9C   5  7  7  5  5
      9D   5  7  5  7  5
      9E   5  6  7  6  5
      9F   5  6  6  7  5

Folosim ca și în exemplificările de mai sus recast12(), căutând mereu să nu „stricăm”, decât eventual temporar, echilibrele deja obținute (și desigur, să echilibrăm încă unele distribuții individuale). Pare să fie ceva caznă, dar lucrurile decurg din aproape în aproape, „identic” celor desfășurate mai sus (și ne-am abține să le mai detaliem aici).

Dar acest "din aproape în aproape" poate fi cumva, plănuit (cu iz de „inteligență”), ghidându-ne întâi după dispoziția lui '7' pe coloane; pentru Lu --> Ma, candidații sunt 10B și 11D (singurele rămase cu 7 ore în ziua Lu și în mod fericit, cu 5 ore Ma); pentru a păstra omogenitatea distribuției globale, trebuie să transferăm invers, Ma --> Lu, două clase – și avem candidați (situația fericită), 9C și 9D (singurele de fapt, cu 7 ore Ma și 5 ore Lu).
Dacă aceste transferuri Lu <--> Ma reușesc în mod „onorabil”, atunci actualizăm pe ecran tabelul de mai sus și repetăm procedura de transfer între câte două zile (pe câte două clase); iar dacă vedem din datele afișate de recast12(), că transferurile plănuite ar strica „neonorabil” echilibrele existente, atunci le abandonăm și încercăm transferuri pe o altă pereche de coloane, din tabelul curent (și după caz, mai folosim și transferuri bilaterale intermediare, între "7" și "6", sau între "6" și "5").

Folosind acest plan de lucru (dar relaxat, investigând mereu lucrurile și… gândindu-ne în van cum le-am putea „automatiza”), nouă ne-a luat cam 3 ore (probabil, de două-trei ori mai mult decât era necesar) pentru a ajunge la o repartiție R12i.RDS care este omogenă (185 186 185 186 186) și în care fiecare clasă are o distribuție omogenă a lecțiilor sale, pe zilele săptămânii; iar distribuțiile individuale (pentru profesori) sunt în bună parte, omogene (celelalte sunt cvasi-omogene, cum le-a lăsat mount_days()).

Obs. Am repetat de nenumărate ori (prin "Copy&Paste") calculul dh de mai sus, prin care obțineam clasele pentru care în setul R12 curent, diferența dintre cel mai mare și cel mai mic număr de ore pe zi este 2 – iar apoi, selectam distribuțiile corespunzătoare acestora prin table(R12[c('cls', 'zl')])[names(dh[dh > 0]), ]
Se cuvenea desigur, să fi încorporat în brush.R o funcție care să producă direct datele respective (fără să fie necesar și un al doilea apel table()):

cls_diff_hours <- function(d)
    table(R12[c('cls','zl')]) %>%
    apply(., 1, function(row) if(diff(range(row)) == d) row) %>% 
    compact() %>% list2DF() %>% as.matrix() %>% 
    t() %>% `colnames<-`(Zile)

De exemplu, constatăm (cu d=0) că în acest moment, în R12 avem o singură clasă care are în fiecare zi un același număr de ore:

> cls_diff_hours(0)
    Lu Ma Mi Jo Vi
11A  6  6  6  6  6

Și anume, 11A are câte 6 ore/zi; celelalte clase au fie 6 sau 7 ore/zi, fie 5 sau 6 ore/zi (matricea distribuțiilor rezultând prin cls_diff_hours(d=1)).

Obs. Pentru un profesor care are și lecții cuplate, se poate ca distribuția cumulată (incluzând și orele cuplajelor în care este angajat) să aibă o zi cu 7 ore – dar în distribuția curentă R12, clasele implicate să nu aibă, niciuna, 7 ore în ziua respectivă (avem în [2], un astfel de caz). Însă a fost ușor de verificat că în R12i.RDS, nu întâlnim această situație.

4

Următoarea funcție (adaptată din [2]) produce un „tabel” pentru distribuțiile individuale neomogene existente în setul R12 curent, ale profesorilor neimplicați în cuplaje și care au măcar 9 ore pe săptămână:

twins <- names(Tw1) %>% union(names(Tw2)) %>% 
         union(unique(dP23$prof))  # exterior funcției, evitând recalcularea
vct_neunif <- function() { 
    tpz <- table(R12[c('prof', 'zl')])  # pe setul curent R12
    NU <- apply(tpz, 1, function(H) 
                        if(diff(range(H)) >= 2 & sum(H) > 8) H) %>%
          compact() %>% list2DF()
    Prof <- setdiff(names(NU), twins)  # restrânge la R1
    NU %>% select(all_of(Prof))
} # neangajați în cuplaje, cu distribuții curente neomogene, cu măcar 9 ore

Astfel, în R12i.RDS avem în acest moment, 14 distribuții individuale cvasi-omogene (pe totaluri de câte măcar 9 ore):

> vct_neunif()
      Bl2 CC1 ET1 Gg1 Gg2 Is1 Is2 LE2 LR1 LR2 Mt2 Mt5 Ps1 Rl2
    1   4   5   1   4   4   5   4   3   4   5   5   3   3   2
    2   4   3   2   4   5   4   4   4   4   3   4   4   2   3
    3   5   5   1   3   3   3   3   3   5   4   3   2   1   3
    4   4   4   3   4   4   3   2   3   4   3   4   3   1   4
    5   3   3   3   5   4   5   3   5   3   4   5   4   2   3

Ne străduim acum să le omogenizăm și pe acestea, pe cât vom putea.

Mai întâi, asociem unui profesor o matrice cu linii pentru fiecare zi, având o singură coloană pe care înregistrăm (separând cu spațiu) clasele repartizate zilnic acestuia:

framedist <- function(P) {
    ds <- R12 %>% filter(prof == P) %>% split(.$zl)
    for(z in Zile)
        ds[[z]] <- paste(ds[[z]]$cls, collapse=" ")
    frm <- ds[[1]] %>% setNames(P)
    for(j in 2:5)
        frm <- rbind(frm, ds[[j]])
    attr(frm, "dimnames")[[1]] <- rep("", 5)
    frm
}

De exemplu, să vedem pe ecran „cadrul” asociat distribuției curente a lui Bl2:

> framedist("Bl2")
     Bl2             
     "10C 11B 7A 9C" 
     "10C 7A 9C 9F"  
     "5B 7A 7B 9B 9E"
     "5A 8A 9B 9C"   
     "11E 7B 8B" 

Funcția următoare mapează framedist() pe vectorul returnat de vct_neunif() (din care omite P) și este o rescriere categoric simplificată, a funcției cu același nume din [2]:

swap_list <- function(P, z1, z2) {  # z1 și z2 din Zile (nu indecși numerici)
    Prof <- names(vct_neunif())
    D <- R12 %>% filter(prof %in% Prof)
    qPz1 <-  D[D$prof==P & D$zl==z1, 2]  # clasele lui P în z1
    map(setdiff(Prof, P), function(P1) {
        ds2 <- D %>% 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)) 
            return(NULL)  # P1 NU are mai puține clase în z2 ca în z1
        c12 <- intersect(qPz1, qP1z2)
        if(length(c12) && length(intersect(c12, qP1z1)) < length(c12))
            return(framedist(P1))
    }) %>% compact()
}

Văzând distribuția lui P, ne dăm seama din care zi z1 să mutăm una dintre clasele respective în ziua z2 (în scopul uniformizării); apoi, swap_list() ne dă profesorii P1 care: au mai multe ore în z2, decât în z1 (invers față de P); au în z2 clase dintre cele aflate la P în z1 și măcar una dintre acestea, nu figurează la P1 și în ziua z1.

Rezultă un mecanism de uniformizare pe care îl ilustrăm aici pentru Bl2; acesta are distribuția (4 4 5 4 3)), care ar deveni uniformă printr-un transfer Mi --> Vi; dar pentru a nu dezechilibra distribuția globală pe zile, trebuie căutat un alt profesor, căruia să-i transferăm invers clasa respectivă (Vi --> Mi):

> swap_list("Bl2", "Mi", "Vi")  # Bl2 are Mi clasele 5B 7A 7B 9B 9E
ET1       
     "8A"      
     "6B 8B"   
     "5B"      
     "5A 6A 7A"
     "5A 5B 7B"
Gg1                
     "11A 5A 5B 9D"     
     "10A 9A 9B 9E"     
     "10E 11B 11E"      
     "10B 12A 12B 9C"   
     "10E 11E 12D 7A 9E"
LE2                
     "11A 12B 9B"       
     "10B 10D 11E 12F"  
     "11E 12F 9F"       
     "10D 12F 9F"       
     "10B 11A 12B 9B 9F"

Deci avem trei candidați, pentru interschimbarea de clasă dorită; am boldat aici, clasele acestora din ziua Mi precum și acele clase din ziua Vi la care are ore Bl2 în ziua Mi.
Dacă am interschimba cu ET1, trebuie să alegem clasa 7B (și nu 5B, fiindcă ET1 are deja oră la 5B în ziua Mi); cu Gg1 putem alege 7A sau 9E, iar cu LE2 trebuie să alegem 9B.
Urmărind să uniformizăm și distribuția celui cu care interschimbă Bl2, am alege fie Gg1, fie LE2; ținem seama și de faptul că ET1 are numai 10 ore și este mai convenabilă distribuția actuală (1 2 1 3 3), dacă ar fi să-i facem o zi liberă, mutând în vreo operație de interschimbare ulterioară, clasa de Lu în ziua Mi (sau invers).
Să zicem că alegem LE2 (clasa fiind în acest caz, unic determinată); interschimbarea decurge invocând change_zl() de două ori consecutiv:

> R12 <- change_zl("Bl2", "9B", "Mi", "Vi")
> R12 <- change_zl("LE2", "9B", "Vi", "Mi")

Actualizăm apoi, în urma interschimbării tocmai efectuate, tabelul distribuțiilor rămase neomogene – invocând imediat vcl_neunif(); din cei 14, câți aveam inițial, au rămas 12 profesori – fiindcă prin interschimbarea efectuată, s-au omogenizat distribuțiile ambilor profesori implicați.

Repetând mecanismul descris, din aproape în aproape, pentru profesorii rămași – ajungem la un moment dat în situația în care nu mai găsim candidați pentru interschimbări (care să respecte regulile pe care ni le-am propus mai sus); de exemplu, noi ne-am „blocat” (procedând însă chiar mecanic, fără a căuta cel mai potrivite alegeri) când ne-au mai rămas 5 profesori cu distribuții neuniforme:

> vct_neunif()
      ET1 Gg2 Is2 LR1 Ps1
    1   2   5   4   5   3
    2   1   4   4   4   3
    3   1   3   2   4   0
    4   3   4   3   4   0
    5   3   4   3   3   3

Distribuția lui Ps1 (cu două zile libere) poate fi considerată ca acceptabilă, acesta având numai 9 ore, la clase distincte; n-am reușit să facem „acceptabilă” și distribuția celor 10 ore ale lui ET1, iar la ceilalți 3 profesori cam trebuie să ne mulțumim cu faptul că au distribuții cvasi-omogene (având între 3 și 5, sau 2 și 4 ore pe zi).

Dacă ne mulțumim așa, salvăm în R12i.RDS și eventual încheiem – altfel, putem s-o luăm de la capăt (relansând brush.R), schimbând însă ordinea în care abordăm profesorii rămași.
Bineînțeles… noi nu prea „ne mulțumim așa” – dar asta ar fi altă poveste (și cam veche): a vedea eventual, aplicația interactivă HTML+javaScript postată pe Github (instanțiată pentru un alt orar, în /recast).

Repartizarea pe orele zilei

vezi Cărţile mele (de programare)

docerpro | Prev | Next