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

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

graf | limbajul R | orar şcolar
2024 dec

Repartizarea pe zile a tuplajelor

Tuplajul (9A 9C 9D) / (Fr2 Fr3 Gr1), cu două ore (una Lu și cealaltă Mi, v. [1]) este unul „cuminte”: putem asocia 9A lui Fr2, 9C lui Fr3 și 9D lui Gr1, impunând apoi celor trei clase câte o aceeași zi, la cei trei profesori; într-o anumită oră a zilei, aceștia vor împărți cum știu ei, elevii claselor inițiale în trei grupe (pe care le renotăm "9A", "9B", "9C") și va intra fiecare la grupa asociată lui.
… Cât pe ce să uităm: avem și un set separat "tuplaje.RDS" (v. [1]-III) de 5 tuplaje „cuminți” (câte un simultan de doi profesori pe două clase), pentru "Des/Muz".

În schimb, pentru tuplajele de două clase cu trei profesori, sau de trei clase cu patru profesori, nu mai putem asocia "one-to-one", câte o clasă la câte un profesor…
Putem proceda foarte simplu (câtă vreme ne găndim la repartizarea lecților pe zile, nu încă pe orele zilei): ignorăm (deocamdată) unul dintre profesorii „în plus”. Am ales să „ignorăm” Fr3 (care dintre cei angajați în tuplaje are cel mai puține ore proprii, în afara tuplajelor) și Fr1; de exemplu, în tuplajul (10B 10C 10E) / (Fr1 Fr2 Fr3 Gr2) eliminăm "Fr3", iar în tuplajul inițial (11C 11E) / (Fr1 Fr2 Gr1) ștergem "Fr1":

library(tidyverse)
tpl <- readRDS("tuplaje_b.RDS")  # v. [1]
tpl <- edit(tpl)
               cls        prof zl
    1  09A 09C 09D Fr2 Fr3 Gr1 Lu
    2  09A 09C 09D Fr2 Fr3 Gr1 Mi
    3      09B 09E     Fr1 Gr1 Ma   # fără Fr3
    4      09B 09E     Fr1 Gr1 Vi
    5      10A 10D     Gr1 Gr2 Jo   # fără Fr3
    6      10A 10D     Gr1 Gr2 Lu
    7  10B 10C 10E Fr1 Fr2 Gr2 Mi   # fără Fr3
    8  10B 10C 10E Fr1 Fr2 Gr2 Ma
    9  11A 11B 11D Fr2 Fr3 Gr1 Vi
    10 11A 11B 11D Fr2 Fr3 Gr1 Jo
    11     11C 11E     Fr2 Gr1 Lu   # fără Fr1
    12     11C 11E     Fr2 Gr1 Mi
    13     12A 12B     Gr1 Gr2 Ma   # fără Fr1
    14     12A 12B     Gr1 Gr2 Vi
    15     12C 12E     Fr1 Gr2 Jo   # fără Fr3
    16     12C 12E     Fr1 Gr2 Lu

și continuăm programul început mai sus, asociind pentru fiecare tuplaj, câte o clasă câte unui profesor, păstrând zilele alocate în [1] tuplajelor respective:

TP <- map_dfr(1:nrow(tpl), function(i) {
          vcl <- strsplit(tpl[i, 1], " ")[[1]]  # clase
          vpr <- strsplit(tpl[i, 2], " ")[[1]]  # profesori ("one-to-one")
          data.frame(prof = vpr, cls = vcl, zl = tpl[i, 3])
      })
saveRDS(TP, "tuplaje_5.RDS")
       prof cls zl
    1   Fr2 09A Lu    # (9A 9C 9D) / (Fr2 Fr3 Gr1) Lu
    2   Fr3 09C Lu
    3   Gr1 09D Lu
    4   Fr2 09A Mi    # (9A 9C 9D) / (Fr2 Fr3 Gr1) Mi
    5   Fr3 09C Mi
    6   Gr1 09D Mi
    ...

Bineînțeles că atunci când va fi să vedem distribuțiile pe zile ale profesorilor, va trebui să contorizăm la Fr3 și la Fr1 clasele din tuplajele din care i-am exclus mai sus.

Dependențele de alocare induse de cuplaje

Avem (v. [1]) un cuplaj Ds1Mz1 (la clasa 12E) și 4 cuplaje "Fr/Gr" (la 11E, 12D și 12E); constituim listele care asociază profesor sau cuplaj, cu cei (profesori sau cuplaje) de care depinde alocarea lecțiilor sale:

Tw1 <- list(Ds1 = "Ds1Mz1", Mz1 = "Ds1Mz1", 
            Fr1 = c("Fr1Gr1", "Fr1Gr2"), Fr2 = "Fr2Gr1", 
            Gr1 = c("Fr1Gr1", "Fr2Gr1"), Gr2 = "Fr1Gr2")
Tw2 <- list(Ds1Mz1 = c("Ds1", "Mz1"), 
            Fr1Gr1 = c("Fr1", "Gr1", "Fr1Gr2", "Fr2Gr1"), 
            Fr1Gr2 = c("Fr1", "Gr2", "Fr1Gr1"))
