www.agora.ro

Tic-Tac

Un MODEL pentru problema ORARULUI

Vlad Bazon

Problema orarului este una dintre cele mai cunoscute probleme de algoritmică, până în prezent necunoscându-se nici un algoritm eficient pentru rezolvarea ei.

Formulare generală

Se dă matricea încadrărilor - precizând pentru fiecare profesor numărul de ore la fiecare clasă. Cerința principală este aceea de a obține un orar: o repartizare a profesorilor la clase, pe zilele săptămânii și în cadrul fiecărei zile, în așa fel încât într-o aceeași oră (din zi):

- fiecare profesor să intre la cel mult una dintre clasele asociate lui prin matricea de încadrare;

- la fiecare clasă să intre cel mult câte unul dintre profesorii asociați clasei.

Particularizări remarcabile

În general, judecarea unor cazuri particulare este utilă pentru înțelegerea problemei, a nuanțelor și limitelor ei și pentru intuirea complexității și a șanselor de aplicare practică a unei metode sau alta; în sfârșit, particularizările respective vor fi importante pentru faza de testare și punere la punct a programului.

Pătrate latine

Presupunem că matricea de încadrare este pătratică și are toate elementele egale cu 1 (adică numărul de profesori este egal cu numărul de clase și fiecare profesor are câte o singură oră la fiecare clasă).

Atunci, repartizarea în cadrul unei singure "zile" a orelor respective conduce la un pătrat latin: pe fiecare linie ("oră" a zilei) și de asemenea, pe fiecare coloană ("clasă"), apar toți profesorii (câte o singură dată).

Orarul unui turneu de șah

Presupunem o matrice de încadrare pătratică, în care valorile de pe diagonala principală sunt egale cu 0 și toate celelalte elemente au valoarea 1.

Presupunem că orarul cerut trebuie să îndeplinească următoarea condiție: pentru oricare numere de ordine J și K, dacă profesorul J are oră la clasa K, atunci simultan, profesorul K are oră la clasa J (cu altă interpretare: dacă J "joacă" cu K, atunci și K joacă cu J).

Identificând "profesorii" și "clasele" cu participanții la un turneu de șah (unde fiecare trebuie să joace câte o singură partidă cu fiecare dintre ceilalți jucători), se obține un orar de desfășurare pe runde ("zile"), a partidelor.

 

Fig. 2 - Obținerea rundelor unui turneu de șah

Pentru acest caz există și un algoritm foarte simplu (ca formulare practică) pentru obținerea orarului ([1]). Acest algoritm se poate deduce urmărind Fig. 2: o rundă se obține selectând întâi o latură (neselectată anterior) a bazei piramidei (care este poligon regulat cu număr impar de vârfuri), adăugând apoi diagonalele succesive ale bazei care sunt paralele cu latura selectată și completând cu muchia laterală corespunzătoare vârfului rămas bazei (se poate presupune că există un număr par de jucători - implicând în caz contrar, un jucător fictiv).

Termenii de bază ai problemei

Datele inițiale sunt numerele

nq= numărul de clase (numărul de coloane ale matricei încadrărilor),

nZ=numărul de zile,

nO=numărul (maxim) de ore / zi,

nP= numărul de profesori (numărul de linii ale matricei încadrărilor).

și matricea încadrărilor. Numele profesorilor, denumirile claselor și alte asemenea date inițiale au importanță numai pentru formularea finală a orarului.

Vom nota apoi:

nS= nZ*nO - numărul (maxim) de ore / săptămână la clase,

nV= nq*nS - numărul total de ore (inclusiv toate orele libere), cu precizările următoare (modelând unele condiții firești, întâlnite în practică): există măcar o clasă căreia matricea încadrărilor îi asociază (în total) exact nS ore (dar niciodată mai mult de nS ore); oricare profesor 1..nP are cel mult nS ore în încadrarea sa; matricea încadrărilor ar trebui să asocieze fiecărei clase cel puțin nS-(nZ-1) ore; altfel spus, la fiecare clasă pot exista cel mult (nZ-1) ore libere ("în plus" față de cele atribuite efectiv, prin matricea încadrărilor).

Toate orele libere (dacă există) sunt atribuite unui profesor fictiv (cu numărul de ordine 0); orele libere la clase distincte se pot desfășura simultan (într-o aceeași oră din cadrul unei zile).

Termenul "oră"

În general, "oră" are mai multe înțelesuri; în contextul nostru sunt utile următoarele două semnificații:

- "oră" poate desemna o asociere posibilă (bazată pe matricea încadrărilor) între o anumită clasă și un anumit profesor (eventual, cel fictiv);