save(Tw1, Tw2, file="Tw12.Rda")

Listele Tw1 și Tw2 ne vor fi utile mai ales atunci când vom trece la repartizarea pe ore a lecțiilor dintr-o aceeași zi (o lecție în cuplaj nu poate cădea în aceeași oră cu o lecție a unuia dintre profesorii care formează cuplajul respectiv).

Repartizarea pe zile a lecțiilor prof | cls

Procedând la fel ca în [4], obținem o repartizare pe zile R12 a tuturor celor 1026 lecții prof|cls (am adăugat și cele 10 linii din "tuplaje.RDS"):

> addmargins(table(R12[c('cls','zl')])) [35, ]
      Lu   Ma   Mi   Jo   Vi  Sum 
     205  205  205  205  206 1026 

R12 este echilibrată nu numai pe zile (cum se vede mai sus), dar și pe clase și pe profesori (cu diferență de cel mult 1 între numărul de ore/zi ale clasei, respectiv ale profesorului); de exemplu, distribuția pe profesori și pe zile a lecțiilor de "Română":

Ro <- R12 %>% filter(grepl("Ro", prof))
sink("Ro.txt")
    print(tidy_as_csv(Ro), width=120, print.gap=2, 
          row.names=FALSE, right=FALSE)
sink()
 prof  Lu           Ma               Mi               Jo               Vi             
 Ro1   07A 07B 10D  07A 07B 10D      07A 07B 10D 11C  07A 07B 11C      07A 07B 10D 11C
 Ro2   09A 09B 12E  09B 11D 12E      09A 09B 11D 12E  09A 09B 12E      09A 11D 12E    
 Ro3   06A 06C 08D  06C 08D 09C      06A 08D 09C      06A 06C 08D 09C  06A 06C 09C    
 Ro4   05B 08A 10E  05B 06B 08A 10E  05B 06B 08A 10E  05B 06B 08A      05B 06B 10E    
 Ro5   05C 09D 10C  05C 09D 11A      05C 09D 11A      05C 10C 11A      05C 09D 10C 11A
 Ro6   09E 12B 12C  09E 12A 12B      09E 12A 12B 12C  09E 12A 12C      09E 12A 12B    
 Ro7   08C 10A 12D  08C 10A 11E      08C 11E 12D      08C 10A 11E      10A 11E 12D    
 Ro8   08B 11B      08B 10B          10B 11B          08B 11B          08B 10B        
 Ro9   05A 07C      07C 07D          05A 07C 07D      05A 07C 07D      05A 07D  

Avem poate excepții (distribuții individuale neomogene), la profesori implicați în tuplajele Fr/Gr:

prof    Lu               Ma               Mi           Jo               Vi             
Fr1     05B 12C 11C      06C 09B 10B 12A  06C 10B 11C  08A 12C          05B 08A 09B 12A   
Fr1Gr1  12D                               12D                                          
Fr1Gr2                   12E                                                           
Fr2     05C 09A 11C      10C              09A 10C 11C  07A 11A          05C 07A 11A    
Fr2Gr1                   11E                                                           
Fr3     07B 09C 10A 12C  08B 9B 10B       09C 10B      08B 11B 12C 10A  07B 11B 9B       
Gr1     06A 09D 10A 11E  06A 08C 09E 12A  07C 09D 11E  08C 10A 11D      07C 09E 11D 12A   
Gr2     05A 08D 10D 12E  07D 10E 12B      05A 10E      06B 07D 10D 12E  06B 08D 12B

Am evidențiat aici, prin boldare, cele două lecții (de Lu și Mi) ale tuplajului "cuminte" (09A 09C 09D) / (Fr2 Fr3 Gr1); se pot descoperi ușor și lecțiile celorlalte tuplaje.
Dar să ne amintim că din unele tuplaje scosesem fie Fr3, fie Fr1; mai sus (dar nu în R12, fiindcă am mări artificial numărul de ore la clasele respective) am adăugat (cu blue) și lecțiile „omise”, la câte una dintre clasele tuplajelor respective. De exemplu, tuplajului (10A 10D) / (Fr3 Gr1 Gr2) îi corespund mai sus (10A 10A 10D) din zilele Lu și Jo.

Fr1 de exemplu, are Lu și Mi câte 4 ore, dintre care una (la 12D) în cuplaj cu Gr1 și una în tuplajul pe (11C 11E), împreună cu Fr2 și Gr1; iar Ma, are 5 ore dintre care una în cuplaj cu Gr2 (la 12E) și una în tuplajul pe (12A 12B) cu Gr1 și Gr2.

Dacă vrem neapărat, putem omogeniza și distribuțiile celor implicați în tuplaje (v. [4]); de exemplu, Gr1 are distribuția (5 4 4 3 4) și o putem omogeniza mutându-i o clasă neaflată în tuplaje (06A) din ziua Lu în ziua Jo:

> recast_by_swap("Gr1")  # v. [4]; vizează numai lecțiile proprii
     Gr1            
[1,] 06A 09D 10A 11E  # + 12D (cuplaj)
[2,] 06A 08C 09E 12A
[3,] 07C 09D 11E      # + 12D (cuplaj)
[4,] 08C 10A 11D    
[5,] 07C 09E 11D 12A
Mutăm o clasă - din care Z1 în care Z2: 1 4
 Interschimbăm cu unul dintre profesorii:
1: Is3
 08B 11A 12E
 08B 08C 11A
 05A 08C 12E
 05A 05C 06A 12E
 05C 09B 12C
# ... (încă 8 profesori cu care s-ar putea interschimba o clasă)
 Mutăm una dintre clasele  06A 09D 11E  din  Jo  în  Lu 
  la al câtelea profesor (0 to Cancel): 1
Clasa de interschimbat între Gr1 și Is3: 06A

După interschimbarea Lu ↔ Jo a clasei 06A între Gr1 și Is3, ambii profesori rămân cu distribuții orare omogene. Dar… am avut noroc: Is3 este singurul dintre cei 9 profesori, căruia interschimbarea respectivă nu-i „strică” distribuția orară; așa că nu am mai încercat omogenizarea și pentru ceilalți implicați în tuplaje.

Încercăm reducerea simultanelor…

Instrumentarea din [4] (preluată și aceasta, din [2] și [3]) pentru alocarea pe ore a tuplajelor, viza cazul particular (dar cel mai obișnuit) când în fiecare tuplaj sunt tot atâția profesori cât și clase. Putem încerca să reducem situația curentă (simultane de trei profesori pe două clase, etc.) la acest caz particular (cam cum am făcut mai sus, pentru repartizarea pe zile a tuplajelor)…

Reluăm tuplajele în forma inițială (v. [1]) și le separăm pe zile, adăugând un câmp "ora" cu valoarea inițială 0:

TJ <- readRDS("tuplaje_b.RDS") %>% 
      mutate(ora = 0L) %>%
      split(.$zl) %>% lapply(., select, -zl)
saveRDS(TJ, "tj2.RDS")
>  TJ[["Mi"]]  # pentru ilustrare
               cls             prof ora
    2  09A 09C 09D      Fr2 Fr3 Gr1   0
    7  10B 10C 10E  Fr1 Fr2 Fr3 Gr2   0
    12     11C 11E      Fr1 Fr2 Gr1   0

Să presupunem că pentru ziua Mi ajungem să alocăm pe ore lecțiile clasei 10C (pentru care se vede întâi, că face parte din tuplajul TJ[["Mi"]][2, ]):

R12 %>% filter(cls=="10C", zl=="Mi") %>% select(prof) %>% 
        mutate(ora = sample(nrow(.))) %>% as.data.frame()
  prof ora
1  SP2   5
2  In2   6
3  Sp1   2
4  Mt5   3
5  En5   1
6  Fr2   4

Dacă SP2 de exemplu, nu este liber în ora 5 (fiindcă la o clasă tratată anterior, i s-a alocat deja ora 5), atunci se va genera în coloana ora o altă etichetare cu 1:6.
Să presupunem că alocarea redată mai sus este admisibilă pentru primele 5 linii și ajungem la Fr2 din linia 6; fiindcă Fr2 face parte din tuplajul identificat la început, se consultă valoarea din câmpul "ora" a acestui tuplaj: dacă este 0, atunci se înscrie în loc 4 și alocarea lecțiilor clasei 10C este încheiată (și se va trece la alocarea lecțiilor unei alte clase); dacă este h0, înseamnă că anterior s-a întâlnit una dintre celelalte clase ale tuplajului și deja s-a alocat ora h, pentru tuplajul respectiv — dacă h este chiar 4, atunci alocarea la clasa 10C este iarăși, încheiată; altfel (h ≠ 4), alocarea curentă trebuie respinsă (fiindcă profesorii din tuplaj trebuie să intre simultan, la clasele din tuplaj).

Raționamentul expus mai sus nu este tocmai corect, având în vedere numai tuplajele pe "Fr/Gr"; se poate ca 10C, sau 10E să facă parte și din tuplaje pe alte discipline.
În "tj2.RDS" avem pe zile, tuplajele pe "Fr/Gr"; să formulăm și tuplajele pe "Des/Muz":

TJ <- readRDS("tuplaje.RDS") %>%
      split(.$zl) %>%
      lapply(., function(J) 
          data.frame(cls = paste(J$cls, collapse=" "),
                     prof = paste(J$prof, collapse=" "),
                     ora = 0L))
saveRDS(TJ, "tj1.RDS")
> TJ[["Mi"]]  # ilustrare
          cls     prof ora
    1 10E 11E  Ds1 Mz1   0

Până la urmă… dacă este bună, încercarea schițată mai sus, de "reducere" prin eliminarea unuia, ar impune modificări importante în programul de repartizare pe ore ("mount_h1.R" din [2]) pe care, ca și în [4], vrem să-l folosim aici; în principiu, lecțiile marcate cu blue mai sus (care au fost "reduse" lui Fr3 și Fr1) trebuie totuși, materializate în orar și nu doar subânțelese…

Cuplaje în tuplaje

vezi Cărţile mele (de programare)

docerpro | Prev | Next