- "oră" poate avea și o semnificație temporală - implicând simultan toate clasele (și un anumit număr de profesori).

Vom numi S-oră orice secvență de nq ore (asocieri posibile profesori-clase, cu respectarea ordinii claselor), care sunt posibile simultan (se pot desfășura într-o aceeași oră din cadrul unei aceleiași zile).

Astfel, orarul unei zile este constituit din nO S-ore (și facem abstracție de ordinea acestora); în total, pe întreaga săptămână - avem de stabilit un număr de nS S-ore, astfel încât ansamblul acestora să fie compatibil cu matricea încadrărilor (și cu eventualele condiționări suplimentare).

2.1 Trei principii de bază pentru rezolvare

Ne bazăm soluționarea pe următoarele trei idei:

1. Numerotarea orelor, astfel încât să se poată recunoaște (ușor) asocierea profesor-clasă aferentă. În principiu, numerotăm cele nV ore modulo (nV) prin 0, 1, 2, ..., nV-1; numerotăm clasele modulo (nq) prin 0, 1, ..., nq-1; partajăm cele nV ore celor nq clase, alegând ca toate cele nS ore la clasa k (k=0..nq-1) să fie orele numerotate modulo (nS) începând de la k*nS - adică orele k*nS, k*nS+1, k*nS+2, ..., k*nS+(nS-1). Respectând totuși uzanțele practicii, vom considera în cele ce urmează că numerotarea începe cu 1 (și nu de la 0).

2. Graful orelor: ne imaginăm graful care are ca vârfuri cele nV ore, iar ca muchii - perechile de ore care nu se pot desfășura simultan. Aplicând pe acest graf un algoritm de colorare cu nS culori, obținem cele nS S-ore ale săptămânii (vârfurile de o aceeași culoare desemnând un număr de nV/nS=nq ore posibile simultan). Observăm că S-orele sunt mulțimi independente maximale în acest graf.

3. Graful S-orelor: considerăm acum graful având ca vârfuri cele nS S-ore obținute la 2) și ca muchii - acele perechi de S-ore care nu s-ar putea desfășura în cadrul unei aceleiași zile (de exemplu, pentru motivul că un același profesor ar trebui să facă într-o aceeași zi, două sau mai multe ore la o aceeași clasă). Colorarea vârfurilor acestui graf cu nZ culori, va conduce la obținerea unui orar final (vârfurile de aceeași culoare desemnând un număr de nS/nZ=nO S-ore posibile simultan, într-o aceeași zi).

Observație

O altă schemă posibilă este următoarea: graful de la 2) se colorează cu nZ culori (cele nO*nq vârfuri de aceeași culoare desemnează ore posibile simultan într-o aceeași zi); apoi, se trece la colorarea fiecăruia dintre cele nZ grafuri rezultate, cu câte nO culori (obținând câte un orar pentru fiecare dintre cele nZ zile).

O exemplificare

Consolidăm termenii proiectării și maniera de rezolvare prezentată mai sus, abordând o situație concretă: nq=4 clase, nZ=2 zile, nO=3 ore/zi, nP=5 profesori și următoarea matrice a încadrărilor:

2 0 1 1

2 1 0 0

1 2 2 0

0 1 2 2

0 2 1 2

Profesorul 1 (linia 1 a matricei) are 2 ore la clasa 1 (coloana 1), 1 oră la clasa 3 și 1 oră la clasa 4; clasa 2 (a doua coloană) are ore respectiv cu profesorii 2 (1 oră), 3 (2 ore), 4 (1 oră) și 5 (2 ore) - în total, 6 ore; clasa 1 are 1 oră fictivă (coloana 1 atribuie în total numai 5 ore din cele nS=nZ*nO=6 ore).

Conform 1) - cu numerotare de la 1 - partajăm cele nV=24 ore în nq=4 clase a câte nS=6 ore astfel:

ora: 1 .. 6 7 .. 12 13 .. 18 19 .. 24

clasa: 1 2 3 4

Alocăm pe profesori (în ordinea 1..nP), orele asociate unei clase; de exemplu, orele 1..6 ale clasei 1 se alocă astfel: orele 1, 2 - profesorului 1 (în matricea de încadrare, linia 1 are 2 în coloana 1); orele 3, 4 - profesorului 2 (linia 2 are 2 în coloana 1); ora 5 - profesorului 3 (care are 1 oră la clasa 1); ora 6 - profesorului fictiv 0.

Putem formula tabloul complet al asocierilor prin

prH[]=( 1, 1, 2, 2, 3, 0, 2, 3, 3, 4, 5, 5, 1, 3, 3, 4, 4, 5, 1, 4, 4, 5, 5, 0)

unde prH[i]= profesorul asociat orei i. De exemplu, mulțimea celor 4 ore din încadrarea profesorului 1 este:

{i:prH[i]=1} = {1, 2, 13, 19};

iar {i:prH[i]=0}={6, 24} = mulțimea orelor fictive.

Realizând colorarea precizată la 2), putem obține o listă de nS=6 S-ore - ca în Fig. 3 a); spre exemplu, S-ora (4 10 15 23) - a patra din listă - are interpretarea următoare: la clasele 1, 2, 3, 4 pot intra simultan profesorii prH[4]=2, prH[10]=4, prH[15]=3 și prH[23]=5. Desigur, S-orele obținute vor putea fi exprimate direct prin intermediul tabloului prH[] - Fig. 3 b); astfel, putem folosi direct (2 4 3 5), în loc de (4 10 15 23).

( 1 7 14 20) (1 2 3 4)

( 2 8 18 21) (1 3 5 4)

( 3 9 13 22) (2 3 1 5)

( 4 10 15 23) (2 4 3 5)

( 5 11 16 19) (3 5 4 1)

( 6 12 17 24) (0 5 4 0)

a) ca ore 1..nV b) interpretând prin prH[]

Fig. 3 - lista S-orelor

Considerând apoi graful celor 6 S-ore și colorând vârfurile în nZ=2 culori - cum am precizat la 3) - putem obține un orar final:

Ziua 1: 1 2 3 4 Ziua 2: 1 3 5 4

2 3 1 5 2 4 3 5

3 5 4 1 0 5 4 0

Fig. 4 - un orar final

Alegând - în vederea colorării, la etapa (II) - diverse ordini de parcurgere a orelor 1..nV, am putea obține mai multe seturi de câte nS S-ore; unele dintre acestea nu vor putea conduce la un orar final (în etapa 3)), decât slăbind condițiile de adiacență între S-ore. Astfel, pe lângă cel din Fig. 3, putem obține și setul de S-ore din Fig. 5; presupunând (la fel ca în cazul Fig. 3) că două S-ore sunt adiacente (în graful de la 3)) dacă ele conțin un același element într-o aceeași poziție (adică un același profesor, de două ori în ziua respectivă, la o aceeași clasă), atunci rezultă graful din fig. 5.

s1=(3 4 5 1)

s2=(2 3 1 4)

s3=(1 3 4 5)

s4=(1 5 3 4)

s5=(2 5 3 0)

s6=(0 2 4 5)

Fig. 5 - Set de S-ore și graful asociat

Existența triunghiurilor (s5 - s2 - s4 și respectiv s3 - s2 - s4) face imposibilă colorarea conform 3), cu numai două culori (pentru a obține cele două zile de câte trei S-ore). Se pot elimina triunghiurile prin tăierea legăturii s2 - s4 (adică, acceptând ca profesorul 4 să facă 2 ore în aceeași zi la clasa 4); atunci orarul devine posibil: ziua1 = {s2, s4, s6} și ziua2 = {s5, s3, s1}. Mai general, dacă în graful S-orelor există un subgraf complet cu nZ+1 vârfuri, atunci cel puțin un profesor va trebui să aibă într-o anumită zi două ore la o aceeași clasă.

Elaborarea programului

Pentru formulări și clarificări inițiale (pentru ceea ce s-ar numi "versiunea 0") este suficient să se folosească limbajul cel mai obișnuit - Pascal; nu este cazul de a ne gândi (mai întâi) la proiectarea de interfețe și facilități de operare - deocamdată, programatorul ar trebui să se supună nu utilizatorului, ci propriilor interese de profilare și punere la punct a programului.

Este convenabilă o separare în module precum descriem în continuare; în principiu, prima literă a denumirii unit-ului va indica "rangul" acestuia, iar restul identificatorului va evoca funcționalitatea modulului respectiv.

Unit-ul _Const

Este un fișier de configurare minimală, ținând loc de o interfață mai elaborată (destinată transmiterii datelor inițiale).

Listing: _CONST.PAS

interface

const

nq=12; { numar de clase }

nZ=8; { numar de zile }

nO=4; { numar (maxim) de ore/zi }

nP=37; { numar de profesori }

fMI='h:\bp70\VB\VB_orar\12_37M.txt';

{ nume complet pt. fișierul în care }

{ s-a scris matricea de încadrare }

fHS='h:\bp70\VB\VB_orar\12_37S.txt';

{ unde se vor depune cele nS "S-ore" }

{ ce vor fi obținute }

fHZ='h:\bp70\VB\VB_orar\12_37Z.txt';

{ pentru orarul final, în forma }

{ "S-ore" pe zile }

nS=nZ*nO;

{ numar maxim de ore/saptamana la clase }

nV=nq*nS; { nr. total de ore (vârfuri }

{ în graful de la 2)) }

Evident, alegerea de dimensiuni constante (modificabile doar prin recompilare) nu ține seama de interesele utilizatorului (dar convine pentru elaborarea unei prime versiuni a programului). Ignorăm aici și necesitatea unui modul de verificare a consistenței datelor primite.

Unit-ul AsetPrCl;

Asigură, conform numerotării de la 2), construcția tablourilor prH[] și clH[], pe baza matricei încadrărilor:

prH[i]:=prof. 1..nP asociat orei i=1..nV (sau, i:=0 dacă ora i este fictivă);

clH[i]:=clasa 1..nq corespunzătoare orei i.

Fie setPrCl procedura care va realiza citirea din fișierul indicat prin _Const.fMI și va încărca corespunzător prH[] și clH[].

Exercițiu: realizați unit-ul AsetPrCl.

Unit-ul BordParc

Pregătește etapa 2), permițând o alegere a ordinii de parcurgere a orelor 1..nV (în vederea colorării cu nS culori, de la 2)).

Listing: BORDPARC.PAS

interface

uses _Const, AsetPrCl;

type OrdineParc=(NAT, ORA_CLASE, GRADE, ALTAORD);

var OrdParc:OrdineParc;

MdH:array[1..nV] of Word;

Hora:array[1..nV] of Byte;

{ MdH[] conține orele 1..nV într-o ordine }

{ modificată (față de cea naturală) }

{ Hora[j] = "S-ora" în care va fi }

{ repartizată ora a j-a din MdH[] }

procedure pOraClase;

{ setează MdH[] astfel încât să se }

{ parcurgă ora I-a de la fiecare clasă, }

{ apoi a II-a de la fiecare clasă, etc. }

procedure pGrade;

{ asigură parcurgerea orelor 1..nV ținând }

{ cont de numărul de legături (de gradele }

{ vârfurilor, în graful orelor de la 2)) }

procedure pAltaOrd;

procedure recOrdNat;

{ ordonează Hora[] conform ordinii }

{ naturale 1..nV - încât Hora[j] să indice }

{ "S-ora" orei j și nu pe a orei MdH[j] }

implementation

. . . . . . . .

Begin

OrdParc:=NAT;

{ ordinea implicită de parcurgere }

End.

Exercițiu: realizați procedurile pOraClase, recOrdNat; gândiți-vă la o parcurgere a orelor care să nu depindă de ordinea claselor (și a profesorilor).

Folosind una sau alta dintre aceste modalități de parcurgere, se asigură obținerea mai departe a mai multor seturi (foarte diferite) de câte nS S-ore. Timpul cel mai bun va corespunde (în general) acelei ordini de parcurgere care minimizează numărul de reveniri în arborele de căutare a soluțiilor (pe parcursul cunoscut al aplicării metodei backtracking); de aceea, unit-ul schițat mai sus este punctul critic al programului.

Unit-ul CsetHora

Asigură obținerea celor nS S-ore, conform 2). Schema generică (backtracking) este următoarea:

procedure SetHora(nivel:Word);

var COLOR:Word;

begin

if nivel>nV then STOP:=TRUE

else

for COLOR:=1 to nS do

begin

OraCurent:=MdH[nivel];

if OraCurent se poate colora cu COLOR

{ adică este posibilă simultan cu }

{ orele colorate anterior cu COLOR }

then

begin

Hora[nivel] := COLOR;

SetHora[nivel+1);

if STOP then Exit

end

end

end;

unde STOP este o variabilă a unit-ului, inițializată cu FALSE și care servește pentru rezumarea procesului de căutare la (numai) prima soluție.

Exercițiu: realizați procedura SetHora.

Exercițiu: presupunând că s-a ajuns la nivel=nV+1 (deci s-a obținut prima soluție) - de câte ori se va executa end (respectiv, cele trei instrucțiuni end din finalul textului de mai sus)? Gândiți-vă la modalități de a forța revenirea directă (în punctul din care s-a apelat setHora(1).

Unit-ul DwrHore;

Prin procedura WriteHore, înscrie cele nS S-ore obținute (notând profesorii 0..nP asociați orelor conținute în S-ore) în fișierul indicat de _Const.fHS.

Exercițiu: realizați WriteHore.

Depunând S-orele obținute într-un fișier, se rezervă utilizatorului posibilitatea de a modifica (dacă se pricepe) unele dintre S-ore, înainte de a continua cu etapa 3).

Unit EgetHore

Furnizează tabloul

HoS:array[1..nS, 1..nq] of Byte;

în care (prin procedura GetHore) citește S-orele existente în fișierul _Const.fHS.

Exercițiu: realizați GetHore.

Exercițiu: gândiți-vă la o procedură de ordonare corespunzătoare a S-orelor din Hos[] (încât colorarea care urmează la 3) să se obțină într-un timp cât mai bun).

Unit FsetZiHO

Furnizează tabloul ZiH:array[1..nS] of Byte; care va fi completat de procedura de colorare:

SetZiHo(nivel:Word)

astfel încât ZiH[j] = culoarea (ziua) atribuită S-orei a j-a din HoS[].

Exercițiu: realizați SetZiHo() (analog cu CsetHora.SetHora()).

Unit-ul GwrZile

Prin procedura WriteZile, înscrie orarul final obținut (sub forma "Ziua", S-orele acelei zile), în fișierul _Const.fHZ (folosind ZiH[] și Hos[]).

Exercițiu: realizați WriteZile.

Unit-urile suplimentare HlistaPr și IminFer

Sunt destinate formatării orarului obținut și respectiv, minimizării numărului de ferestre.

Exercițiu: realizați funcția ListaOre(p:Byte) care să returneze (ca string) lista orelor profesorului p;

Exemplu

1: |-DA-B-| |DAB---| ...

ar însemna că profesorul 1 are în prima zi ore la clasele D (a II-a din zi), A (a III-a), B (a V-a) și are fereastră ora a IV-a; în a doua zi are primele trei ore, la clasele notate D, A, B; etc.

Exercițiu: realizați o funcție de extragere și listare a orarului unei clase precizate.

Exercițiu: realizați o funcție care să dea numărul de ferestre dintr-o zi (din cadrul orarului obținut anterior). Realizați o procedură care să ordoneze S-orele dintr-o zi astfel încât numărul de ferestre să fie minim.

Asamblarea modulelor

Programul vizat de modulele descrise mai sus are forma următoare:

Listing MAIN.PAS  
 uses _Const,  
 { dimensiuni,  matricea de incadrare    } 
 AsetPrCl,  
 { numerotarea orelor,  conform 1) }    
 BordParc,  
 { ordinea repartizarii orelor in S-ore    } 
 CsetHora,  
 { obtine S-orele,  conform (II) si    BordParc } 
 DwrHora,  
 { pune la dispozitie S-orele    obtinute } 
 EgetHore,  { incarca S-orele (eventual    } 
 { modificate manual) }    
 FsetZiHo,  
 { repartizeaza S-orele    pe zile,  } 
 { conform 3) } 
 GwrZile; { scrie orarul final - nZ    } 
 { secvente de câte nO    "S-ore } 
 Begin 
 SetPrCl; 
 SetHora(1); 
 WriteHore; 
 GetHore; 
 SetZiHO(1); 
 WriteZile 
 End. 
 

Se va putea considera un alt program, pentru minimizarea numărului de ferestre în orarul obținut și pentru formatarea orarului:

 Listing: MAIN2.PAS  
 uses _Const,  HlistaPr,  IminFer;    
 var s:Byte; 
 Begin 
 GetOrar; { citeste orarul din _Const.fHZ    } 
 for s:=1 to nZ do    
 MinimizNrFer(s);{ reordonează S-orele    } 
 { din ziua s,  incat numarul    total de } 
 { ferestre să fie minim    } 
 PreiaDenumiri; { nume profesori,  }    
 { denumiri clase,  nume    materii } 
 WrOrarPr; { listeaza orele pe } 
 { profesori,  apeland ListaOre()    } 
 WrCondica { listeaza orarul sub forma    } 
 { obisnuita in condica    } 
 ... 
 End. 

 

Bibliografie

1.

V. Bazon - O clasă de pătrate latine și aplicații, G.M.m. nr. 1-2/1983

2. V. Bazon - Factorizarea grafului K2n și aplicații, G.M.m. nr. 1/1987

3. V. Bazon - Limbaje și calculator, Ed. Petrion 1998

4. N. Christofides - Graph theory - an algorithmic approach, 1975

5. L. Livovschi, H. Georgescu - Sinteza și analiza algoritmilor, 1986

Vlad Bazon este profesor la Liceul "Anghel Rugină" din Vaslui. Poate fi contactat prin e-mail la vbazon@mail.quantum.ro

[cuprins]