121
UNIVERSITEIT GENT FACULTEIT ECONOMIE EN BEDRIJFSKUNDE ACADEMIEJAAR 2006-2007 Planning van operatiekwartieren: een heuristische benadering door Kurt Van Damme Scriptie voorgedragen tot het bekomen van de graad van: Master in de Toegepaste Economische Wetenschappen Promotor: Prof. Dr. M. VANHOUCKE Assistent: B. MAENHOUT

Planning van operatiekwartieren: een heuristische benadering

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

UNIVERSITEIT GENT

FACULTEIT ECONOMIE EN BEDRIJFSKUNDE

ACADEMIEJAAR 2006-2007

Planning van operatiekwartieren:

een heuristische benadering

door Kurt Van Damme

Scriptie voorgedragen tot het bekomen van de graad van:

Master in de Toegepaste Economische Wetenschappen

Promotor: Prof. Dr. M. VANHOUCKE

Assistent: B. MAENHOUT

II

III

UNIVERSITEIT GENT

FACULTEIT ECONOMIE EN BEDRIJFSKUNDE

ACADEMIEJAAR 2006-2007

Planning van operatiekwartieren:

een heuristische benadering

door Kurt Van Damme

Scriptie voorgedragen tot het bekomen van de graad van:

Master in de Toegepaste Economische Wetenschappen

Promotor: Prof. Dr. M. VANHOUCKE

Assistent: B. MAENHOUT

IV

"Permission”

Kurt Van Damme

V

Woord vooraf

Ik zou graag enkele mensen willen bedanken die mij geholpen hebben bij het verwezenlijken van

deze scriptie:

Mijn promotor Prof. Dr. Vanhoucke voor het creëren van de mogelijkheid en Broos Maenhout

voor zijn vlugge respons op elke vraag en zijn algemene behulpzaamheid.

De forumleden van Daniweb bij het helpen van een beginneling met de programmeertaal C.

Mijn vriendin voor het bezorgen van steun en een rustige omgeving.

VI

Inhoudsopgave

1 Inleiding ........................................................................................................................................1

2 Probleemdefiniëring ......................................................................................................................2

2.1 Opzet simulatiemodel.............................................................................................................2

2.2 Objectieven van de thesis.....................................................................................................11

3 Literatuur.....................................................................................................................................12

3.1 Planning OK.........................................................................................................................12

3.2 Algoritmes............................................................................................................................17

3.2.1 Genetische algoritmes ...................................................................................................17

3.2.2 Lokale zoekmethoden ...................................................................................................19

3.2.3 VNS...............................................................................................................................21

4 Programma ..................................................................................................................................23

4.1 Hardware en software...........................................................................................................23

4.2 Samenvatting........................................................................................................................24

4.3 Generatie probleeminstanties ...............................................................................................29

4.4 Beschrijving doelfunctie ......................................................................................................32

4.5 Beschrijving genetische algoritmes......................................................................................37

4.6 Beschrijving lokale zoekmethoden ......................................................................................41

4.7 Beschrijving VNS ................................................................................................................47

4.8 Opzet ....................................................................................................................................49

5 Resultaten ....................................................................................................................................51

5.1 5000 iteraties ........................................................................................................................51

5.1.1 Overzicht waarden.........................................................................................................51

5.1.2 Tijdsgebruik ..................................................................................................................52

5.1.3 Oplossingskwaliteit .......................................................................................................54

5.1.4 Efficiëntie ......................................................................................................................57

5.1.5 Invloed aantal patiënten ................................................................................................58

5.2 Variabel aantal iteraties........................................................................................................59

6 Conclusie.....................................................................................................................................62

6.1 Uitbreidingen........................................................................................................................62

6.2 Conclusie..............................................................................................................................64

VII

Bijlage 1: blokschema ....................................................................................................................66

Bijlage 2: verscheidene “lijst.txt”...................................................................................................67

1000 iteraties met maximaal 100 opeenvolgende iteraties zonder verbetering .........................67

1000 iteraties met maximaal 20 opeenvolgende iteraties zonder verbetering ...........................68

20000 iteraties met maximaal 400 opeenvolgende iteraties zonder verbetering .......................69

Bijlage 3: C-code...........................................................................................................................70

Probleeminstanties genereren.....................................................................................................70

Datastructuur ..............................................................................................................................73

Sancties berekenen .....................................................................................................................75

Lokale zoekmethoden ................................................................................................................84

Genetisch algoritme 0...............................................................................................................106

Genetisch algoritme 1...............................................................................................................108

Bibliografie...................................................................................................................................111

VIII

Lijst van figuren

Figuur 1: opzet operatiekamers in hospitaal.txt ...............................................................................3

Figuur 2: opzet blokken in hospitaal.txt...........................................................................................4

Figuur 3: opzet tafels........................................................................................................................6

Figuur 4: opzet chirurgen in hospitaal.txt ........................................................................................7

Figuur 5: opzet personeel in hospitaal.txt ........................................................................................8

Figuur 6: opzet discriminatie tussen major- en minor operaties in hospitaal.txt .............................8

Figuur 7: samenvatting simulatieomgeving ...................................................................................10

Figuur 8: flowchart operatieproces (Coulier en Alvarado-Vargas, 2006, blz. 4)...........................14

Figuur 9: lokaal maximum en –minimum (www.doc.ic.ac.uk/~sgc/teaching/v231/lecture3.html,

2005).......................................................................................................................................21

Figuur 10: mappenstructuur ...........................................................................................................24

Figuur 11: vraagstelling en sancties ...............................................................................................29

Figuur 12: presentatie van een planning ........................................................................................29

Figuur 13: probleeminstantie .........................................................................................................30

Figuur 14: schema genetisch algoritme 0.......................................................................................39

Figuur 15: schema genetisch algoritme 1.......................................................................................40

Figuur 16: sanctiewaarden, iteraties en populatie-elementen in hospitaal.txt...............................50

Figuur 17: lijst.txt...........................................................................................................................51

Figuur 18: aantal milliseconden en behandelde elementen per lokale methode............................52

Figuur 19: tijdsduur en aantal behandelde elementen per bestand.................................................53

Figuur 20: procentuele sanctieafname per methode.......................................................................54

Figuur 21: sancties per lokale zoekmethode ..................................................................................55

Figuur 22: grafiek sancties per lokale zoekmethode......................................................................56

Figuur 23: sanctieafname en aantal milliseconden per methode....................................................57

Figuur 24: relatie aantal patiënten en eindsanctie ..........................................................................58

Figuur 25: eindsancties met 1000 en 5000 iteraties .......................................................................60

Figuur 26: eindsancties en tijdsgebruik bij verschillende opzet ....................................................60

Figuur 27: beginsanctie en sanctieafname bij verschillende opzet ................................................61

IX

Afkortingen

AHHK: algemene heelkunde

GIHK: gastro-enterologie

NEURO: neurologie

OK: operatiekwartier

ORTH: orthopedie

PACU: post anesthesia care unit; ontwaakzaal

PLAST: plastische chirurgie

TVHK: cardio-vasculaire chirurgie

VNS: variable neighbourhood search; variabele omgevingszoektocht

1

1 Inleiding

Één van de duurste middelen in een hospitaal is het operatiekwartier (OK). Aangezien tot 70%

van alle hospitalisaties een verblijf in het OK inhoudt, is een optimaal gebruik van de OK-

capaciteit van cruciaal belang (Oostrum et al., 2002, blz. 2).

De budgetten van publieke en private hospitalen nemen echter af om economische redenen.

Zulke situaties leiden tot het verminderen van medisch personeel, meer patiënten per arts en het

delen van kamers en apparatuur. Artsen ondervinden meer stress en apparatuur wordt frequenter

gebruikt (Guinet en Chaabane, 2003 blz. 69-70).

In de meeste ziekenhuizen vormt de operatiekamer een knelpunt. Daarbij komt nog dat ze meer

dan 9 % van het jaarlijkse budget opslorpt (Marcon et al., 2003, blz. 83). Effectieve planning van

OK, met als doel kostenreductie, terwijl een goede gezondheidszorg onderhouden wordt is één

van de belangrijkste prioriteiten geworden (Jebali et al., 2006, blz. 52).

Met behulp van een betere operatietoewijzing aan de verschillende operatiekamers wordt een

manier gezocht om overwerk en onderbenutting te minimaliseren, alsook de wachttijden van

patiënten tussen de hospitalisatiedatum en de operatiedatum zodat hun tevredenheid wordt

verbeterd en de beschikbaarheid van bedden op peil blijft. De doelfunctie wordt omgezet in een

kostenfunctie. Teil (2000) analyseerde de lasten van een chirurgisch centrum. Ze toonde aan dat

de vaste kosten enorm zijn in vergelijking met de variabele kosten. Daaruit volgt dat ongebruikte

operatiekamers een geduchte kost vertegenwoordigen (Jebali et al., 2006, blz. 54).

Ook overbezetting brengt ongewenste kosten met zich mee. Buiten de regelmatige openingsuren

wordt het personeel betaald voor overwerk. Dexter (2001) beweert dat kosten in verband met

overwerk ongeveer 1,75 de regelmatige uurkost uitmaken. Bovendien stijgt de hospitalisatiekost

sterk met het aantal dagen dat de patiënt zich in het hospitaal bevindt, wachtend op de operatie

(Jebali et al., 2006, blz. 54).

2

Systemen die afspraken regelen worden gebruikt in menig dienstensectoren. Ze verhogen het

gebruik van de beschikbare middelen, doen de werkbelasting overeenkomen met de voorhanden

zijnde capaciteit en effenen het klantenverloop (Denton en Gupta, 2002, blz. 1003).

Ook in de ziekenzorg kan een geautomatiseerd systeem soelaas bieden. Het verbeteren van OK-

gebruik omvat het vinden van planningen die de chirurgische beperkingen en middelen tegemoet

komen, zodat de operatiekamers ten volle benut kunnen worden terwijl de arbeidskosten

geminimaliseerd worden (Marcon et al., 2003, blz. 84).

Deze scriptie tracht dan ook een bijdrage te leveren bij het verkrijgen van zo optimaal mogelijke

OK-planningen, rekening houdend met vooraf bepaalde beperkingen en prioriteiten.

Dit werk begint met een probleemdefiniëring. De virtuele hospitaalomgeving waarbinnen

gewerkt wordt zal worden wordt besproken, alsook de accenten van de scriptie. In hoofdstuk 3

wordt nadien kort literatuur besproken die hospitaalplanning behandelt en de in dit werk

gebruikte heuristiek toelicht. Hoofdstuk 4 beschrijft het gehele programma en hoofdstuk 5 de

behaalde resultaten. Tenslotte worden in hoofdstuk 6 enkele uitbreidingen voorgesteld en

uiteindelijk volgt de conclusie. Enkele belangrijke code van het programma wordt meegegeven in

de bijlage.

2 Probleemdefiniëring

2.1 Opzet simulatiemodel

Het simulatiemodel is geen exacte kopie van de hospitaalomgeving in het universitair ziekenhuis

in Gent of van een ander ziekenhuis. Een aantal gegevens zijn noodgedwongen gebaseerd op

veronderstellingen of werden gekozen teneinde het model enige logica mee te geven. Zo zal

bijvoorbeeld geen enkele operatiekamer eerder sluiten dan het einde van een blok die het omvat.

Daar waar mogelijk werd zoveel mogelijk rekening gehouden met data beschikbaar uit de

gelezen literatuur. De belangrijkste gegevens zijn vlug aan te passen in “hospitaal.txt”.

3

Planningshorizon

Er wordt gewerkt met een planningshorizon van 1 werkweek, van maandag (dag 1) tot en met

vrijdag (dag 5).

Operatiekamers

In overeenstemming met de UZ2 eenheid wordt er gewerkt met 12 operatiekamers die

beschikbaar zijn voor alle disciplines (Coulier en Alvarado-Vargas, 2006, blz. 2). De dertiende

operatiekamer waar spoedoperaties aan worden toegewezen, en dus ook deze spoedgevallen,

wordt buiten beschouwing gelaten. Elke dag wordt gebruik gemaakt van alle 12 de

operatiekamers. Elke kamer opent om 7:15. Het sluitingsuur is afhankelijk van de ingesloten

blokken en is dus voor elke kamer anders. Na het gewone sluitingsuur kent elke operatiekamer

een periode van 3 uur waarbinnen overwerk geduld wordt, mits extra sanctie. Onderstaande

figuur toont de tijden van elke operatiekamer zoals ingegeven in “hospitaal.txt”. Doorheen gans

het model wordt gewerkt met minuten, waarbij 0 minuten gelijk wordt gesteld aan 00:00 uur.

Figuur 1: opzet operatiekamers in hospitaal.txt

4

Blokken

Patiënten worden in een planning echter niet aan een kamer, maar aan een blok toegewezen.

Aangezien elk blok een vaste dag en operatiekamer heeft, zijn deze gegevens automatisch bekend

wanneer het bloknummer gekend is. Op deze manier hoeft naast een kamernummer niet telkens

een dag en een bloknummer toegewezen worden.

De blokindeling wijzigt elke dag. Iedere dag moeten wel alle disciplines vertegenwoordigd zijn,

zodat het programma niet vastloopt. Op het aantal blokken per kamer staat geen beperking. In het

model telt elke kamer echter op elke dag 1 of 2 blokken1. Bijlage 1 toont een visuele voorstelling.

Figuur 2: opzet blokken in hospitaal.txt

1 De blokken in de operatiekamers: 0, 1, 5 en 11 werden overeenkomstig Modeling and Simulating of Operating

Room Scheduling door Coulier C. en Alvarado-Vargas M. ingevuld. Wegens overdadig aanwezig werd nadien in

deze kamers de tijd van discipline 4 (orthopedie) ingekort. De resterende blokken werden willekeurig aangevuld,

rekening houdend met de totale beschikbare tijd van iedere discipline.

5

Het sluitingsuur van elke operatiekamer wordt gelijk gesteld aan het einde van het laatste blok die

de kamer omvat in de planningshorizon. Doorgaans zal een kamer dus 4 dagen langer open zijn

dan nodig. Het accent ligt echter op de blokken.

Disciplines

De operaties worden ingedeeld in 6 disciplines (Coulier en Alvarado-Vargas, 2006, blz. 3):

• Discipline 1: gastro-enterologie (GIHK)

• Discipline 2: algemene heelkunde (AHHK)

• Discipline 3: neurochirurgie (NEURO)

• Discipline 4: orthopedie (ORTH)

• Discipline 5: plastische chirurgie (PLAST)

• Discipline 6: cardio-vasculaire chirurgie (TVHK)

Elke discipline telt een hoog aantal mogelijke operatietypes. Het type operatie heeft een invloed

op de operatieduur en de materiaalvereisten (Coulier en Alvarado-Vargas, 2006, blz. 19). Alle

operatietypes in het model implementeren zou teveel moeilijkheden opleveren. Daardoor worden

alle operaties opgedeeld in lange en korte operaties, respectievelijk major- en minor operaties.

Het programma wijst iedere patiënt willekeurig één van de zes disciplines toe. De verdeling van

geschatte vereiste tijden is echter telkens anders. Per probleeminstantie heeft dus niet elke

discipline dezelfde hoeveelheid tijd nodig. Dit kan leiden tot een capaciteitsverdeling die niet in

overeenstemming is met de probleeminstanties. De uiteindelijke verdeling is als volgt: GIHK

heeft 94 uren, AHHK heeft 94,5 uren, NEURO heeft 96,5 uren, ORTH heeft 94,5 uren, PLAST

heeft 89 uren en TVHK heeft 112,5 uren. Deze getallen vormen de som van de duurtijden van de

overeenstemmende blokken.

6

Tafels

Voor een patiënt geopereerd kan worden, moet hij verplaatst worden van zijn bed naar een

operatietafel. Deze tafels zijn niet gebonden aan een operatiekamer. Er zijn echter maar een

gelimiteerd aantal tafels beschikbaar (Coulier en Alvarado-Vargas, 2006, blz. 19).

Er zijn vijf soorten operatietafels. Niet elke tafel kan dienen voor alle disciplines. Volgend

overzicht toont de vijf tafels, hun afkorting, hun aantal en de disciplines waar elke tafel bij

gebruikt kan worden (Coulier en Alvarado-Vargas, 2006, blz. 20)2.

Figuur 3: opzet tafels

Het aantal beschikbare tafels wordt ingegeven in “hospitaal.txt”. Doordat in het model vijf

disciplines eventueel gebruik kunnen maken van een buigtafel, zijn van dat tafeltype het meest

exemplaren voorhanden.

Chirurgen

De operatie wordt uitgevoerd door een specifieke chirurg. Anesthesievereisten zijn over het

algemeen flexibeler en verschillende anesthesisten kunnen de anesthesie uitvoeren. Artsen,

chirurgen en anesthesisten zijn slechts in staat een gelimiteerd aantal interventie-uren te

verwezenlijken (Guinet en Chabaane, (2003), blz. 71). De chirurgen worden opgedeeld volgens

discipline. Zij zullen enkel opereren in een overeenstemmend blok. Patiënten krijgen geen

individuele chirurg toegewezen. Er wordt verondersteld elke chirurg in aanmerking komt om elke

operatie van dezelfde discipline uit te voeren. Het aantal interventie-uren wordt per discipline op

dagelijkse basis bekeken.

2 Enkel de aantallen met betrekking tot de tafel met carbon laken, de knietafel en de tractietafel werden in de aangehaalde dissertatie gevonden. Het aantal buigtafels en het aantal tafels met vlak laken werd autonoom ingevuld.

7

Het model telt 16 chirurgen met elk hun dagelijkse beschikbare tijd. Ook wanneer elk blok volzet

is zonder overbezet te zijn, bestaat de mogelijkheid dat chirurgen overbelast raken. Hiervoor zal

het programma rekening moeten houden met de pre- en post-operatietijden en de kuis- en

opsteltijden. Deze tijden zorgen namelijk voor blokbezetting, maar vereisen geen aanwezigheid

van een chirurg.

Figuur 4: opzet chirurgen in hospitaal.txt

Overig personeel

Het hospitaal telt 12 anesthesisten (Coulier en Alvarado-Vargas, 2006, blz. 41). Het is wettelijk

verplicht dat er tijdens een operatie minstens één anesthesist aanwezig is in de operatiekamer

(Coulier en Alvarado-Vargas, 2006, blz. 15).

Tijdens de operaties zijn er drie verplegertypes aanwezig. Er is de instrumentele verpleger die het

materiaal behandeld en het aan de chirurg geeft. Verder is er de lopende verpleger die de

instrumentele verpleger steriel materiaal aanbiedt. Tenslotte is er de anesthesie verpleegster die

de anesthesist assisteert. Gemiddeld zijn er iedere dag zo’n 2,5 verpleegsters per operatiekamer

beschikbaar (Coulier en Alvarado-Vargas, 2006, blz. 15). Major-operaties vereisen meer

verplegers dan minor-operaties (Coulier en Alvarado-Vargas, 2006, blz. 25)

8

Na een operatie, moet de operatiekamer gekuist worden. Major-operaties vereisen een langere

kuistijd dan minor-operaties. Het kuisen zelf gebeurt door twee soorten personeel. Eerst is er een

kuisteam dat de vloeren voor zijn rekening neemt, vervolgens is er een logistiek team dat de

tafels en het materiaal behandelt (Coulier en Alvarado-Vargas, 2006, blz. 15).

Volgend overzicht toont de exacte aantallen van ieder personeelstype en hun dagelijkse

beschikbare tijd in het simulatiemodel.

Figuur 5: opzet personeel in hospitaal.txt

Zoals vermeld vereisen major-operaties een langere kuis- en opsteltijd en meer verplegers dan

minor-operaties. Volgende figuur toont de variabelen die opgedeeld worden naargelang het om

major- of minor-operaties gaat. De ingegeven tijden stellen opnieuw het aantal minuten voor.

Figuur 6: opzet discriminatie tussen major- en minor operaties in hospitaal.txt

9

Probleeminstanties

Het beschreven simulatiemodel moet uiteindelijk zo goed mogelijk tegemoet komen aan de

vereisten van de patiënten. Om deze patiënten te simuleren worden probleeminstanties opgesteld.

Dit zijn patiëntenlijsten waarbij voor elke patiënt de vereisten en kenmerken worden vermeld.

Kenmerken zoals discipline, tafel en de geschatte tijden worden conform de probleemdefiniëring

opgesteld. De tijden hebben betrekking op het pre-operatief proces, de anesthesie voor de

operatie, de eigenlijk operatie en het anesthesie proces na de operatie. Bij het aanmaken van de

probleeminstanties wordt aan elke patiënt voor elk van de beschouwde stadia willekeurig een

geschatte duur meegegeven. Deze tijden moeten gegenereerd worden uit de volgende intervallen

(in minuten)3:

operatie

pre op pre an major minor post an

GIHK 5-10 10-30 121-360 30-120 5-10

AHHK 5-10 10-30 121-360 30-120 5-10

NEURO 3-15 8-50 195-380 80-90 5-10

ORTH 1-10 0-15 10-85 1-20

PLAST 5-10 15-45 195-375 30-50 5-10

TVHK 1-10 20-50 120-400 45-55 5-30

Tevens krijgt de helft van de patiënten een hospitalisatiedatum mee. Alle patiënten ontvangen een

optimale dag en een deadline. De optimale dag zal plaatsvinden binnen de planningshorizon. De

hospitalisatiedatum en de deadline worden aangemaakt in verhouding tot deze optimale dag.

Indien een hospitalisatiedatum wordt toegekend, dan zal deze tussen 3 dagen en 10 dagen voor de

optimale operatiedag liggen. De deadline kan gaan van 1 tot en met 3 dagen na de optimale dag.

Deze twee getallen werden opzettelijk relatief klein gehouden teneinde de meeste deadlines

relevantie mee te geven. De planningshorizon beslaat immers slechts een werkweek en geen

enkele patiënt kan naar een volgende week verzet worden. Deadlines die in een andere

planningsweek vallen zouden daardoor geen sanctie kunnen veroorzaken.

3 Deze intervallen zijn ruw gebaseerd op de operatietijden vermeld in: Coulier C. en Alvarado-Vargas M. (2006) Modeling and Simulating of Operating Room Scheduling, blz. 69.

10

Een uitgebreidere beschrijving van de probleeminstanties wordt gegeven in sectie 4.3.

Samenvatting simulatiemodel

Onderstaande figuur toont alle gegevens uit “hospitaal.txt” zoals gepresenteerd in het

programma. Dit vormt tevens een samenvatting van de ingegeven virtuele hospitaalomgeving.

Figuur 7: samenvatting simulatieomgeving

11

2.2 Objectieven van de thesis

Onderzoeksvraag: hoe in een redelijke tijd, schema's opstellen met een goede haalbaarheid die

rekening houden met de restricties?

Deze schema’s bevinden zich op het niveau van de OK-planning. Dit werk kent alle patiënten een

bloknummer toe, maar bepaald geen volgorde waarin operaties binnen een blok zullen doorgaan.

Deze volgorde zal in een latere fase moeten worden bepaald. Het stipuleren van een optimaal

aantal operatiekamers, het opstellen van een blokschema en beslissingen in verband met andere

hospitaalbeperkingen gebeuren in een eerder stadium (zie sectie 3.1).

Het modelleren van een exact virtueel hospitaal met echte patiëntenlijsten is dus geen prioriteit.

Beperkingen zijn bovendien vaak hospitaalspecifiek. De focus ligt op de doeltreffendheid en

tijdsbenutting van de verschillende heuristische algoritmes.

“Een heuristiek is een techniek die goede (bijna optimaal) oplossingen zoekt aan een redelijke

computerverwerkingkost zonder haalbaarheid of optimalisatie te kunnen garanderen. In vele

gevallen kan zelfs niet gemeld worden hoe ver een bepaalde haalbare oplossing van het optimum

verwijderd is” (Reeves, 1993, blz. 6).

De planningen bestaan uit koppels patiëntennummers en bloknummers. Na alle heuristiek op de

initiële planningen te hebben toegepast is het de bedoeling planningen te verkrijgen die de

beschikbare middelen zo efficiënt mogelijk gebruiken. Niet alleen bloktijden moeten

gerespecteerd worden, maar ook alle menselijke middelen en materieel moeten zo weinig

mogelijk overbezetting of onderbezetting ondervinden.

12

3 Literatuur

3.1 Planning OK

Algemeen

OK’s zijn volgens schattingen goed voor een derde tot de helft van de totale kosten van een

hospitaal. Daardoor vertegenwoordigen ze het gebied met het meeste potentieel voor

kostenbesparing. Zelfs relatief kleine verbeteringen in efficiëntie vertalen zich in significante

besparingen en voordelen voor de maatschappij. Deze bestaan uit: salarissen voor chirurgen,

anesthesisten en verpleegsters en vaste kosten in verband met faciliteiten en materiaal. Effectieve

leveringen van chirurgische diensten vereisen een OK-manager, of een gelijkaardig lichaam, om

operaties efficiënt te programmeren, zodat een optimaal evenwicht bestaat tussen enerzijds een

hoge occupatie van het OK-personeel en andere middelen en anderzijds een lage OK-

werkloosheid en overwerkkosten (Denton en Gupta, 2002, blz. 1003).

De OK-planning bepaalt de chirurgische operaties die moeten uitgevoerd worden in het

chirurgisch centrum van een hospitaal, alsook de middelen die toegewezen worden aan de

operaties over de periode van een week of een dag (Jebali et al., 2006, blz. 52).

Sier, Tobin en McGurk halen enkele doelen van een OK-planning aan. Bij het plannen zullen

OK-managers prioriteiten moeten stellen en overeenkomstig handelen. Het proces wordt

ingewikkeld wanneer verschillende tegenstrijdige doelstellingen bestaan. Planningsystemen

worden ontwikkeld om (Sier et al., 1997, blz. 884):

• de OK effectief te gebruiken

• chirurgen, patiënten en personeel tevreden te stemmen

• eenvoud en gemak te bereiken bij het plannen

• de PACU doeltreffend te gebruiken

• een lage annuleringsgraad te bekomen

13

Planningsfasen

Het plannen van een operatiekwartier vereist het nemen van vele beslissingen die kunnen

onderverdeeld worden volgens de tijd die nodig is om ze te implementeren. Ze worden ingedeeld

in drie hoofdcategorieën: capaciteitsplanning, capaciteitsverdeling en operatiekamerplanning.

Capaciteitsplanning: capaciteitsplanning focust zich op de vraag en aanbod op lange termijn.

Deze planning beslist hoeveel capaciteit er nodig om aan de vraag te kunnen voldoen. Concreet

wil dit zeggen: beslissen hoeveel operatiekamers er nodig zullen zijn in het OK. Hiervoor dient

de vraag te worden voorspeld. Meestal wordt dit gedaan door de huidige vraag te extrapoleren

(Coulier en Alvarado-Vargas, 2006, blz. 6).

Capaciteitsverdeling: nadat de capaciteit is gepland over een lange termijn, ontstaat de nood om

deze capaciteit te verdelen over de verschillende disciplines en departementen. Dit wordt gedaan

over een gemiddelde termijn van een jaar en in twee fases. Eerst wordt beslist hoeveel capaciteit

elke discipline nodig heeft op jaarlijkse basis. In het UZ Gent, wordt er capaciteit toegewezen

voor het volgende jaar gebaseerd op het gebruik van de operatiekamers in het vorige jaar. Ten

tweede worden deze jaarlijkse capaciteiten vertaald naar wekelijks geplande bloktijden (Coulier

en Alvarado-Vargas, 2006, blz. 6).

In veel hospitalen wordt aan elke specialiteit een aantal operatiekamers toegewezen. Deze

operatiekamers worden dan gevuld met operaties op wekelijkse basis (Hans et al., 2006, blz. 3).

Operatiekamerplanning: wanneer de vorige planningen gebeurd zijn en alle disciplines

beschikken over hun wekelijkse vaste capaciteit, dient de gedetailleerde planning van de

patiënten per operatiekamer gevormd te worden (Coulier en Alvarado-Vargas, 2006, blz. 6).

Het plannen van patiënten gebeurd niet globaal. Elke discipline plant zijn eigen patiënten

gebaseerd op de bloktijden die werden toegewezen volgens de capaciteitsverdeling (Coulier en

Alvarado-Vargas, 2006, blz. 7).

14

Operatieproces gekaderd

Het traject start wanneer de patiënt wordt opgenomen in het hospitaal. Wanneer hij uiteindelijk

aankomt in de operatiekamer, begint men met het preoperatief proces. Nadien brengt de

anesthesist de patiënt in slaap. Wanneer deze processen zijn beëindigd, kan men met de

eigenlijke operatie beginnen. Na de operatie wordt de patiënt gewekt door de anesthesist en wordt

die naar de PACU, of in het geval van een major-operatie, naar intensieve zorg gebracht (Coulier

en Alvarado-Vargas, 2006, blz. 4).

Volgende figuur toont de stadia die een patiënt meemaakt. De stadia in het midden worden

behandeld in dit onderzoek.

Figuur 8: flowchart operatieproces (Coulier en Alvarado-Vargas, 2006, blz. 4)

Als het OK evenwichtig overeenstemt met secundaire middelen zoals de PACU, vormen deze

secundaire middelen geen knelpunt en kunnen ze genegeerd worden (Guinet en Chabaane, 2003,

blz. 71).

15

Beperkingen

Sier, Tobin en McGurk4 noteren tien beperkingen waar een operatiekamerplanning aan

onderhevig is:

• Het aantal operatiekamers: dit is afhankelijk van de grootte van het hospitaal, de

beschikbaarheid van assisterende verplegers en andere factoren.

• De beperkte hoeveelheid van gespecialiseerde apparatuur: veel operaties vereisen

gespecialiseerde apparatuur. Er moet op gelet worden dat geen twee operaties die gebruik

maken van hetzelfde zeldzame materiaal op een gelijk tijdstip gepland staan.

• Beschikbare bloktijden: bloktijden worden gebruikt om binnen een departement,

kamertijd toe te wijzen aan een serie van operaties. Deze worden meestal door dezelfde

chirurg uitgevoerd. Het tijdstip waarop personeel en materiële middelen aanwezig dienen

te zijn is gebaseerd op deze bloktijden (Denton en Gupta, 2003, blz. 1004).

• Planningspolitiek van het hospitaal: sommige hospitalen verkiezen langere operaties eerst

te plannen, terwijl andere ziekenhuizen de korste operaties voorop plaatsten.

• Geschatte operatieduur: stafleden zullen moeten inschatten hoelang elke chirurg nodig

heeft, zodat ze de chirurg niet overladen binnen de beschikbare bloktijd.

Een accurate voorspelling van de operatieduur voor elk operatietype is een vereiste voor

een effectief gebruik van het OK. De schatting van een operatieduur is echter geen

makkelijke taak, omdat deze afhankelijk is van het ziektebeeld van de patiënt en de

ervaring van de chirurg (Jebali et al., 2006, blz. 52-53).

• Regelmatig beschikbare tijd in het OK: de OK-tijd van disciplines wordt gewoonlijk

gepland op wekelijkse of maandelijkse basis. Soms kan een discipline echter niet genoeg

patiënten voorleggen om een sessie te verantwoorden. Deze discipline zal toch de

aanwezige operaties moeten kunnen uitvoeren. Er moet dus op gelet worden dat er

regelmatig een tijdsperiode voorkomt waarin deze operaties kunnen doorgaan.

• Beschikbaarheid van de anesthesist: het is nodig dat de anesthesist beschikbaar is tijdens

de operatie om de patiënt te controleren.

4 SIER D., TOBIN P. en MCGURK C. (1997). Scheduling Surgical Procedures, The Journal of the

Operational Research Society, vol. 48, nr. 9, blz. 884-891.

16

• Kamerbeperkingen qua apparatuur: soms is apparatuur te gevoelig om van kamer naar

kamer verplaatst te worden. In andere gevallen kan ze gewoon deel uitmaken van de

kamer waardoor operaties die deze apparatuur vereisen in die bepaalde operatiekamer

gepland zullen moeten worden.

• Toegewezen starttijd aan chirurgen: aan chirurgen wordt gewoonlijk een starttijd

toegewezen. Meestal is dit wanneer het OK opent of hervat in de namiddag.

• Kamergebruik over een langere periode: disciplines moeten hun toegewezen tijd ten volle

benutten. Wanneer een discipline consistent tijd over heeft moet die reservetijd naar een

discipline gaan zonder marge.

Verder kan men rekening houden met een gemiddeld aantal spoedoperaties in een planning. De

meeste grote hospitalen hebben echter een specifieke spoedoperatiekamer en hebben hier dus

geen nood aan. In kleinere hospitalen kan het echter een factor zijn (Sier et al., 1997, blz. 885).

Concrete beperkingen worden gegeven door Guinet en Chaabane (Guinet en Chabaane, 2003):

• Iedere patiënt krijgt minimum en maximum 1 sessie5.

• De blok- en OK-tijden worden gerespecteerd5.

• Het overwerk van elke dag overschrijdt de overwerk capaciteit niet5.

• Een chirurg kan maar een beperkt aantal uur per dag opereren.

• Een patiënteninterventie kan niet gebeuren voor of op zijn hospitalisatiedatum.

• Een patiënteninterventie kan niet gebeuren na zijn interventiedeadline5.

• Een patiënteninterventie kan niet gebeuren wanneer zijn chirurg niet vrij is.

• De duur van een blok waar de patiënt aan wordt toegewezen, kan niet korter zijn dan de

duur van de interventie.

• Elke OK wordt gedurende elk blok voor maximum 1 operatie tegelijkertijd gebruikt.

• Een patiënteninterventie kan niet gebeuren wanneer de benodigde apparatuur niet

aanwezig is in een bepaalde operatiekamer.

5 Tevens vermeld in: JEBALI AÏDA, HADJ ALOUANE ATIDEL B. en LADET PIERRE (2006). Operating rooms scheduling, Int. J. Production Economics, vol. 99, blz. 52-62.

17

De twee laatste beperkingen en enkele factoren van Sier, Tobin en McGurk vallen buiten het

bereik van dit werk. Deze kunnen worden gebruikt om een planning waarvan de volgorde van de

operaties bekend is te beoordelen.

Een beoordeling van huidige methoden onthult dat eender welke geautomatiseerde procedure

dient aangepast te worden, aangezien de beperkingen die het probleem beschrijven vaak

hospitaalspecifiek zijn (Sier et al., 1997, blz. 884).

3.2 Algoritmes

3.2.1 Genetische algoritmes

De naam “genetisch algoritme” vindt zijn oorsprong in de gelijkenis van de presentatie van een

complexe structuur door middel van een componentenvector en het concept van de genetische

structuur van een chromosoom. Bij selectief kweken van planten of dieren wordt immers gezocht

naar nageslacht met zekere wenselijke kenmerken. Kenmerken die bepaald worden op genetisch

niveau door de manier waarop de chromosomen van de ouders zich combineren. Op een analoge

manier zoeken we intuïtief naar oplossingen voor complexe problemen door delen van bestaande

oplossingen te combineren (Reeves, 1993, blz. 152-153).

Werkwijze

Genetische algoritmes baseren zich op het selecteren van subgroepen, meestal paren, van

oplossingen uit een populatie, ouders genoemd. Deze ouders worden gecombineerd en er worden

nieuwe oplossingen of kinderen gevormd. De combinatieregels om kinderen voort te brengen zijn

gebaseerd op de genetische notie van kruising, die bestaat uit het inwisselen van

oplossingenwaarden van welbepaalde variabelen. Kinderen die een overlevingstest in het

voordeel van degene met superieure kwaliteit doorstaan, worden beschikbaar als ouder van de

volgende generatie. De ouders worden bij elke generatie op willekeurige basis, of op lichtelijk

beïnvloede willekeurige basis, uit een populatie gekozen (Reeves, 1993, blz. 138).

Om de optimale oplossing te vinden voor grote combinatorische problemen, onderhoudt een

genetisch algoritme een populatie van chromosomen wiens fitheidswaarde berekend zijn. Elk

chromosoom is een oplossing voor het probleem en zijn fitheidswaarde is gerelateerd aan de

18

waarde van de doelfunctie van die oplossing. De keuze van de ouders kan tevens gebeuren op

basis van die fitheidswaarde. Ze kan echter ook willekeurig plaatsvinden of een combinatie zijn

van de twee. Wanneer de ouders kinderen hebben voortgebracht kan op verscheidene manieren

vervanging plaatsvinden. Beide ouders kunnen vervangen worden of slechts één ouder kan

worden afgelost. Men kan ook de hele populatie vervangen na iedere keer een bepaald aantal

proeven te hebben doorlopen (Reeves, 1993, blz. 153).

Vanuit het perspectief van operationeel onderzoek kan een genetisch algoritme gezien worden als

de intelligente exploitatie van een willekeurige zoektocht (Reeves, 1993, blz. 152).

Een visuele voorstelling van de methoden gebruikt in dit werk, kan bekeken worden in sectie 4.5.

Populatie

De meeste van de eerste pogingen om combinatorische problemen op te lossen maakten gebruik

van lineaire programmering. Algemeen door het introduceren van een populatie met integere

variabelen die de waarden 0 of 1 konden aannemen (Reeves, 1993, blz. 3). Hoewel

geargumenteerd werd dat een binaire presentatie significante voordelen oplevert werd dit recenter

toch in twijfel getrokken (Reeves, 1993, blz. 153).

Hoe wordt de prestatie van een genetisch algoritme beïnvloedt door populatiegrootte? In principe

is het duidelijk dat kleine populaties het risico lopen om slechts een fractie van de

oplossingenruimte te behandelen, terwijl grote populaties erg lange berekeningstijden opleveren

(Reeves, 1993, blz. 165). Empirische resultaten van veel auteurs stellen dat populaties met 30

elementen in vele gevallen adequaat bleken (Reeves, 1993, blz. 165).

De initiële populatie bestaat veelal uit willekeurig gegenereerde binaire getallenreeksen. Een

populatie bestaande uit oplossingen van een hogere kwaliteit, tot stand gebracht door een andere

heuristische techniek, kan het genetisch algoritme helpen om vlugger, betere oplossingen te

vinden. Een mogelijk nadeel is wel dat de kans op voortijdige convergentie vergroot (Reeves,

1993, blz. 166).

19

Operatoren

Er zijn verschillende genetische operatoren voorhanden om chromosomen te manipuleren. De

meest voorkomende zijn (Reeves, 1993, blz. 153):

• oversteekpunten: een inwisseling van secties van de ouderchromosomen

• mutatie: een willekeurige wijziging van het chromosoom.

Voor veel applicaties bleek het gebruik van enkel oversteekpunten heel effectief. Mutatie helpt

slechts om een redelijk niveau van populatiediversiteit te onderhouden. Overdreven

mutatiegebruik kan trouwens de oversteekpunten verhinderen degelijk werk te leveren (Reeves,

1993, blz. 170).

Het gebruik van adaptieve operatoren, waarbij het gebruik van bepaalde operatoren afhankelijk

wordt van de populatie, kan hier een oplossing bieden. Zo kan men de mutatiefrequentie

bijvoorbeeld laten toenemen wanneer de populatiediversiteit afneemt (Reeves, 1993, blz. 174).

Genetische algoritmes gaan hoofdzakelijk met structuur om door hun resultaten over te dragen

naar een andere methode. Een lokale zoekmethode neemt het op zich om verder nieuwe

oplossingen te genereren (Reeves, 1993, blz. 140).

3.2.2 Lokale zoekmethoden

Om de effectiviteit van een genetisch algoritme te verhogen kunnen we een andere heuristiek

introduceren. Een voordeel van een genetisch algoritme is zijn domeinonafhankelijkheid. Dit wil

zeggen dat je het kan loslaten op een gecodeerd probleem zonder dat het veel van het probleem

dient te weten. Wanneer wordt overgegaan op lokale zoekmethoden zal meer probleemspecifieke

kennis moeten geïntroduceerd worden (Reeves, 1993, blz. 179).

Lokale zoekmethoden hebben als doelstelling het vinden van een oplossing uit een grote

oplossingenset en dit op een vlugge en intelligente manier. Ze concentreren zich op problemen

die ondubbelzinnig kunnen worden geformuleerd door middel van wiskundige terminologie en -

notatie. Verder wordt verondersteld dat de kwaliteit van een oplossing kwantificeerbaar is en dat

deze kan vergeleken worden met de kwaliteit van een andere oplossing. Tenslotte wordt

verondersteld dat de oplossingenset eindig is (de Kreuk et al., 2005).

20

Een naïeve aanpak om combinatorisch problemen op te lossen is simpelweg alle haalbare

oplossingen opsommen, hun doelfuncties evalueren en de beste oplossing eruit pikken. Hoewel

dit in theorie mogelijk is, levert het in de praktijk grote moeilijkheden op door het enorme aantal

mogelijke oplossingen bij elk probleem met een redelijke omvang (Reeves, 1993, blz. 7).

Lokale zoekmethoden verhelpen dit probleem door slechts een kleine subgroep van de

oplossingenruimte te onderzoeken. Dit wordt bereikt door een omgeving te definiëren en in de

omgeving van de huidige oplossing te zoeken naar verbetering. Als er geen aanpalende oplossing

is die een verbetering van de doelfunctie oplevert, wordt de huidige oplossing weerhouden als

een benadering van het optimum. Vindt er wel verbetering plaats, dan wordt het proces herhaald.

De methode van de steilste afdaling onderzoekt de hele omgeving en selecteert die oplossing die

resulteert in de grootste verbetering van de doelfunctie. Willekeurige afdaling selecteert de

aanpalende oplossingen die het onderzoekt op verbetering op willekeurige basis en accepteert de

eerste oplossing die daadwerkelijk een verbetering oplevert (Reeves, 1993, blz. 21).

Bij lokale zoekmethoden wordt dus een subgroep van haalbare oplossingen onderzocht door

herhaaldelijk van een huidige naar een aanpalende oplossing te gaan. Bij een minimalisatie-

probleem, passen de traditionele lokale zoekmethoden een afdalingsstrategie toe. De zoektocht

beweegt zich altijd in de richting van verbetering. Deze strategie resulteert echter vaak in een

lokaal optimum, in de plaats van een globaal optimum. Om dit tegen te gaan kan hetzelfde

algoritme toegepast worden op meerdere initiële oplossingen of kan de complexiteit van de

omgevingen verhoogd worden om zo het bereik van de zoektocht te vergroten. Helaas bleken

deze varianten niet helemaal bevredigend. De oplossingen die worden bekomen met deze

afdalingsstrategieën zijn volledig afhankelijk van de initiële oplossingen (Reeves, 1993, blz. 21).

21

Figuur 9: lokaal maximum en –minimum (www.doc.ic.ac.uk/~sgc/teaching/v231/lecture3.html, 2005)

Recent werden echter verscheidene algemene heuristische methoden voorgesteld die dit schema

op diverse manieren uitbreiden en zo trachten te vermijden in een zwak lokaal optimum

gevangen te raken (Mladenović en Hansen, 1997, blz. 1097).

3.2.3 VNS

Er bestaan veel behandelingen voor combinatorische optimalisering. Deze concentreren zich

echter eerder op exacte - dan op heuristische methodes. Dat wil zeggen dat ze voornamelijk

technieken omvatten die gegarandeerd de optimale oplossing vinden voor een probleem (Reeves,

1993, blz. 5).

“Een heuristiek is een techniek die goede (i.e. bijna optimaal) oplossingen zoekt aan een redelijke

computerverwerkingkost zonder haalbaarheid of optimalisatie te kunnen garanderen. In vele

gevallen kan zelfs niet gemeld worden hoe ver een bepaalde haalbare oplossing van het optimum

is” (Reeves, 1993, blz. 6).

Wat we optimaliseren is echter een model van een realistisch probleem. Er is geen garantie dat de

beste oplossing van het model tevens de beste oplossing voor het onderliggende realistisch

probleem vormt. Anders gezegd, moeten we een exacte oplossing van een benaderend model

prefereren of een benaderende oplossing van een exact model (Reeves, 1993, blz. 11)?

22

Het systematisch veranderen van omgeving binnen een mogelijk gerandomiseerde lokaal

zoekalgoritme vormt een simpele en effectieve metaheuristiek voor combinatorische – en globale

optimalisatie. We noemen dit variabele omgevingszoektocht (VNS of variable neighborhood

search) (Hansen en Mladenović, 2001, blz. 449).

Een kenmerk van vele combinatorische problemen is dat ze naast de eventuele echte of globale

optima veel meer lokale optima bezitten. Dit kan geïllustreerd worden door het concept

“omgeving” in te voeren. Ruw gesteld is een omgeving N(x,σ) van een oplossing x een

oplossingenset die kan bereikt worden door vanuit een x een simpele operatie uit te voeren. Een

operatie σ kan het verwijderen of toevoegen van een object aan een oplossing inhouden. Het

wisselen van twee objecten is een ander voorbeeld van een operatie die veel voorkomt bij

rangschikkingproblemen. Indien een oplossing y beter is dan elke andere oplossing in zijn

omgeving N(y, σ), dan is y een lokaal optimum met betrekking tot zijn omgeving (Reeves, 1993,

blz. 4-5).

In tegenstelling tot de meeste andere lokale zoekmethoden volgt een VNS geen traject, maar

onderzoekt het steeds verder liggende omgevingen. De gewenste kenmerken van de huidige

oplossing, dit zijn de variabelen die reeds een optimale waarde bezitten, worden behouden en

gebruikt om veelbelovende buuroplossingen te bekomen. Een lokale zoekmethode wordt

herhaaldelijk toegepast om van deze nabijgelegen oplossingen naar lokale optima te raken

(Hansen en Mladenović, 2001, blz. 450; Mladenović en Hansen, 1997, blz. 1097).

De verschillende stadia bij een basis VNS zijn (Hansen en Mladenović, 2001, blz. 451):

Initialisatie: selecteer een groep omgevingsstructuren Nk, k = 1, …, kmax, die gebruikt zullen

worden in de zoektocht; vind een initiële oplossing x; kies een eindconditie;

Herhaal de volgende stappen tot de eindconditie is voldaan:

1) Stel k <- 1; (2) tot k = kmax, herhaalde volgende stappen:

a) Schudden. Genereer willekeurig een punt x’ uit de ke omgeving van x (x’ Є Nk(x));

b) Lokale zoektocht. Pas een lokale zoekmethode toe met x’ als initiële oplossing; duid het

verkregen lokaal optimum aan met x”.

c) Verdergaan of niet. Als dit lokaal optimum beter is dan het vorige, verschuif er naartoe

(x <- x”), en zet de zoektocht verder met N1 (k <- 1); anders, stel k <- k + 1;

23

Bij het opstellen van een VNS dient men volgende vragen te beantwoorden (Hansen en

Mladenović, 2001, blz. 451):

• Hoeveel en welke Nk moeten gebruikt worden?

• Wat is hun volgorde in de zoektocht?

• Welke strategie moet gebruikt bij het veranderen van omgeving?

Als eindvoorwaarde van de zoektocht kan gebruik gemaakt worden van: een maximaal

toegestane rekentijd, een maximaal aantal iteraties of een maximaal aantal iteraties tussen twee

verbeteringen (Hansen en Mladenović, 2001, blz. 450).

4 Programma

4.1 Hardware en software

De prestaties van 2 computers werden vergeleken.

Laptop:

• Systeem: 32-bit Windows Vista Home Premium

• Processor: AMD Turion™ 64 x 2 Mobile Technoloy TL-52, 1,60 GHz

• RAM: 2 Gigabyte

Desktop:

• Systeem: Microsoft Windows XP Professional Service Pack 2

• Processor: Celeron® CPU 3.06 GHz

• RAM: 512 MB

Beide maakten initiële planningen op met het aantal iteraties ingesteld op 5000. De laptop had

hier 42 minuten voor nodig, terwijl de desktop 19 minuten nodig had. Alle besproken resultaten

kwamen dan ook tot stand op de desktop.

24

De programma’s werden geschreven met de gratis Dev-C++ 4.9.9.2 programmeeromgeving van

Bloodshed Software gebruik makende van de programmeertaal C.

4.2 Samenvatting

In deze sectie wordt de structuur van het programma verduidelijkt. Verder komt ook de gebruikte

mappenstructuur, waarin onder meer de tussentijdse planningen worden ondergebracht, en de

inhoud van de .txt bestanden aan bod. Een uitgebreidere uitleg bij de generatie van

probleeminstanties, de genetische algoritmes, de lokale zoekmethoden en de VNS wordt gegeven

in de latere titels van hoofdstuk 4.

Mappenstructuur

Onderstaande figuur toont de mappenstructuur met bijhorende .exe- en .txt bestanden.

Figuur 10: mappenstructuur

25

“hospitaal.txt” bevat de waarden die toegekend werden aan de variabelen in verband met de

simulatieomgeving. Daarnaast omvat het ook de sanctiewaarden, het gewenste aantal iteraties,

het maximaal aantal iteraties zonder verbetering en het aantal planningen per populatie. Alle

waarden in dit tekstbestand kunnen makkelijk gewijzigd worden.

In “lijst.txt” worden de tijdsduur, het aantal iteraties, de sanctieafname en de beginsanctie

opgetekend. Dit gebeurt na elke willekeurige- of heuristische generatie van initiële schema’s, na

elk genetisch algoritme, na elke aparte lokale zoekmethode en na elke VNS.

De eigenlijke beschrijving van het programma volgt de volgorde van de .exe bestanden zoals

getoond in bovenstaande figuur. Bij de uitvoering van elk .exe bestand kunnen de variabelen in

verband met de simulatieomgeving bekeken worden, alsook een patiëntenlijst naar keuze. Verder

wordt bij elk heuristisch proces de voortgang en het gemiddeld aantal behandelde populatie-

elementen getoond. Tenslotte wordt na de volledige uitvoering van een .exe bestand het totaal

aantal gebruikte minuten getoond.

Probleeminstanties genereren.exe

Generatie probleeminstanties

Alvorens planningen kunnen worden opgesteld, moeten eerst probleeminstanties aangemaakt

worden. Het programma stelt automatisch 200 patiëntenlijsten op aan de hand van één ingevoerd

getal, dat als richtlijn zal dienen voor het aantal patiënten per lijst. Elke probleeminstantie komt

in een patienten(probleeminstantienummer).txt te staan. Deze komen terecht de map

“probleeminstanties”. Voor een gedetailleerde uitleg van een aparte patiëntenlijst, zie sectie 4.3.

Initiele planningen genereren.exe

Met de aangemaakte probleeminstanties kunnen vervolgens de eerste planningen worden

gemaakt. Iedere patiënt wordt in elke planning opgenomen. Een operatie naar een volgende

planningsweek verschuiven is geen optie.

26

Willekeurige generatie initiële schema’s

Bij elke probleeminstantie worden aan de patiënten willekeurig bestaande bloknummers

toegewezen. Hier wordt geen populatie gevormd. Het resultaat is één schema per

probleeminstantie. Het aantal iteraties uit “hospitaal.txt” geeft hier aan hoeveel maal voor elke

probleeminstantie een schema opgesteld moet worden. De planning met de laagste totale sanctie

komt voor elke probleeminstantie terecht in “initiele_planning(probleeminstantienummer).txt” in

de map “initiele_planningen”.

Het gemiddelde van elk eerst aangemaakt schema vormt de beginsanctie in “lijst.txt”. Het

gemiddelde van de 200 verschillen tussen de beginsanctie en de sanctie van het uiteindelijk

schema vormt de sanctieafname.

Generatie initiële schema’s met behulp van heurstiek

Opnieuw worden willekeurig bloknummers aan patiënten toegekend. Toch worden de kandidaat-

blokken beperkt door enkele criteria. Eerst wordt nagegaan of er nog niet volzette blokken met

dezelfde discipline als de patiënt beschikbaar zijn. Indien alle blokken met de juiste discipline

volzet zijn, wordt willekeurig een blok met de juiste discipline toegekend. In het andere geval

worden blokken geëvalueerd tot een blok met de juiste discipline wordt gevonden dat nog niet

volzet is. Een patiënt mag geen blok toegewezen krijgen met de verkeerde discipline. Doorheen

de rest van het programma zal dit worden volgehouden. Er is dan ook geen lokale zoekmethode

op discipline gericht. Een hoge discipline-sanctiewaarde bij sanctie 14 zorgt hopelijk wel voor de

correctie van eventueel misplaatste patiënten.

Op deze manier wordt een populatie gevuld met een in “hospitaal.txt” gegeven aantal elementen.

Nadien wordt, telkenmale een volledig schema wordt bekomen, in de voorlopige populatie naar

het schema gezocht met de hoogste totale sanctie. Indien het recent aangemaakte schema beter

blijkt te zijn, vervangt dit recente schema het schema met de laagste sanctie. Het aantal iteraties

werd afgelezen uit “hospitaal.txt”. De 200 aangemaakte populaties worden opgetekend in de

“initiele_planning(probleeminstantienummer).txt” in de map “initiele_planingen” en zullen

worden opgevraagd door de hierna beschreven .exe bestanden.

Het gemiddelde van elk eerst aangemaakt schema vormt de beginsanctie in “lijst.txt”. Het

gemiddelde van de 200 verschillen tussen de beginsancties en de sancties van het beste schema’s

in de uiteindelijke populaties vormt de sanctieafname.

27

Zoek0.exe

Lokale zoekmethoden

Eer de lokale zoekmethoden worden toegepast, wordt elke populatie herschikt. De elementen met

de laagste sanctie komen vooraan en zullen dus eerst aan bod komen. Vervolgens wordt elke

methode een aantal iteraties toegepast op de 200 populaties. Het aantal iteraties wordt

opgevraagd uit “hospitaal.txt”. Elke methode begint met dezelfde populaties. Bij “zoek0.exe” zijn

dat de initiële populaties voortgebracht door “initiele planningen genereren.exe”. Uiteindelijk telt

de map “zoek0”, 200 bestanden “instantie_lokaal(probleeminstantienummer).txt” met voor elke

methode het best bekomen schema. “lijst.txt” wordt na elke methode aangevuld.

VNS

Hier werd een volgorde bepaald waarin de lokale zoekmethoden moeten worden uitgevoerd.

Opnieuw wordt het gegeven aantal iteraties uitgevoerd. Nu zal echter niet elke methode alleen al

de iteraties uitvoeren, maar zullen ze samen dit aantal bereiken. De eerste lokale zoekmethode

begint opnieuw met de populaties voortgebracht door “initiele planningen genereren.exe”.

Wanneer een volgende lokale zoekmethode in werking treedt, zal deze verder werken met de

gewijzigde populaties. Wanneer de laatste zoekmethode geen verbetering in de vorm van lagere

sancties teweegbrengt, neemt de eerste lokale zoekmethode opnieuw over. Uiteindelijk komt het

beste schema van elke probleeminstantie terecht bij de schema’s aangemaakt door de lokale

zoekmethoden in het bestand: “instantie_lokaal(probleeminstantienummer).txt” in de map

“zoek0” en wordt “lijst.txt” aangevuld.

Zoek1.exe

Genetisch algoritme 0

Het genetisch algoritme werkt met de populaties voortgebracht door “initiele planningen

genereren.exe”. Elke populatie ondergaat het ingegeven aantal interaties. Na afloop worden de

gewijzigde populaties bijgehouden in de verscheidene “kind(probleeminstantienummer).txt”

bestanden in de map “kinderen1”. Uiteindelijk wordt ook “lijst.txt” aangevuld.

Lokale zoekmethoden

Hier geldt bijna dezelfde werkwijze als bij “zoek0.exe”. Alleen wordt hier gestart met de

genetisch gewijzigde populaties. De beste schema’s van elke methode komen hier terecht in de

map “zoek1”.

28

VNS

Ook hier geldt bijna dezelfde werkwijze als bij “zoek0.exe”. Er wordt eveneens gestart met de

genetisch gewijzigde populaties en de beste schema’s komen tevens terecht in map “zoek1”.

Zoek2.exe

Het volledige proces verloopt analoog aan “zoek1.exe”. De populaties en schema’s komen

terecht in de mappen: “kinderen2” en “zoek2”.

Eindplanningen bekijken.exe

Dit bestand zal geen enkele planning meer wijzigen of opstellen. Wel kunnen uiteindelijk de

geproduceerde letterreeksen in de tekstbestanden bekeken worden in een bevattelijkere vorm.

Hospitaalgegevens bekijken

Het is mogelijk enkele data uit “hospitaal.txt” te bekijken. Dit zijn alle variabelen die vlug

kunnen aangepast worden. Een schermafbeelding met deze gegevens uit “eindplanningen

bekijken.exe” staat bij sectie 2.1. Verdere uitleg over de opzet van het simulatiemodel en de

gebruikte waarden wordt gegeven in secties 2.1 en 4.8.

Probleeminstantie bekijken

Indien een patiëntenlijst moet getoond worden, dient het nummer van de probleeminstantie

gegeven te worden. De figuur met een vervolgens getoonde lijst staat in sectie 4.3.

Planning en bijhorende sancties

Tenslotte kan voor een probleeminstantie naar keuze elke eindplanning met sancties, inclusief

sanctiewaarden, bekeken worden. Daartoe worden drie gegevens gevraagd. Eerst dient opnieuw

een probleeminstantienummer ingegeven te worden. Vervolgens moet gekozen worden voor een

planning tot stand gebracht ofwel zonder genetisch algoritme, met genetisch algoritme 0 of met

genetisch algoritme 1. Uiteindelijk heeft men de optie een planning en bijhorende sancties te

bekijken tot stand gebracht door één van de acht lokale zoekmethoden of door de VNS. De

volgende twee figuren illustreren deels de getoonde planning en sancties.

29

Figuur 11: vraagstelling en sancties

Figuur 12: presentatie van een planning

Daarna is het mogelijk opnieuw een probleeminstantie, planning en sancties te bekijken.

4.3 Generatie probleeminstanties

De gebruiker voert het gewenste aantal patiënten in en het programma maakt 200

probleeminstanties aan. Om extra variabiliteit te bekomen, zal niet elke probleeminstantie een

gelijk aantal patiënten tellen. Bij iedere lijst wordt een willekeurig patiëntenaantal gegenereerd

liggende in het interval: [ingevoerd getal – 30; ingevoerd getal + 30].

30

Onderstaande schermafbeeling van “eindplanningen bekijken.exe” toont een probleeminstantie

met alle aangemaakte gegevens. Deze worden in overeenstemming met de beperkingen uit de

probleemdefiniëring gegenereerd. Een patiënt die plastische chirurgie nodig heeft zal

bijvoorbeeld nooit een knietafel vereisen. De bespreking zal de volgorde van de kenmerken in de

figuur volgen.

Figuur 13: probleeminstantie

Nummer

Elke patiënt ontvangt een opeenvolgend nummer, beginnend bij nul. Dit patiëntennummer zal

doorheen het programma als identificatie dienen.

Discipline

Met betrekking tot de discipline werden geen kwantiteitsregels opgesteld. Iedere patiënt ontvangt

willekeurig één van de zes disciplines. De toegewezen discipline vormt wel een ankerpunt voor

enkele verdere gegevens.

Major / Minor

Ook dit kenmerk wordt willekeurig toegewezen. Alleen orthopedische patiënten zullen enkel

minor-operaties ondergaan. Of een major- dan wel minor-operatie wordt toegekend zal een

invloed uitoefenen op de verschillende tijden van het operatieproces, de opsteltijd en het aantal

vereiste verplegers.

31

Tafel

Welke tafel de patiënt nodig heeft is afhankelijk van zijn discipline. Elke discipline heeft een

aantal mogelijke tafels waaruit vervolgens willekeurig wordt gekozen. De verhoudingen tussen

disciplines en tafels worden uitgelegd in sectie 2.1.

Tijden

Deze omvatten de nodige minuten voor het pre-operatief proces, de pre-anesthesie, de eigenlijke

operatie en de post-anesthesie. De totale tijd is de som van al deze tijden. Daarbij heeft elke

patiënt nog een kuis- en opsteltijd. Deze wordt echter volledig bepaald door het major/minor

kenmerk. Een expliciete weergave leek dan ook niet nodig. De vier tijden, hier toegekend,

hebben allen wel een interval met een minimum- en een maximum aantal minuten waarbinnen

willekeurig wordt gekozen. De intervallen bij de tijden van het pre-operatief proces, de pre-

anesthesie en de post-anesthesie worden enkel bepaald door de discipline. Enkel bij de eigenlijke

operatietijd speelt naast de discipline ook de major/minor indeling een rol bij de definiëring van

het interval. Voor een expliciete weergave van de tijdsintervallen, zie sectie 2.1.

Hospitalisatiedatum

De operaties moeten gerealiseerd worden binnen een tijdsinterval, gedefinieerd door de

hospitalisatiedatum en de operatiedeadline van de patiënt (Guinet en Chabaane, (2003), blz. 71).

Elke patiënt heeft 50% kans om een hospitalisatiedatum toegewezen te krijgen. Niet elke patiënt

hoeft immers al een datum te bezitten bij het opmaken van de planningen. Bij elke patiënt zonder

datum wordt verondersteld dat deze zal worden bepaald door zijn plaats in het uiteindelijke

schema. Elke patiënt wordt immers gewoonlijk gehospitaliseerd één dag voor de dag waarop de

operatie plaatsvindt (Guinet en Chaabane, 2003, blz. 41; Jebali et al., 2006, blz. 54). Voor de

andere patiënten wordt de datum willekeurig gekozen uit een interval met een minimum- en een

maximum aantal dagen. Ze wordt voorgesteld in verhouding tot de optimale operatiedag.

Optimale dag

De optimale operatiedag is een willekeurig gekozen getal van 1 tot 5, dat een werkdag voorstelt.

Dat wil zeggen dat elke optimale dag binnen het tijdsbestek van de planning zal vallen.

32

Deadline

Iedere patiënt heeft een dag waarvoor hij absoluut moet zijn geopereerd. Deze deadline is een

willekeurig gekozen getal dat in verhouding tot de optimale dag staat.

Leeftijd en Naam

Deze persoonsgegevens worden tevens willekeurig gevormd. Er werd enkel bepaald dat iedere

patiënt meerderjarig dient te zijn. Op het verdere verloop van het programma hebben deze twee

data echter geen enkele invloed.

4.4 Beschrijving doelfunctie

Om de planningen te evalueren wordt gebruik gemaakt van sancties. Deze zullen hoger worden

telkens een overtreding wordt begaan. Telkens de sancties worden berekend, gebeurt dit voor één

relevante planning, uiteraard over het ganse tijdsbestek van vijf dagen. Oplossingen met een lage

sanctie zullen dus van een hogere kwaliteit zijn dan oplossingen met een hogere sanctie.

De beschrijvingen van de sancties werden algemeen gehouden. Concrete informatie zoals de

gebruikte sanctiewaarden komt aan bod in sectie 4.8.

Sanctie 1: iedere patiënt krijgt minimum en maximum 1 sessie.

Bij elke patiënt wordt het bloknummer gecontroleerd. Dit bloknummer moet uiteraard

overeenkomen met een blok in het simulatiemodel. Telkens het programma bij een patiënt een

bloknummer tegenkomt dat kleiner is dan nul, of groter is dan het hoogst mogelijke bloknummer

wordt sanctie 1 verhoogd met de sanctiewaarde. Deze sanctie leek vanzelfsprekend en essentieel.

Ze werd echter voornamelijk voor de volledigheid toegevoegd. De bloknummers worden

namelijk niet binair gevormd waardoor het makkelijk wordt enkel de juiste bloknummers toe te

wijzen. Bovendien heeft elke patiënt, binnen elke planning, slechts één numerieke variabele die

het bloknummer voorstelt.

33

Sanctie 2: elke operatiekamer kan enkel gebruikt worden tijdens de toegestane openingstijden.

Bij ieder blok wordt de bezette tijd bij het aanvangstijdstip geteld. Op deze manier wordt

automatisch rekening gehouden met de kuis- en opsteltijd die tussen de operaties gepland staat6.

Vervolgens wordt het sluitingsuur van de operatiekamer, waarin het blok valt, van deze som

afgetrokken. Indien we een positief getal overhouden wordt dit sluitingsuur dus overschreden.

Om rekening te houden met de verdeling van het overwerk, wordt dit positief getal eerst

gekwadrateerd en vervolgens gedeeld door honderd. Pas dan wordt sanctie 2 verhoogd met het

product van het resultaat van deze bewerking en de sanctiewaarde. Op deze wijze zullen enkele

operatiekamers met relatief veel overwerk zwaarder bestraft worden dan meerdere

operatiekamers met minder overwerk. Om sanctie 2 niet allesoverheersend te maken werd

tenslotte het gekwadrateerd getal door honderd gedeeld. De relatieve belangrijkheid kan dan nog

altijd verhoogd worden door middel van de sanctiewaarde. Aangezien we veronderstellen dat een

blok nooit zal aanvangen eer de juiste operatiekamer geopend is, wordt enkel het sluitingsuur

gecontroleerd. Hierop moet dus gelet worden bij de invulling van “hospitaal.txt”.

Het is de bedoeling dat deze sanctie zich hoofdzakelijk op overbezette blokken richt waarvan het

einde dicht tegen het sluitingsuur van de operatiekamer aanleunt.

Sanctie 3: bij elke operatiekamer mag het overwerk het absolute sluitingsuur niet overschrijden.

De gebruiker van het programma wordt de mogelijkheid gegeven om een aanvaardbaar aantal

minuten overwerk aan elke operatiekamer toe te kennen. Indien een planning langer overwerk

dan het ingegeven aantal inhoudt, treedt deze extra sanctie in werking. Sanctie 3 kan dus gezien

worden als een eenvoudige, robuuste variant van sanctie 2. Indien men hiervan geen gebruik

wenst te maken, dienen de aanvaardbare overuren bij elke operatiekamer simpelweg hoog genoeg

gedefinieerd te worden. Analoog aan sanctie 2 wordt voor elk blok de bezette tijd bij het

aanvangsuur geteld. Dit wordt dit maal verminderd met de som van het sluitingsuur en het

ingegeven aanvaardbaar aantal minuten overwerk van de relevante operatiekamer. Indien een

positief getal overblijft, wordt sanctie 3 verhoogd met het product van dit getal en de

sanctiewaarde.

6 Deze tijden worden bij de bezette tijd geteld, bij iedere sanctieberekening.

34

Sanctie 4: een blok mag niet overbezet worden.

Opnieuw wordt elk blok overlopen. Telkens wordt de beschikbare tijd van de bezette tijd

afgetrokken. Een positief verschil stelt dan de overbezetting van dat blok in minuten voor. Iedere

minuut zorgt voor een verhoging van sanctie 4 met de sanctiewaarde.

Sanctie 5: een patiënt mag niet aan een blok toegewezen worden, indien de patiënt meer tijd

vereist dan de duur van dat blok.

Bij iedere patiënt wordt eerst naar het bloknummer gekeken. Per patiënt waarbij de totale vereiste

tijd langer is dan de totale beschikbare tijd van het blok, wordt sanctie 5 verhoogd met de

sanctiewaarde.

Sanctie 6: een patiënteninterventie mag enkel gebeuren na de hospitalisatiedatum.

Alle patiënten worden overlopen. Enkel bij degene waarbij een hospitalisatiedatum bekend is,

wordt het bloknummer bekeken. Indien dat blok voor de dag van de hospitalisatie valt, wordt

sanctie 6 verhoogd met de sanctiewaarde. Ook indien dat blok op de dag van de hospitalisatie ligt

wordt sanctie 6 verhoogd met de sanctiewaarde. Er wordt immers verondersteld dat de

hospitalisatiedatum nog niet bekend is voor patiënten die bijvoorbeeld slechts een kleine ingreep

vereisen en deze zullen ondergaan op hun hospitalisatiedatum. Deze patiënten zonder

hospitalisatiedatum worden dan ook niet gesanctioneerd.

Sanctie 7: een patiënteninterventie kan niet plaatsvinden na de interventiedeadline.

Het programma bekijkt bij iedere patiënt het blok en de dag waarop deze valt. Indien deze dag na

de deadline valt, wordt sanctie 7 per patiënt verhoogd met de sanctiewaarde.

Sanctie 8: een patiënt moet gepland staan op zijn optimale dag.

Terwijl sancties 6 en 7 gezien kunnen worden als strengere limietsancties, heeft sanctie 8 een

milder karakter. Het is dan ook de bedoeling dat de sanctiewaarde bij deze sanctie lager ligt, dan

bij de twee voorgaande datumsancties. Het programma bekijkt opnieuw bij iedere patiënt het

blok en de dag waarop deze valt. Vervolgens wordt nagegaan hoeveel dagen deze dag voor of na

de optimale dag van de patiënt ligt. Dan kan sanctie 8 verhoogd worden met het product van de

sanctiewaarde en deze in dagen uitgedrukte afstand.

35

Sanctie 9: een tafeltype mag op geen enkele dag overbezet raken.

Er wordt verondersteld dat elke tafel minstens beschikbaar is vanaf de aanvang van het vroegste

blok van de geïnspecteerde dag tot wanneer de laatste operatie van de dag beëindigd is. Deze

tijdstippen worden dus bij iedere dag eerst verzameld. Dit moet bij iedere sanctieberekening

opnieuw gebeuren. Bij elke planning zal het eindtijdstip immers verschillen. Vervolgens wordt de

bezette tijd van elk tafeltype bijgehouden. Dit is de som van de totale tijden van alle patiënten die

gebruik maken van het bekeken tafeltype op de geïnspecteerde dag. Nadien wordt de beschikbare

tijd van elk tafeltype berekend door bij elk type het aantal tafels te vermenigvuldigen met het

tijdsinverval, beschreven in het begin van deze sanctieparagraaf. Tenslotte wordt sanctie 9

verhoogd met de sanctiewaarde voor iedere minuut die de bezette tijd meer telt dan de

beschikbare tijd. Dit gebeurt bij elk tafeltype en voor elke dag.

Sanctie 10: een chirurg mag op geen enkele dag overbezet raken.

Chirurgen voeren verschillende activiteiten uit (de Kreuk et al., 2003). In deze thesis wordt de

beschikbaarheid van chirurgen echter aan slechts één activiteit gerelateerd: het opereren van

patiënten.

Bij de hospitaalgegevens werden eerst bij elke chirurg de discipline en de dagelijkse beschikbare

tijd vermeld. Deze sanctie groepeert deze gegevens en verkrijgt zo de totale dagelijkse

beschikbare tijd per discipline. Vervolgens wordt elke dag en discipline overlopen. Telkens een

patiënt met de juiste discipline op de behandelde dag gepland staat, wordt de bezette tijd van de

discipline in kwestie aangepast. Tenslotte kent het programma de dagelijkse bschikbare- en

bezette tijd per discipline. Sanctie 10 wordt per overbezette minuut verhoogd met de

sanctiewaarde en dit voor iedere discipline.

Sanctie 11: de drie verplegertypes mogen op geen enkele dag overbezet raken.

Deze sanctie wordt voor de vijf dagen herhaald. Om de bezette tijd van de lopende-,

instrumentele- en anesthesie verplegers te kunnen berekenen, zijn telkens een aantal gegevens

vereist. Eerst wordt de totale tijd van alle operaties, die gepland staan op de relevante dag,

opgedeeld naargelang het major- of minor-operaties betreft en opgeteld. Nadien kan voor elk

verplegertype de totale bezette tijd berekend worden, door de major- en minor-tijd te

vermenigvuldigen met het respectievelijk aantal vereiste aanwezigheden. Hoeveel verplegers van

36

elk type er aanwezig moeten zijn bij een major- en minor-operatie werd uit de hospitaalgegevens

afgelezen. Uit de hospitaalgegevens haalde het programma ook het aantal verplegers en de

dagelijkse beschikbare tijd van elk verplegertype. Door deze te vermenigvuldigen wordt de totale

beschikbare tijd verkregen. Tenslotte wordt voor elk verplegertype sanctie 11 verhoogd met het

product van de sanctiewaarde en het aantal minuten dat de bezette tijd meer telt dan de

beschikbare tijd.

Sanctie 12: bij elke operatie moet een anesthesist aanwezig zijn.

We veronderstellen dat iedere anesthesist gekwalificeerd is om een operatie van eender welke

discipline te ondersteunen. Anesthesisten werden dus niet in types opgedeeld en de bezette tijd

valt dan ook makkelijker te berekenen. Deze wordt gelijkgesteld aan de totale tijden van alle

patiënten die op de behandelde dag gepland staan. De beschikbare tijd is het product van het

aantal anesthesisten en hun dagelijkse beschikbare tijd. Deze twee gegevens werden opnieuw uit

de hospitaalgegevens afgelezen. Sanctie 12 wordt verhoogd met het product van de

sanctiewaarde en het verschil van de bezette- en beschikbare tijd. Uiteraard voor elke dag en

enkel indien de bezette tijd hoger ligt dan de beschikbare tijd.

Sanctie 13: de kuis- en logistieke ploeg mag op geen enkele dag overbezet worden.

Voor iedere dag wordt het aantal major- en minor-operaties nagegaan. Uit de hospitaalgegevens

werden de vereiste kuis- en opsteltijden voor major- en minor-operaties afgelezen. De totale kuis-

en opsteltijd van de behandelde dag wordt vervolgens berekend. Dit gebeurt door de major- en

minor-kuis- en opsteltijd met hun respectievelijk aantal operaties te vermenigvuldigen. De twee

producten worden tenslotte opgeteld. De beschikbare tijd wordt apart berekend voor de kuisteams

en de logistiek teams. Voor beide teams wordt simpelweg het aantal teams vermenigvuldigd met

de dagelijkse beschikbare tijd. Indien de kuisteams overbezet worden, wordt sanctie 13

uiteindelijk verhoogd met de sanctiewaarde en dit voor elke overplande minuut. Dit gebeurt ook

bij de logistieke teams.

37

Sanctie 14: in een blok mogen enkel operaties met de juiste discipline gepland staan.

Zoals sanctie 1 werd deze sanctie voornamelijk toegevoegd, omdat ze een aspect aanspreekt dat

inherent deel uitmaakt van een goede planning. Het programma werd echter zo geschreven dat

een patiënt alleen maar aan een blok kan toegewezen worden indien de patiënt en het blok

dezelfde discipline hebben. Bij de zoekmethoden zullen bovendien alleen twee patiënten van blok

wisselen indien ze dezelfde discipline vereisen. Toch wordt deze sanctie iedere keer

gecontroleerd en zal ze verhoogd worden met de sanctiewaarde telkenmale, bij het overlopen van

de planning, een patiënt wordt gevonden die aan een blok met een verkeerde discipline werd

toegewezen.

Totale sanctie: ∑ aparte sancties.

Tenslotte worden de 14 sancties opgeteld en wordt de totale sanctie verkregen. Dit getal vormt de

fitheidswaarde van een planning. Het programma zal een planning zonder overtredingen een

fitheidswaarde van 0 toekennen. Dit is dan meteen een globaal optimum. Het schema zal dan

enkel nog door het programma verbeterd kunnen worden na een verfijning van de beperkingen en

de doelfunctie.

4.5 Beschrijving genetische algoritmes

De genetische algoritmes hoeven geen verbetering met zich mee te brengen. Het is de bedoeling

dat ze de oplossingenruimtes op intelligente wijze naar betere regionen brengen. De

daaropvolgende lokale zoekmethoden zorgen dan voor de sterke verbetering.

Aangezien we niet met binaire getallenreeksen werken wordt het gebruik van alternatieverre

operatoren zoals inversie7 uitgesloten. De binaire voorstelling van bloknummers zou ertoe

kunnen leiden dat patiënten bij een verkeerde discipline terechtkomen. Hiervoor zou uiteraard

een selectieprocedure kunnen toegevoegd worden, maar dit neemt op zijn beurt extra rekenkracht

in.

7 (Reeves, 1993, blz.173)

38

Algemeen:

• De beschrijvingen beslaan één iteratie.

• Veel methodes gaven heel vlug aanleiding tot convergentie. Na slechts 1000 iteraties

bleken de meeste een populatie met identieke elementen voort te brengen. De twee

beschreven genetische algoritmes zijn echter zodanig opgesteld dat convergentie

onmogelijk wordt.

• Er wordt gewerkt met planningen bestaande uit bloknummers. Daardoor bestaat het

gevaar dat een patiënt bepaalde geschikte blokken nooit zal worden toegewezen, wanneer

hij deze initieel ook al in geen enkel populatie-element bezat. Wanneer er echter

voldoende elementen in de populatie aanwezig zijn, wordt het aannemelijk dat alle

blokken met de juiste discipline bij elke patiënt zijn vertegenwoordigd.

• Beide algoritmes laten ruimte voor sanctieverhoging. Bij genetisch algoritme 0 kan een

inferieur kind toch ouder worden, indien het gevormd werd samen met een superieur kind.

Het is namelijk de gezamenlijk totale sanctie die als maatstaf wordt gehanteerd.

Bij genetisch algoritme 2 worden de twee kindsancties wel tegen elkaar afgewogen, maar

niet ten opzichte van de oudersancties. Zo wordt telkens het beste kind geselecteerd

zonder dat dit kind van een hogere kwaliteit dan één van de ouders moet zijn. Op het

einde van elke iteratie vervangt de kindpopulatie trouwens de ouderpopulatie zonder dat

aan enige kwaliteitseis moet zijn voldaan.

Genetisch algoritme 0

Uit de populatie worden willekeurig twee verschillende elementen geselecteerd die als ouder

zullen dienen. Deze twee ouders worden nadien opgedeeld naar discipline. Het algoritme wordt

dus bij elke iteratie verschillende malen toegepast. Namelijk eens per disciplineonderdeel van de

ouder. Er zullen per iteratie dus ook evenveel oversteekpunten bepaald worden als er disciplines

zijn. De bepaling van deze oversteekpunten werd zo willekeurig mogelijk gehouden. Elk

disciplinedeel zal overal gesplitst kunnen worden. Elk deel zal wel steeds minstens één patiënt

moeten bevatten. Eens binnen een disciplinedeel een oversteekpunt is bepaald, worden uit de

twee ouders twee kinderen gevormd. Dit gebeurt voor elke discipline tot de volledige ouders

behandeld zijn. Ter verduidelijking volgt een schematische voorstelling:

39

Figuur 14: schema genetisch algoritme 0

Nu worden voor beide ouders en beide kinderen de sancties berekend. Om convergentie te

vermijden zullen ofwel beide ouders ofwel beide kinderen de selectie overleven. Enkel indien de

som van de twee oudersancties groter is dan de som van de twee kindersancties, zullen de

kinderen hun ouders in de populatie vervangen.

Genetisch algoritme 1

Ook dit algoritme werd opgebouwd met het vermijden van convergentie als criterium. Daarom

zal elke patiënt na iedere iteratie dezelfde bloknummers bezitten. Deze zullen wel anders

verdeeld zijn over de elementen in de populatie. Het voortbrengen van een kindoplossing gebeurt

patiënt per patiënt. Bij elke patiënt worden willekeurig twee verschillende ouders uit de volledige

populatie gekozen. Elke patiënt kan dus verschillende ouderelementen hebben. De bloknummers

van deze ouderoplossingen worden gekopieerd in twee kindoplossingen. Wanneer dit voor elke

patiënt is gebeurd beschikken we over twee volledige kindoplossingen. Deze hebben alle

elementen in de populatie als mogelijke ouder gehad. Vervolgens worden de sancties van de twee

kindoplossingen berekend. Het kindelement met de laagste sanctie komt terecht in een

kindpopulatie. Alle bloknummers uit de ouderpopulatie die hebben bijgedragen tot dit winnend

kindelement, worden gemarkeerd. Ze zullen niet meer worden gebruikt bij de vorming van de

40

volgende kindelementen. Kandidaat bloknummers worden immers telkens willekeurig

geselecteerd tot het programma er één tegenkomt dat nog niet gemarkeerd en dus nog niet

gebruikt is. Het vormen van kindelementen herhaalt zich tot een nieuwe kindpopulatie is

gevormd met twee elementen minder dan de ouderpopulatie. Nu telt elke patiënt nog twee

ongebruikte bloknummers in de ouderpopulatie. De twee kindelementen die hieruit voortkomen

kunnen allebei zonder sanctieberekening aan de kinderpopulatie worden toegevoegd. Dit neemt

minder tijd in beslag dan een laatste keer willekeurig bloknummers evalueren tot er twee

gevonden worden zonder markering. Bij het willekeurig kiezen wordt namelijk nooit bijgehouden

welke bloknummers al in aanmerking werden genomen. Wanneer de kindpopulatie uiteindelijk

vervolledigd is, zal ze de ouderpopulatie vervangen en kan een volgende iteratie beginnen. Ter

verduidelijking volgt een schematische voorstelling:

Figuur 15: schema genetisch algoritme 1

41

Dit algoritme zal ongetwijfeld meer tijd in beslag nemen dan genetisch algoritme 0. Terwijl bij

algoritme 0 een iteratie het voortbrengen van een kindelement inhoudt, wordt bij algoritme 1

immers bij iedere iteratie een volledig nieuwe populatie voortgebracht.

Berekening kwaliteitswaarden

• Beginsanctie: de totale sanctie van elk populatie-element uit de beginpopulatie wordt

berekend. Nadien wordt hiervan het gemiddelde genomen. Dit wordt gedaan bij elke

probleeminstantie. De uiteindelijk waarde in “lijst.txt” is het gemiddelde van de 200

probleeminstantiewaarden.

Deze waarde is niet gelijk aan het verschil tussen de initiële beginsanctie min de initiële

sanctieafname. Bij de genetische algoritmes worden immers alle populatie-elementen in

de berekening omvat, terwijl bij de initiële waarden slechts rekening gehouden wordt met

het beste- en het slechtste populatie-element.

• Sanctieafname: na afloop van de algoritmes wordt opnieuw de totale sanctie berekend van

elk element in de uiteindelijk populatie. Per probleeminstantie wordt de som van deze

sancties afgetrokken van de som van de elementensancties uit de beginpopulatie. Het

resultaat wordt gedeeld door het aantal planningen. De uiteindelijk waarde in “lijst.txt” is

het gemiddelde van de 200 probleeminstantiewaarden.

4.6 Beschrijving lokale zoekmethoden

Alvorens de lokale zoekmethoden worden toegepast, worden voor elke probleeminstantie de

populatie-elementen oplopend geordend volgens hun totale sanctiewaarde. Op deze manier

krijgen de betere planningen een grotere kans om behandeld te worden door de lokale

zoekmethoden eer het maximum aantal iteraties bereikt is.

De methodes voeren in feite altijd één van de volgende operaties uit: het verwisselen van 2

bloknummers of het verplaatsten van een enkele patiënt naar een ander blok.

Algemeen:

• De lokale zoekmethoden worden telkens voor alle 200 probleeminstanties uitgevoerd. Bij

de beschrijving wordt daar geen aandacht meer aan besteed. Er wordt telkens één iteratie

beschreven.

42

• Alle zoekmethoden worden gekenmerkt door een zekere willekeur. Zo wordt uit een

probleemgebied niet noodzakelijk de patiënt gekozen die het meest bijdraagt tot het

behandelde probleem. Ook de wijziging zelf heeft dit kenmerk.. Zo wordt een patiënt die

niet op zijn optimale dag gepland staat enkel in een blok geplaatst dat dichter bij die

optimale dag valt, maar niet noodzakelijk exact op die optimale dag valt.

• Iedere maal het programma willekeurig een patiënt of blok met de juiste kenmerken

zoekt, worden geen gegevens over de zoektocht bijgehouden. Daardoor is het mogelijk

dat dezelfde patiënt of hetzelfde blok meerdere keren wordt geëvalueerd.

• Wanneer een lokale zoekmethode een wijziging teweegbrengt in een planning, kan een

nieuwe planning ontstaan met dezelfde totale sanctiewaarde als de oorspronkelijke

planning. In dat geval zal de wijziging ongedaan gemaakt worden. Hiervoor is geen

motivatie aanwezig. Er wordt echter aangenomen dat de kans hierop zodanig klein is dat

ze geen factor is bij de beoordeling van de lokale zoekmethode in kwestie.

• Wanneer willekeurig een variabele gezocht wordt, bestaat de mogelijkheid dat de

planning geen enkele variabele met de juiste kenmerken bezit. In dat geval wordt het

aantal willekeurige evaluaties gemaximaliseerd met vier maal het aantal mogelijkheden.

Uit experiment blijkt dat dan vrijwel altijd alle mogelijkheden aan bod zijn gekomen.

Lokale zoekmethode 0: kies twee patiënten met dezelfde discipline en een verschillend blok en

verwissel ze.

Eerst wordt uit de behandelde probleeminstantie willekeurig een patiënt gekozen, die dan de

criteria levert voor de tweede patiënt. Om een tweede patiënt te bekomen wordt opnieuw

willekeurig een patiënt uit de relevante probleeminstantie gehaald en dit tot een patiënt gevonden

wordt met een zelfde discipline en een verschillend bloknummer als de eerste patiënt. De twee

patiënten wisselen van blok en indien de totale sanctie van de planning in kwestie daalt, wordt de

wijziging behouden. Blijft de totale sanctie gelijk of stijgt ze, dan wordt de wijziging ongedaan

gemaakt.

43

Lokale zoekmethode 1: kies een patiënt en wijs die een ander blok toe met een juiste discipline.

Eerst wordt uit de probleeminstantie willekeurig een patiënt gekozen. Vervolgens wordt

willekeurig een blok geselecteerd en dit tot een blok wordt gevonden met dezelfde discipline als

de patiënt, zonder dat deze patiënt al aan het blok behoort. Uiteindelijk wijst het programma de

patiënt toe aan het gekozen blok. Blijft de totale sanctie gelijk of stijgt ze, dan wordt de wijziging

ongedaan gemaakt.

Lokale zoekmethode 2: zoek een blok dat overbezet is en wijs een willekeurige patiënt uit dat

blok toe aan een ander blok met de juiste discipline.

Eerst wordt gecontroleerd of de planning overbezette blokken bezit. Dit wordt gedaan aan de

hand van sanctie 4. Deze sanctie is groter dan nul vanaf er één blok overbezet is. Is sanctie 4

gelijk aan nul, dan wordt overgegaan naar het volgende populatie-element en wordt sanctie 4

opnieuw gecontroleerd. In het andere geval worden willekeurig blokken geselecteerd tot er één

wordt gevonden met een hogere bezette- dan beschikbare tijd. Vervolgens worden op

willekeurige basis patiënten geselecteerd tot het programma een patiënt uit het overvolle blok

tegenkomt. Tenslotte wordt voor die patiënt een nieuw blok gezocht. Dit gebeurt door opnieuw

op willekeurige basis blokken te controleren tot een ander bloknummer wordt gevonden waarvan

de bezette tijd lager ligt dan de beschikbare tijd en met de juiste discipline voor de patiënt. Het

programma zal dit maximum een aantal, vier maal het aantal blokken, doen. Indien dan nog geen

geschikt blok is gevonden, wordt de zoektocht milder. De willekeurig gekozen blokken hoeven

dan niet meer te beschikken over reservetijd, maar dienen enkel de juiste discipline en een ander

bloknummer dan het origineel te hebben. Uiteindelijk wordt de patiënt toegewezen aan het nieuw

blok. Als de totale sanctie gelijk blijft of stijgt, dan wordt de wijziging ongedaan gemaakt.

Lokale zoekmethode 3: zoek een patiënt die niet op zijn optimale dag gepland staat en plaats

hem dichter bij die dag.

Analoog aan lokale zoekmethode 2 wordt eerst gecontroleerd of er überhaupt patiënten zijn die

niet op hun optimale dag gepland staan. Dit maal gebeurt dit met behulp van sanctie 8, die groter

is dan nul vanaf dat er één patiënt niet op zijn optimale dag gepland staat. Als sanctie 8 gelijk is

aan nul, wordt verdergegaan met een nieuw populatie-element. In het andere geval worden op

willekeurige basis patiënten geëvalueerd tot het programma een patiënt vindt die niet op zijn

44

optimale dag gepland staat. Voor die patiënt wordt dan een nieuw blok gezocht. Het nieuwe blok

moet de juiste discipline hebben, maar dient niet noodzakelijk op de optimale dag te vallen. Het

volstaat dat het nieuwe blok dichterbij de optimale dag plaatsvindt dan het oude blok.8

Uiteindelijk wordt de patiënt aan het nieuwe blok toegewezen. Als de totale sanctie gelijk blijft

of stijgt, dan wordt de wijziging ongedaan gemaakt.

Lokale zoekmethode 4: zoek een dag en een tafel die samen voor sancties zorgen (of de kleinste

buffer hebben) en verwissel een relevante patiënt met een patiënt uit een andere dag, met een

kortere totale duur en met dezelfde discipline.

Vooraleer tot de eigenlijke zoekmethode wordt overgegaan, worden enkele gegevens opgeslagen.

Bij iedere berekening van de sancties, wordt bijgehouden welke tafels op welke dagen overbezet

zijn. Tevens wordt de tafel-dag combinatie met de kleinste buffertijd of de hoogste overbezetting

bijgehouden. In het begin van een iteratie wordt sanctie 9 gecontroleerd. Is sanctie 9 hoger dan 0,

dan zijn er onvermijdelijk één of meerdere overbezette tafel-dag combinaties waarmee we verder

kunnen. Is sanctie 9 gelijk aan nul en er dus geen overbezette tafel-dag combinaties zijn, dan

wordt verder gewerkt met de tafel-dag combinatie met het minst reservetijd9. Vervolgens kiest

het programma willekeurig een dag en tafeltype uit en dit tot een overbezette combinatie of de

combinatie met het minst reservetijd gevonden wordt. Daarna worden willekeurig patiënten uit de

relevante probleeminstantie geëvalueerd tot er één wordt gevonden die gebruik zal maken van het

tafeltype in kwestie en gepland staat op de overbezette dag met betrekking tot dat tafeltype.

Nadien wordt de planning opnieuw willekeurig patiënt per patiënt onderzocht, tot iemand

gevonden wordt die op een andere dag gepland staat, dezelfde discipline heeft en een kortere

totale duur 10 heeft. Indien een bepaald aantal patiënten, namelijk vier maal het aantal patiënten in

de probleeminstantie, geëvalueerd is en geen geschikte patiënt gevonden is, wordt overgegaan

naar de volgende iteratie. Er wordt niet overgegaan naar een nieuw populatie-element, aangezien

niet noodzakelijk alle overbezette tafel-dag combinaties aan bod zijn gekomen. Tevens kan het

8 Er wordt van uitgegaan dat elke discipline op elke dag vertegenwoordigd is. Dit is alleszins het geval bij het gebruikte blokschema. Dit heeft als gevolg dat altijd een beter blok kan gevonden worden. 9 Men kan argumenteren dat dit onzinnig is, aangezien tevens onderbenutting niet gewenst is. Op deze wijze wordt echter getracht speelruimte te schapen voor toekomstige zoekoperaties. 10 In plaats van het criterium ‘kortere totale duur’ zou ook ‘ander tafeltype’ gebruikt kunnen worden. Indien disciplines echter slechts gebruik maken van één tafeltype, zou dit de zoektocht naar een wisselpartner onmogelijk maken. De disciplines GIHK, AHHK en PLAST maken bijvoorbeeld enkel gebruik van een buigtafel.

45

zijn dat de eerst geselecteerde patiënt een relatief korte totale duur had, waardoor de zoektocht

naar een geschikte wisselpartner, met een nog kortere totale duur, bemoeilijkt werd.

Indien toch een tweede patiënt gevonden wordt, die aan alle criteria voldoet, worden de twee

patiënten in elkanders blok gestoken. Indien de totale sanctie na deze wissel gelijk blijft of stijgt,

wordt de wijziging ongedaan gemaakt.

Lokale zoekmethode 5: zoek een discipline en een dag die samen de sancties verhogen doordat

de chirurgen overbezet zijn (of zoek een discipline en een dag met de kleinste buffer) en verwissel

een relevante patiënt met een patiënt met een andere dag, een kortere operatieduur en dezelfde

discipline.

Analoog aan lokale zoekmethode 4 worden enkele gegevens opgeslagen, vooraleer tot de

eigenlijke zoekmethode wordt overgegaan. Bij iedere berekening van de sancties, wordt

bijgehouden op welke dagen, chirurgen van een bepaalde discipline overbezet zijn. Tevens wordt

de discipline-dag combinatie met de kleinste buffertijd qua chirurgen of de hoogste overbezetting

van chirurgen bijgehouden. Nadien wordt sanctie 10 gecontroleerd. Is deze sanctie groter dan nul,

dan is er minstens één discipline waarvan de chirurgen overbezet zijn op een bepaalde dag. Is

deze sanctie gelijk aan nul, dan werken we verder met de discipline-dag combinatie waarbij de

chirurgen het minst beschikbare tijd over hebben. Nu kan het programma willekeurig disciplines

en dagen evalueren tot het een combinatie vindt die eerder vatbaar leek voor verbetering. Met

betrekking tot deze combinatie wordt de planning vervolgens willekeurig onderzocht tot een

patiënt gevonden wordt met een gelijke dag en discipline. Nadien worden opnieuw patiënten uit

het populatie-element geëvalueerd tot een tweede patiënt is gevonden. Deze tweede patiënt moet

met betrekking tot de eerste patiënt volgende kenmerken bezitten: een gelijke discipline, een

kortere operatieduur en een verschillende dag. Indien na een bepaald aantal evaluaties, vier maal

het aantal patiënten in de probleeminstantie, geen geschikte patiënt is gevonden, wordt

overgegaan naar de volgende iteratie. Wordt wel een geschikte patiënt gevonden, dan wisselen

beide patiënten van blok. Indien de totale sanctie hierna gelijk blijft stijgt, wordt de wijziging

ongedaan gemaakt.

46

Lokale zoekmethode 6: zoek de drukste dag voor de anesthesisten en verzet een patiënt van die

dag naar een blok met de juiste discipline en op de dag waarop de anesthesisten het minst bezet

zijn.

Wanneer de sancties worden berekend, wordt ook bijgehouden op welke twee dagen de

anesthesisten het meest- en het minst bezet zijn. Als de lokale zoekmethode begint worden de

patiënten willekeurig onderzocht tot een patiënt wordt gevonden die op de drukste dag gepland

staat. Nadien worden willekeurig blokken geëvalueerd tot een blok gevonden wordt dat valt op de

minst drukke dag voor anesthesisten en de discipline behandelt die overeenstemt met deze van de

patiënt. Uiteindelijk wordt de patiënt verplaats naar het nieuwe blok. Indien deze wijziging een

hogere of stagnerende totale sanctie veroorzaakt, wordt ze ongedaan gemaakt.

Lokale zoekmethode 7: zoek de dag waarop de kuis- of logistieke ploeg(en) het meest bezet zijn

en verwissel een relevante major-patient met een minor-patient uit een blok met de juiste

discipline en op een dag waarop de ploegen niet overbezet zijn of het minst bezet zijn.

De twee dagen waarop de kuis- en logistieke ploeg respectievelijk het meest en het minst bezet

zijn, zijn bekend na iedere berekening van de sancties. Dit geldt ook voor alle dagen waarop de

kuis- en logistieke ploeg niet overbelast is. De lokale zoekmethode onderzoekt dan willekeurig de

planning tot een patiënt is gevonden die een major-operatie dient te ondergaan en gepland staat

op de dag waarop de kuis- en logistieke ploeg het meest bezet is. Het programma krijgt een aantal

keer, vier maal het aantal patiënten in de probleeminstantie, de kans om een geschikte patiënt te

vinden. Als het daar niet in slaagt, dan wordt overgegaan naar het volgende populatie-element.

Bij een volgende iteratie zou namelijk opnieuw geen patiënt met een major-operatie gevonden

worden, aangezien maar één dag in aanmerking komt. Indien er dagen beschikbaar zijn waarop

de kuis- en logistieke ploeg niet overbezet zijn, dan worden de blokken in willekeurige volgorde

onderzocht, tot het programma een blok vindt met de juiste discipline dat op zo’n dag valt. Het

kan echter ook gebeuren dat de kuis- en logistieke ploeg op elke dag overbelast worden. In dat

geval wordt willekeurig een blok gezocht met de juiste discipline dat op de dag valt waarop de

kuis- en logistieke ploeg het minst belast worden.

Uiteindelijk is het blok bekend waarin een patiënt met een minor-operatie gezocht wordt. Dit

gebeurt opnieuw willekeurig. Indien na een bepaald aantal gecontroleerde patiënten, vier maal

het aantal patiënten in de probleeminstantie, nog geen geschikte patiënt is gevonden, wordt

47

overgegaan naar de volgende iteratie. Hier wordt dus niet direct overgegaan naar een volgend

populatie-element. Bij iedere iteratie kunnen namelijk meerdere blokken in aanmerking komen

om een patiënt met een minor-operatie in te zoeken. Uiteindelijk wisselen de twee patiënten van

blok. Indien deze wissel de totale sanctie niet verlaagt, wordt ze ongedaan gemaakt.

Berekening kwaliteitswaarden

• Beginsanctie: telkens een nieuw populatie-element wordt behandeld, wordt de totale

sanctie van die planning berekend. Wanneer wordt overgestapt op de volgende

probleeminstantie wordt van deze sancties het gemiddelde genomen. Uiteindelijk hebben

we voor elke probleeminstantie een gemiddelde sanctie die enkel rekening houdt met de

populatie-elementen die daadwerkelijk aan bod zijn gekomen. Het gemiddelde van deze

200 getallen vormt de uiteindelijke beginsanctie in “lijst.txt”.

• Sanctieafname: telkens wanneer overgegaan wordt naar een volgend populatie-element,

wordt van het net behandelde element de eindsanctie berekend. Dit gebeurt ook bij het

laatst behandelde element. Hiervan wordt opnieuw het gemiddelde berekend. Per

probleeminstantie wordt vervolgens deze gemiddelde eindsanctie van de gemiddelde

beginsanctie afgetrokken. Uiteindelijk kan dan de gemiddelde sanctieafname over de

probleeminstanties heen berekend worden. Deze waarde komt terecht in “lijst.txt”.

4.7 Beschrijving VNS

De meeste lokale zoekmethoden onderzoeken slechts één omgeving (Hansen en Mladenović,

2001, blz. 450). Daardoor bestaat het risico in een zwak lokaal optimum terecht te komen. Een

VNS probeert dit tegen te gaan door van omgeving te wisselen. Wanneer meer dan één omgeving

onderzocht wordt dienen de volgende drie vragen beantwoord te worden (Hansen en Mladenović,

2001, blz. 451):

1) Hoeveel en welke omgevingen moeten gebruikt worden?

Alle aparte lokale zoekmethoden geschreven voor dit onderzoek worden in de VNS ingesloten.

De meeste richten zich op een specifieke sanctie en op deze manier worden deze sancties hopelijk

aangesproken. In totaal bevat de VNS acht omgevingen.

48

2) Wat is hun volgorde in de zoektocht?

De volgorde waarin de lokale zoekmethoden werden geschreven diende als uitgangspunt.

Vervolgens werden hun prestaties met een klein aantal iteraties bekeken. Methode 2 presteerde

gevoelig beter dan de overige en werd daarom eerst geplaatst. Een mogelijke verklaring is dat

deze methode zich richt op overbezette blokken, waardoor na de toepassing ervan een planning

een egalere verdeling kent, waardoor ook de andere sancties zullen dalen. Vervolgens werden de

twee algemenere methodes 0 en 1 relatief voorop geplaatst. Het leek logisch deze eerst een kans

te geven alvorens specifiek sancties aan te spreken. Tenslotte werd lokale methode 4 naar achter

verschoven ,doordat deze samen met methode 7 een lagere sanctieafname optekende.

De uiteindelijk volgorde van de lokale zoekmethoden binnen de VNS is als volgt: 2 0 1 3 5 6 4 7.

3) Welke strategie moet gebruikt bij het veranderen van omgeving?

Wanneer een methode een gegeven aantal opeenvolgende iteraties geen verbetering meer

oplevert, wordt een volgende methode ingeschakeld. Soms kan een methode geen verbetering

meer teweegbrengen, omdat de sanctie die ze probeert te verminderen reeds gelijk is aan nul. Dan

wordt meteen overgegaan op een andere methode, zodat geen iteraties worden verspild.

Indien alle methodes aan bod zijn gekomen en het maximaal aantal iteraties nog niet bereikt is,

zal de eerste lokale zoekmethode opnieuw in werking treden met een volgend populatie-element.

Er wordt verwacht dat de VNS minder populatie-elementen zal behandelen dan de aparte lokale

zoekmethoden, aangezien verscheidene malen het maximaal aantal iteraties zonder verbetering

moet zijn bereikt eer naar een volgend element wordt gegaan.

De berekening van de beginsanctie en de sanctieafname verloopt analoog aan deze bij de aparte

lokale zoekmethoden.

49

4.8 Opzet

Sanctiewaarden

De totale sanctie doet dienst als fitheidswaarde. De verschillende sanctiewaarden spelen daarbij

een grote rol. Hoe groter de sanctiewaarde, hoe meer de desbetreffende sanctieovertreding ervoor

zal zorgen dat de kwaliteit van een planning lager zal worden aanzien. Hoe zwaar aan een

bepaalde overtreding getild wordt, heeft te maken met de prioriteiten die gesteld worden. De

absolute waarden spelen niet zo’n grote rol, aangezien de fitheidswaarde gebruikt wordt om de

kwaliteit van verschillende planningen te vergelijken. De laagste sanctiewaarde wordt dan ook op

1 gezet en de andere worden overeenstemmend ingegeven. Doordat steeds naar die totale sanctie

als kwaliteitsmeter gekeken wordt is er geen garantie dat een lokale zoekmethode gericht op een

specifieke sanctie die sanctie ook daadwerkelijk zal doen dalen. De relatieve belangrijkheid van

de sancties, aangetoond door de sanctiewaarden, zal hier hopelijk de bovenhand nemen.

Verder dient rekening gehouden te worden met de aard van de overtreding. Sommige sancties

worden verhoogd per overtredende patiënt, andere per overbezette minuut. Deze laatste krijgen

een lagere sanctiewaarde, zodat deze sancties niet allesoverheersend zouden worden.

Er wordt geen gebruik gemaakt van harde sancties. Sommige sanctiewaarden worden echter

zodanig hoog ingegeven, zodat de kans klein wordt dat het programma extra overtredingen met

betrekking tot deze sancties toelaat om de andere sancties omlaag te krijgen.

Aantal iteraties en populatie-elementen

Het aantal iteraties wordt éénmaal ingegeven. Het geldt voor de generatie van initiële schema’s,

de genetische algoritmes, de aparte lokale zoekmethoden en de VNS. Er dient op gelet te worden

dat het aantal iteraties minstens even hoog is als het aantal populatie-elementen. Anders wordt bij

de creatie van de initiële schema’s niet de volledige populatie ingevuld. De beschreven resultaten

zijn tot stand gekomen met 5000 iteraties. Doordat de mogelijkheid om extra populatie-elementen

aan te maken niet werd geprogrammeerd, werd de populatie gevuld met een groot aantal

elementen.

Elke populatie telt 50 elementen. Mede hierdoor werd het aantal toegestane iteraties zonder

verbetering op 100 opeenvolgende iteraties gezet. Daardoor is het onmogelijk dat alle elementen

aan bod zijn gekomen, zonder dat het maximaal aantal iteraties is bereikt. Zelfs indien geen

50

enkele methode een wijziging teweegbrengt die leidt tot de daling van de totale sanctie. Wanneer

deze 100 iteraties zonder verbetering bereikt zijn, zal het programma overschakelen naar ofwel

een volgend populatie-element bij de aparte lokale zoekmethoden, ofwel naar een volgende

omgeving bij de VNS. Het aantal toegestane iteraties zonder verbetering zal, bij een gelijk

blijvend totaal aantal iteraties, een grote invloed hebben op de uiteindelijk beginsancties en

sanctieafnames in “lijst.txt”. Wanneer een deze variabele van toepassing is, bij de lokale

zoekmethoden, wordt immers enkel rekening gehouden met behandelde populatie-elementen.

Een hoog getal zal de elementen meer kansen geven, vooraleer een ander element wordt

ingeschakeld.

Volgende figuur toont de ingegeven waarden:

Figuur 16: sanctiewaarden, iteraties en populatie-elementen in hospitaal.txt

Aantal patiënten

In “probleeminstanties genereren.exe” werd het getal 160 ingegeven. Er werden dus 200

probleeminstanties aangemaakt met een patiëntenaantal tussen 130 en 190. De totale beschikbare

bloktijd zal ongeveer gelijk zijn aan de totale vereiste tijd van een lijst met 160 patiënten. Er is

echter geen garantie dat de disciplineverdeling van de probleeminstanties en de blokken

gelijkaardig zal zijn.

51

5 Resultaten

5.1 5000 iteraties

5.1.1 Overzicht waarden

Figuur 17: lijst.txt

52

Meteen wordt duidelijk dat alle methoden hun maximaal aantal iteraties hebben bereikt. Er waren

dus voldoende populatie-elementen voorhanden.

5.1.2 Tijdsgebruik

De tijdswaarde drukt uit hoeveel milliseconde elke methode gemiddeld nodig had om een

probleeminstantie te behandelen. Meteen valt op dat de genetische algoritmes aanzienlijk meer

seconden nodig hadden dan de lokale zoekmethoden. Het willekeurig aanmaken van initiële

schema’s nam het minst tijd in beslag. Dit is logisch, aangezien door het gebrek aan enige

heuristiek er geen variabelen op bepaalde kenmerken moesten worden onderzocht.

Als men de lokale zoekmethoden rangschikt volgens tijdsgebruik, dan is de volgorde exact

hetzelfde bij alle .exe bestanden. Volgende figuur geeft enkele gegevens met betrekking tot

“zoek0.exe” visueel weer. Het aantal milliseconden en behandelde elementen zijn beide steeds

het gemiddelde per probleeminstantie.

Figuur 18: aantal milliseconden en behandelde elementen per lokale methode

Het tijdsverbruik vertoont een opvallend negatieve relatie met het aantal behandelde elementen.

Hier kan geen sluitende verklaring voor gegeven worden. Vaak is het wel zo dat wanneer een

bepaalde sanctie gelijk is aan nul, de zoekmethode gericht op die sanctie zal overgaan op een

53

andere populatie-element. Als dit gebeurt wordt nog altijd een iteratie in rekening gebracht die

dan weinig tijd heeft gebruikt.

Houden we geen rekening met het aantal behandelde elementen en schrijven we het tijdsverbruik

volledig toe aan de werking van de zoekmethoden, dan kan de VNS aanzien worden als de

traagste methode terwijl methode 7 veruit de vlugste is.

Volgende figuur toont het gemiddeld aantal behandelde elementen per probleeminstantie voor

elke methode en het totaal aantal minuten voor elk .exe bestand.

Figuur 19: tijdsduur en aantal behandelde elementen per bestand

Over de drie bestanden heen zijn met betrekking tot het aantal behandelde elementen slechts

milde verschillen waar te nemen. “Zoek2.exe” zal met het genetisch algoritme 1, dat per iteratie

een volledige populatie voortbrengt een veel langere duur kennen. In figuur 17 zien we dat de

lokale zoekmethoden tevens meer tijd nodig hebben om aan 5000 iteraties te raken wanneer eerst

een genetische algoritme is toegepast. Voornamelijk genetisch algortime 1 zorgt voor een

stijging.

54

5.1.3 Oplossingskwaliteit

De initiële heuristiek bij de opmaak van de beginschema’s zorgt voor een lagere beginsanctie dan

het willekeurig proces. De willekeurige methode heeft dus nog veel ruimte voor sanctieverlaging.

Ook al heeft ze daadwerkelijk een hoge sanctieafname, toch is haar eindsanctie (beginsanctie –

sanctieafname) hoger dan bij de opstelling gebruik makend van initiële heuristiek.

Genetisch algoritme 0 brengt slechts een lichte daling van de gemiddelde totale sanctie teweeg,

terwijl genetisch algoritme 1 een grote stijging veroorzaakt. Bij algoritme 0 worden dan ook

oudersancties met kindersancties vergeleken, terwijl dit bij algoritme 1 niet het geval is.

Als we van de beginsanctie de sanctieafname aftrekken, houdt de VNS telkens de laagste sanctie

over. Beide genetische algoritmes maken weinig verschil, maar de VNS na genetisch algoritme 0

levert globaal het beste resultaat.

We zullen nu de prestaties van elke lokale methode bekijken en de invloed van de genetische

algoritmes hierop. Om rekening te houden met de verschillende beginsancties wordt in volgende

figuur de sanctieafname als procent van de beginsanctie voorgesteld.

Figuur 20: procentuele sanctieafname per methode

55

De lokale zoekmethoden slagen er niet in een hogere procentuele sanctieafname te

verwezenlijken met behulp van genetisch algoritme 0. Wanneer de lokale methodes toegepast

worden na genetisch algoritme 1, zorgen ze voor een opmerkelijk hogere procentuele afname.

Absoluut bekeken maakt dit echter de sanctiestijging niet goed die door dit genetisch algoritme

werd veroorzaakt.

Relatief gezien presteerde de VNS telkens het beste. Enkel aparte methode 2 deed lichtjes beter

na genetisch algoritme 1. Ook de twee algemene lokale zoekmethoden 0 en 1 scoren goed, ook al

maken zij amper gebruik van probleemspecifieke informatie. Lokale zoekmethode 4 en 7 zijn

duidelijk de zwakste. Deze twee methodes richten zich respectievelijk op sancties 9 en 13. Ze

worden echter ook uitgevoerd indien deze sancties gelijk zijn aan nul. Een mogelijke verklaring

is dat beide sancties bestraft worden met een heel erg kleine sanctiewaarde gelijk aan 1. Indien

deze sancties na behandeling door methodes 4 en 7 daadwerkelijk minder overtredingen tellen,

zal dit dus minder invloed hebben op de totale sanctie.

Volgende figuren maken echter duidelijk dat de lokale zoekmethoden weinig eigen accenten

leggen qua sancties. De cijfers zijn het gemiddelde van de aparte sanctiewaarden van alle 200

probleeminstanties. De lokale zoekmethoden werden niet voorafgegaan door een genetisch

algoritme.

Figuur 21: sancties per lokale zoekmethode

56

In grafiekvorm valt op dat enkel bij sancties 3 en 4 enig verschil is waar te nemen. Deze sancties

die het overschrijden van de overwerkcapaciteit en het overbezetten van een blok bestraffen,

kennen beide dan ook een hoge sanctiewaarde die per bestrafte minuut wordt bijgeteld. Minieme

verschillen zullen dan ook al redelijk zwaar doorwegen.

Figuur 22: grafiek sancties per lokale zoekmethode

Sancties 3, 5, 6 en 7 werden allemaal een hoge sanctiewaarde toegekend. Toch zijn deze sancties,

relatief bekeken, niet zo hoog. Dit kan zijn omdat het simpelweg makkelijker was om deze

sancties te vermijden, of omdat het programma deze sancties prioritair behandelde.

In figuur 22 zien we een piek bij sancties 4 en 11. Deze sancties bestraffen overbezette blokken

en het overbelasten van verpleegkundigen. Dit toont de nood aan extra verplegers aan. Tevens

blijkt hieruit dat het blokschema ofwel een onaangepaste capaciteitsverdeling kent, of dat er

gewoon te weinig beschikbare bloktijd aanwezig is. Wanneer de eindplanningen bekeken worden

valt op dat disciplines TVHK en ORTH vaak een capaciteitsoverschot bezitten dat zou kunnen

herverdeeld worden naar de andere disciplines: GIHK, NEURO, PLAST en AHHK.

57

5.1.4 Efficiëntie

Een heuristische methode dient niet alleen een goed resultaat te vormen, maar moet dit ook doen

binnen een redelijke tijd. Om de efficiëntie van de aparte lokale zoekmethoden en de VNS na te

gaan werd de gemiddelde sanctieafname en de gemiddelde tijd waarin ze die verwezenlijkten

bekeken. De gegevens zijn afkomstig uit “lijst.txt” (figuur 17).

Figuur 23: sanctieafname en aantal milliseconden per methode

Hoe efficiënter de methode, hoe meer ze zich linksboven zal bevinden in de grafiek. We merken

een duidelijke positieve relatie op. Geen enkele methode lijkt superieur te presteren met

betrekking tot een kenmerk zonder daarbij het andere te verwaarlozen. Vele methoden liggen wel

binnen hetzelfde nauwe tijdsinterval terwijl hun sanctieafname grotere verschillen vertoont.

Lokale methoden 0 en 1, die de meest algemene procedure uitvoeren, kunnen relatief gezien als

efficiënt worden beschouwd.

In figuur l7 zien we dat genetisch algoritme 1 heel inefficiënt is. Niet alleen heeft deze methode

een erg hoog tijdsgebruik, ze zorgt ook voor populaties met een sterk gestegen sanctie en een

groter tijdsverbruik bij de lokale zoekmethoden. Dit algoritme is wel het enige waar het aantal

58

populatie-elementen een grote tijdsinvloed op uitoefent. Hoe meer elementen, hoe meer werk per

iteratie zal moeten verzet worden.

5.1.5 Invloed aantal patiënten

Hoe meer patiënten een probleeminstantie bevat, hoe meer de aanwezige middelen en het

personeel onder druk zal komen te staan. Om de invloed van het aantal patiënten op de kwaliteit

van de uiteindelijke schema’s te bekijken werden de eerste 60 probleeminstanties oplopend

gerangschikt volgens hun patiëntenaantal. Zestig is tevens het aantal mogelijke

patiëntenaantallen. Vervolgens werden de 60 bijhorende eindsancties van de planningen tot stand

gekomen met de VNS en zonder genetisch algoritme als kwaliteitswaarde gebruikt. Deze

gegevens werden afgelezen uit “eindplanningen bekijken.exe”.

Figuur 24: relatie aantal patiënten en eindsanctie

Er valt een stijgende trend waar te nemen. Dit houdt steek aangezien een planning met een hoog

aantal patiënten meer overtredingen zal begaan en moeilijker zal zijn te optimaliseren. De

afwijkingen kunnen te wijten zijn aan verschillende vereisten bij lijsten met een zelfde aantal

patiënten en aan de zoekmethoden die geen vaststaand resultaat zullen voortbrengen. Zelfs niet

wanneer ze meerdere malen op een zelfde probleeminstantie worden toegepast.

59

5.2 Variabel aantal iteraties

In totaal werden de programma’s vier keer opgestart. Hierbij werden dezelfde probleeminstanties

gebruikt en het aantal populatie-elementen werd constant gehouden. Het aantal iteraties werd

gewijzigd om hun invloed na te gaan op de oplossingskwaliteit en het tijdsgebruik. De resultaten

die tot stand kwamen met een totaal van 5000 iteraties en een toegestaan maximum van 100

opeenvolgende iteraties zonder verbetering werden besproken in sectie 5.1.

In deze sectie vergelijken we de resultaten die werden bereikt met:

1. 1000 iteraties met maximaal 100 opeenvolgende iteraties zonder verbetering.

2. 1000 iteraties met maximaal 20 opeenvolgende iteraties zonder verbetering.

3. 5000 iteraties met maximaal 100 opeenvolgende iteraties zonder verbetering.

4. 20000 iteraties met maximaal 400 opeenvolgende iteraties zonder verbetering.

De lijsten die werden aangemaakt bij (1), (2) en (4) met het aantal milliseconden, het aantal

iteraties, de sanctieafname en de beginsanctie werden aan de bijlage toegevoegd.

(1) en (3) hebben een zelfde aantal mogelijke iteraties zonder verbetering en een verschillend

totaal aantal iteraties. Onderstaande figuur toont aan dat de kwaliteit van de planningen

nauwelijks verschilt. Doordat alle schema’s tot stand zijn gekomen met 100 toegestane,

opeenvolgende iteraties zonder verbetering, hebben de beste initiële planningen, die eerst werden

gerangschikt, evenveel nuloperaties mogen ondergaan. Hieruit blijkt wel dat een quasi evengoed

resultaat kan worden verkregen met minder iteraties en dus met minder tijd. De grafiek toont de

beginsanctie min de sanctieafname bij elke methode. De gebruikte waarden komen uit de twee

bijhorende “lijst.txt”.

60

Figuur 25: eindsancties met 1000 en 5000 iteraties

Om toch een kwaliteitsverschil te kunnen waarnemen werd dan ook besloten het aantal iteraties

zonder verbetering evenredig met het totaal aantal iteraties aan te passen. We vergelijken de

eindsancties bij de planningen uit (2), (3) en (4) en hun tijdsgebruik met behulp van hun

“lijst.txt”. Eerst werd voor elke methode, niet voorafgegaan door een genetisch algoritme, de

eindsanctie (beginsanctie – sanctieafname) berekend. Vervolgens werd van de tijdswaarden en de

bekomen eindsancties het gemiddelde genomen bij elke bekeken opzet.

Figuur 26: eindsancties en tijdsgebruik bij verschillende opzet

61

Nu beide iteratiewaarden variabel zijn merken we duidelijk een kwaliteitsverschil tussen de

opzetten. Er vind een duidelijk kwaliteitsverlies plaats bij opzet (2). Ondanks het grote verschil in

aantal iteraties tussen opzetten (3) en (4), daalt de gemiddelde eindsanctie slechts lichtjes. Het

gemiddelde tijdsgebruik per probleeminstantie stijgt evenredig met het totaal aantal iteraties en

zal dit vermoedelijk blijven doen. Welke opzet men de meest optimale vind is echter eveneens

afhankelijk van het belang dat aan beide kenmerken wordt gehecht.

Tenslotte toont figuur 27 de beginsanctie en sanctieafname bij elke methode niet voorafgegaan

door een genetisch algoritme en dit eveneens voor de opzetten (2), (3) en (4).

Figuur 27: beginsanctie en sanctieafname bij verschillende opzet

62

We merken een negatieve relatie op. Hoe lager de beginsanctie, hoe moeilijker het wordt om ze

nog te doen afnemen. Toch slaagt een opzet met hogere iteratiewaarden erin om oplossingen van

een betere kwaliteit te genereren dan een opzet met lagere iteratiewaarden (zie figuur 26). De

hoogste sanctieafname wordt echter bereikt door de VNS in opzet (2). Lokale zoekmethodes 4, 5,

6 en 7 zijn duidelijk bij elke opzet het zwakst. Een hoog aantal iteraties zorgt voor initiële

schema’s van een betere kwaliteit. Door het nogal altijd sterk willekeurig karakter van de initiële

heuristiek is dit niet verwonderlijk. Ook lokale zoekmethoden 1 en 2 lijken qua sanctieafname

relatief gezien, tegenover de andere methoden in dezelfde opzet, gebaat met een hoger aantal

iteraties. Methoden 1 en 2 hebben van alle zoekmethodes steeds het meest aantal mogelijkheden

tot wijziging van een planning. Steeds komen alle patiënten in aanmerking om een ander

bloknummer te krijgen. Dit zou een verklaring kunnen zijn.

Tenslotte merken we een positieve relatie op tussen het aantal behandelde populatie-elementen

(zie figuur 19) en de beginsanctie. Dit komt doordat de elementen meteen na de initiële heuristiek

of de genetische algoritmes oplopend worden geordend met betrekking tot de totale sanctie. Hoe

meer elementen gewijzigd worden, hoe meer de elementen met een hogere sanctie dus aan bod

zullen komen.

6 Conclusie

6.1 Uitbreidingen

Een juistere hospitaalomgeving leidt tot planningen die beter bruikbaar zullen zijn in het echte

leven. De beschreven hospitaalomgeving was gebaseerd op deze van het universitair ziekenhuis

te Gent. Een aantal beperkingen kunnen echter worden verfijnd of toegevoegd:

• Apparatuur: apparatuur is meestal toegewezen aan een theater en operaties die deze

apparatuur nodig hebben worden in deze kamer gepland. Indien nodig kan apparatuur

verplaatst worden naar een ander theater, maar dit wordt in regel vermeden rekening

houdend met de gevoelige natuur van de apparatuur. Sommige apparatuur kan niet

worden verplaatst, aangezien ze een onderdeel vormt van de kamer en het dwingend

63

wordt om operaties overeenkomstig te plannen (Sier et al., 1997, blz.885). Deze scriptie

omvatte enkel operatietafels. Men kan beperkingen toevoegen met betrekking tot ander

materiaal, eventueel met bijhorende operatiekamerbeperkingen.

• Beschikbare tijden: men kan nagaan hoeveel chirurgen elke discipline precies telt en hun

beschikbare tijd op dagelijkse basis implementeren. Ook de beschikbare tijden van tafels,

verpleegkundigen en ander personeel kan verfijnd worden.

De probleeminstanties werden automatisch opgesteld. Men kan hieraan een realistische

disciplineverdeling aan toevoegen. Ook de toevoeging van een verwachtingswaarde en

standaardafwijking aan de verschillende tijden kan bekeken worden.

De gebruikte heuristiek kan tevens aangepast worden. Om eventueel heuristiek van een betere

kwaliteit te bekomen, kunnen enkele variabelen en procedures getest worden:

• Genetische algoritmes: de waarde van doelfunctie zonder bewerking gebruiken als

fitheidmaatstaf is een mogelijkheid, maar brengt nadelen met zich mee. De populatie

heeft namelijk de neiging te convergeren naar een set van gelijkaardige chromosomen

waartussen discriminatie moeilijk wordt, gebruik makend van een naïeve fitheidmaatstaf.

Indien een populatie veel zwakke chromosomen en slechts enkele met een hoge kwaliteit

kent, kan een naïeve fitheidmaatstaf leiden tot een vlugge dominantie van deze laatste. Dit

leidt tot voortijdige convergentie naar een lokaal optimum. Een schaalprocedure kan

beide problemen proberen verlichten (Reeves, 1993, blz. 168-169).

De totale sanctie diende simpelweg als fitheidmaatstaf voor de planningen. Andere

mogelijkheden werden niet onderzocht.

Ouderplanningen werden willekeurig uit de populatie geselecteerd. Men heeft echter ook

de optie om deze te selecteren op basis van hun totale sanctie.

• Initiële heuristiek: deze kan uitgebreid worden om meteen enkele belangrijke

overtredingen aan te spreken. Door bijvoorbeeld nooit een blok toe te wijzen aan een

patiënt met een langere totale duur dan de duur van dat blok.

64

Het model zou kunnen uitgebreid worden naar andere planningsfasen. De planningen die worden

verkregen dienen bijvoorbeeld nog een volgorde toegekend te worden. Tevens is het mogelijk dat

extra patiënten dienen gepland te worden of dat sommige patiënten wegvallen, wanneer reeds

schema’s werden opgemaakt. In de plaats van dezelfde heuristiek toe te passen op de gewijzigde

probleeminstantie kan een algoritme dat deze aanpassingen zo vlot mogelijk inpast eventueel

soelaas bieden.

6.2 Conclusie

Één van de duurste middelen in een hospitaal is het operatiekwartier. Aangezien tot 70% van alle

hospitalisaties een verblijf in het OK inhoudt, is een optimaal gebruik van de OK-capaciteit van

cruciaal belang (Oostrum et al., 2002, blz. 2). Deze scriptie probeert om OK-planningen zo

optimaal mogelijk te krijgen via een heuristische benadering. Daartoe werd via beperkingen een

hospitaalomgeving beschreven, gebaseerd op het universitair ziekenhuis in Gent. Vervolgens

werden 200 probleeminstanties aangemaakt die door deze omgeving zo optimaal mogelijk

geholpen moeten worden. Planningen werden opgesteld waarin patiënten een bloknummer

toegekend kregen, waardoor ze automatisch ook een operatiekamer en een operatiedag werden

toegewezen. Eveneens werden 14 sancties beschreven die overtredingen van deze planningen

vaststelden en deze een waarde toekenden.

Om deze totale sanctiewaarden omlaag te krijgen en zo optimalere planningen te verkrijgen werd

met behulp van de programmeertaal C gebruik gemaakt van heuristiek. Deze omvat initiële

heuristiek bij het opmaken van startplanningen, 2 genetische algoritmes, 8 aparte lokale

zoekmethoden en 1 VNS. Deze werden toegepast in 3 programma’s. Het eerste, “zoek0.exe”,

voerde enkel de initiële heuristiek, de lokale zoekmethoden en de VNS uit. In tweede en derde

programma, respectievelijk “zoek1.exe” en “zoek2.exe”, werden deze methoden voorafgegaan

door één van de twee genetische algoritmes.

Deze genetische algoritmes combineerden verschillende planningen van een zelfde

probleeminstantie tot een nieuwe populatie planningen. De lokale zoekmethoden voerden

vervolgens procedures uit binnen elke aparte planning die de totale sanctiewaarde ervan deed

dalen. Dit was tevens het geval bij de VNS die alle lokale zoekmethoden omvat.

65

Uit de resultaten blijkt dat alle lokale zoekmethoden planningen vormden met minder

overtredingen. Methoden 0, 1, 2 en 3 zorgden in vergelijking met de andere methoden voor een

sterke sanctiedaling. Methoden 4 en 7 waren duidelijk het zwakst qua kwaliteitsverbetering. De

VNS zorgde echter vaak voor het beste resultaat, maar deze methode nam ook het meeste tijd in

beslag. Indien de aparte lokale zoekmethoden en de VNS werden toegepast na een genetisch

algoritme nam hun tijdsgebruik toe. Bovendien leverde genetische algoritme 1 populaties met een

sterk gestegen totale sanctie. Genetisch algoritme 0 zorgde voor een heel lichte daling, maar

maakte uiteindelijk weinig onderscheid qua oplossingskwaliteit.

De methoden toelaten om een hoger aantal iteraties uit te voeren leverde betere planningen op,

maar vanaf 5000 iteraties werden de sanctiedalingen minder substantieel. Het tijdsverbruik steeg

evenredig met het totaal aantal iteraties. Het aantal opeenvolgende iteraties zonder verbetering

dat werd toegestaan bleek echter een belangrijkere factor bij de kwaliteit van de eindplanningen.

Uiteindelijk waren betere planningen mogelijk indien disciplines THHK en ORTH blokcapaciteit

zouden afstaan aan GIHK, NEURO, PLAST en AHHK. Ook het kleine aantal verpleegkundigen

zorgde steeds voor een hoge sanctie.

66

Bijlage 1: blokschema

67

Bijlage 2: verscheidene “lijst.txt”

1000 iteraties met maximaal 100 opeenvolgende iteraties zonder verbetering

68

1000 iteraties met maximaal 20 opeenvolgende iteraties zonder verbetering

69

20000 iteraties met maximaal 400 opeenvolgende iteraties zonder verbetering

70

Bijlage 3: C-code

Probleeminstanties genereren printf ("Hoeveel patienten wilt u telkens ongeveer genereren?\n"); scanf ("%d", &aantal); // het randomseed bepalen srand (time(NULL)); // de 200 probleeminstanties aanmaken for (tel[0] = 0; tel[0] < 200; tel[0]++) { // het bestand openen bestand_patienten[tel[0]] = fopen (probleeminsta ntie[tel[0]], "w"); // het aantal patienten bepalen random = rand () % 60 + (aantal - 30); // het aantal patienten en de hoofdingen in het bes tand zetten fprintf (bestand_patienten[tel[0]], "%d\n", rand om); fprintf (bestand_patienten[tel[0]], "nr\t"); fprintf (bestand_patienten[tel[0]], "disc\t"); fprintf (bestand_patienten[tel[0]], "ma/mi\t"); fprintf (bestand_patienten[tel[0]], "tafel\t"); fprintf (bestand_patienten[tel[0]], "prep.t\t"); fprintf (bestand_patienten[tel[0]], "prean.t\t") ; fprintf (bestand_patienten[tel[0]], "oper.t\t"); fprintf (bestand_patienten[tel[0]], "postan.t ") ; fprintf (bestand_patienten[tel[0]], "hospit\t"); fprintf (bestand_patienten[tel[0]], "opDag\t"); fprintf (bestand_patienten[tel[0]], "deadl\t"); fprintf (bestand_patienten[tel[0]], " age\t"); fprintf (bestand_patienten[tel[0]], "naam\n\n"); // de gegevens in het bestand zetten for (tel[1] = 0; tel[1] < random; tel[1]++) { // nummer fprintf (bestand_patienten[tel[0]], "%d\t", t el[1]); // discipline discipline = rand() % 6 + 1; fprintf (bestand_patienten[tel[0]], " %d\t", discipline); //major of minor operatie if (discipline == 4) major_minor = 2; else major_minor = rand() % 2 + 1; fprintf (bestand_patienten[tel[0]], " %d\t", major_minor); // tafel is afhankelijk van de discipline if (discipline == 1 || discipline == 2 || discipline == 5) fprintf (bestand_patienten[tel[0]], " 1\t "); if (discipline == 3) { willekeurig = rand() % 2 + 1; if (willekeurig == 1) fprintf (bestand_patienten[tel[0]], " 1\t"); if (willekeurig == 2) fprintf (bestand_patienten[tel[0]], " 4\t"); } if (discipline == 4) {

71

willekeurig = rand() % 4 + 1; if (willekeurig == 1) fprintf (bestand_patienten[tel[0]], " 1\t"); if (willekeurig == 2) fprintf (bestand_patienten[tel[0]], " 3\t"); if (willekeurig == 3) fprintf (bestand_patienten[tel[0]], " 4\t"); if (willekeurig == 4) fprintf (bestand_patienten[tel[0]], " 5\t"); } if (discipline == 6) fprintf (bestand_patienten[tel[0]], " 2\t "); //pre operatief process tijd in minuten if (discipline == 1 || discipline == 2 || discipline == 5) fprintf (bestand_patienten[tel[0]], " %d\t ", rand() % 6 + 5); if (discipline == 3) fprintf (bestand_patienten[tel[0]], " %d\t ", rand() % 13 + 3); if (discipline == 4 || discipline == 6) fprintf (bestand_patienten[tel[0]], " %d\t ", rand() % 10 + 1); //pre anesthesie tijd in minuten if (discipline == 1 || discipline == 2) fprintf (bestand_patienten[tel[0]], " %d\t ", rand() % 21 + 10); if (discipline == 3) fprintf (bestand_patienten[tel[0]], " %d\t ", rand() % 43 + 8); if (discipline == 4) fprintf (bestand_patienten[tel[0]], " %d\t ", rand() % 14 + 0); if (discipline == 5) fprintf (bestand_patienten[tel[0]], " %d\t ", rand() % 31 + 15); if (discipline == 6) fprintf (bestand_patienten[tel[0]], " %d\t ", rand() % 31 + 20); //operatie tijd in minuten (afhankelijk van discipl ine en major/minor karakter) if (major_minor == 1) { if (discipline == 1 || discipline == 2) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 240 + 121); if (discipline == 3) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 186 + 195); if (discipline == 5) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 181 + 195); if (discipline == 6) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 280 + 120); } if (major_minor == 2) { if (discipline == 1 || discipline == 2) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 91 + 30); if (discipline == 3) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 11 + 80); if (discipline == 4) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 76 + 10); if (discipline == 5) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 21 + 30); if (discipline == 6) fprintf (bestand_patienten[tel[0]], " % d\t", rand() % 11 + 45); } //post anesthesie tijd in minuten if (discipline == 1 || discipline==2 || discipline == 3 || discipline == 5) fprintf (bestand_patienten[tel[0]], " %d\ t", rand() % 6 + 5); if (discipline == 4) fprintf (bestand_patienten[tel[0]], " %d\ t", rand() % 20 + 1); if (discipline == 6) fprintf (bestand_patienten[tel[0]], " %d\ t", rand() % 26 + 5); // hospitalisatiedatum tov de optimale dag

72

tel[2] = rand() % 2 + 1; if (tel[2] == 1) fprintf (bestand_patienten[tel[0]], " %d \t", -(rand() % 8 + 3)); if (tel[2] == 2) fprintf (bestand_patienten[tel[0]], " %d \t", 100); // optimale dag fprintf (bestand_patienten[tel[0]], " %d\t", rand() % 5 + 1); // deadline tov de optimale dag fprintf (bestand_patienten[tel[0]], " %d\t", rand() % 3 + 1); // leeftijd fprintf (bestand_patienten[tel[0]], " %d\t", rand() % 82 + 18); //naam lengte_naam = rand() % 6 + 3; for (tel_naam = 1; tel_naam <= lengte_naam; tel_naam++ ) { nummer_letter_naam = rand() % 26 + 1; switch (nummer_letter_naam) { case 1 : fprintf(bestand_patienten[tel[0]], "a"); break; case 2 : fprintf(bestand_patienten[tel[0]], "b"); break; case 3 : fprintf(bestand_patienten[tel[0]], "c"); break; case 4 : fprintf(bestand_patienten[tel[0]], "d"); break; case 5 : fprintf(bestand_patienten[tel[0]], "e"); break; case 6 : fprintf(bestand_patienten[tel[0]], "f"); break; case 7 : fprintf(bestand_patienten[tel[0]], "g"); break; case 8 : fprintf(bestand_patienten[tel[0]], "h"); break; … break; case 18 : fprintf(bestand_patienten[tel[0]], "r"); break; case 19 : fprintf(bestand_patienten[tel[0]], "s"); break; case 20 : fprintf(bestand_patienten[tel[0]], "t"); break; case 21 : fprintf(bestand_patienten[tel[0]], "u"); break; case 22 : fprintf(bestand_patienten[tel[0]], "v"); break; case 23 : fprintf(bestand_patienten[tel[0]], "w"); break; case 24 : fprintf(bestand_patienten[tel[0]], "x"); break; case 25 : fprintf(bestand_patienten[tel[0]], "y"); break; case 26 : fprintf(bestand_patienten[tel[0]], "z"); break; default : break; } } fprintf(bestand_patienten[tel[0]], "\n"); } }

73

Datastructuur // definieren van patientenvariabelen in structuur struct OPERATIEDATA { int discipline; int major_minor; int tafel; int pre_operatie_tijd; int pre_anesthesie_tijd; int operatietijd; int post_anesthesie_tijd; int totale_tijd; int blok_random; int blok_heur[101]; int blok; }; struct ALLE_PATIENTENDATA { int nummer; char naam[50]; int leeftijd; int hospitalisatie; int optimale_dag; int deadline; struct OPERATIEDATA operatiedata; } patient[200][300]; // definieren van OK-variabelen in structuur struct OK { int nummer; int open; int sluit; int overtijd; } ok[15]; // definieren van blok-variabelen in structuur struct BLOK { int nummer; int ok; int dag; int duur; int aanvang; int discipline; int bezette_tijd; } blok[100]; // definieren van chirurg-variabelen in structuur struct CHIRURG { int nummer; int discipline; int beschikbaar; } chirurg[30]; // definieren van personeel-variabelen in structuur struct PERSONEEL { int type; int aantal; int beschikbaar; } personeel[7]; // definieren van tafel-variabelen in structuur struct TAFEL { int aantal; int bezette_tijd; int beschikbare_tijd; } tafel[5]; // definieren van major/minor-afhankelijke-variabel en in structuur struct MAJORMINOR {

74

int opsteltijd; int aantal_instrumentele_verpleegsters_nodig; int aantal_lopende_verpleegsters_nodig; int aantal_anesthesie_verpleegsters_nodig;

} majorminor[2];

75

Sancties berekenen void sancties_berekenen( void) { bezette_bloktijd_nagaan (); // alle sancties op 0 zetten for (tel[0] = 0; tel[0] < 15; tel[0]++) sanctie[tel[0]] = 0; // [1] Iedere patiënt krijgt minimum en maximum 1 s essie (sanctie 1 bis kan niet overschreden worden) for (tel[0] = 0; tel[0] < aantal_patienten[tel[30]]; t el[0]++) if (patient[tel[30]][tel[0]].operatiedata.blok < 0 || patient[tel[30]][tel[0]].operatiedata.blok > aantal _blokken - 1) // de omvang van sanctie 1 bepalen sanctie[1] = sanctie[1] + sanctie_bij[1]; // [2] Elke OK kan enkel gebruikt worden tijdens de toegestane openingstijden -> alleen de sluitingstijd van de ok dient gecontrolee rd te worden, aangezien een blok nooit begint eer zijn ok open is. for (tel[0] = 0; tel[0] < aantal_blokken; tel[0]++) { // het sluitingsuur van de juiste OK bekijken tel[1] = blok[tel[0]].ok; // nagaan of er sluitingstijd wordt overschreden tel[2] = blok[tel[0]].aanvang + blok[tel[0]]. bezette_tijd - ok[tel[1]].sluit; // indien er overtijd bestaat, de omvang van sancti e 3 aanpassen if (tel[2] > 0) sanctie[2] = sanctie[2] + tel[2] * sanctie _bij[2]; } // [3] Voor elke OK, mag het overwerk de beschikbar e tijd voor overwerk niet overschrijden for (tel[0] = 0; tel[0] < aantal_blokken; tel[0]++) { // het sluitingsuur van de juiste OK bekijken tel[1] = blok[tel[0]].ok; // nagaan of de overtijd wordt overschreden tel[2] = (blok[tel[0]].aanvang + blok[tel[0]] .bezette_tijd) - (ok[tel[1]].sluit + ok[tel[1]].overtijd); // indien er overtijd bestaat, de omvang van sancti e 3 aanpassen if (tel[2] > 0) sanctie[3] = sanctie[3] + tel[2] * sanctie _bij[3]; } // [4] Een blok kan niet voller zijn dan zijn duur for (tel[0] = 0; tel[0] < aantal_blokken; tel[0]++) { tel[1] = blok[tel[0]].bezette_tijd - blok[tel [0]].duur; if (tel[1] > 0) sanctie[4] = sanctie[4] + tel[1] * sanctie _bij[4]; } // [5] De duur van een periode waar de patiënt word t aan toegewezen, kan niet kleiner zijn dan de duur van de operatie for (tel[0] = 0; tel[0] < aantal_patienten[tel[30]]; t el[0]++) { tel[1] = patient[tel[30]][tel[0]].operatiedat a.blok; if (patient[tel[30]][tel[0]].operatiedata.totale_tijd > blok[tel[1]].duur) sanctie[5] = sanctie[5] + sanctie_bij[5]; }

76

// [6] Een patiënteninterventie kan niet gebeuren v oor of op zijn hospitalisatiedatum for (tel[0] = 0; tel[0] < aantal_patienten[tel[30]]; t el[0]++) { // alleen voor patienten met een hospitalisatiedatu m if (patient[tel[30]][tel[0]].hospitalisatie != 100) { // de dag van de geplande operatie nagaan tel[1] = patient[tel[30]][tel[0]].operati edata.blok; tel[1] = blok[tel[1]].dag; // de hospitaliesatiedatum nagaan tel[2] = patient[tel[30]][tel[0]].optimal e_dag + patient[tel[30]][tel[0]].hospitalisatie; // indien nodig de omvang van de sanctie aanpassen if (tel[1] <= tel[2]) sanctie[6] = sanctie[6] + sanctie_bij[ 6]; } } // [7] Een patiënteninterventie kan niet gebeuren n a zijn interventie deadline for (tel[0] = 0; tel[0] < aantal_patienten[tel[30]]; t el[0]++) { // de dag van de geplande operatie nagaan tel[1] = patient[tel[30]][tel[0]].operatieda ta.blok; tel[1] = blok[tel[1]].dag; // de deadline nagaan tel[2] = patient[tel[30]][tel[0]].optimale_d ag + patient[tel[30]][tel[0]].deadline; // indien nodig de omvang van de sanctie aanpassen if (tel[1] > tel[2]) sanctie[7] = sanctie[7] + sanctie_bij[7]; } // [8] Een patient moet gepland staan op zijn optim ale dag for (tel[0] = 0; tel[0] < aantal_patienten[tel[30]]; t el[0]++) { // het aantal dagen tussen de geplande operatie en de optimale dag nagaan tel[1] = patient[tel[30]][tel[0]].operatieda ta.blok; tel[2] = abs(blok[tel[1]].dag - patient[tel[ 30]][tel[0]].optimale_dag); // de sanctie aanpassen in functie van het verschil sanctie[8] = sanctie[8] + tel[2] * sanctie_b ij[8]; } // [9] Een tafel kan elke dag niet meer bezet worde n dan zijn (aantal * de beschikbare tijd) for (tel[0] = 0; tel[0] < 5; tel[0]++) { // voor elke dag de variabelen de beginwaarden toew ijzen tel[2] = 1500; tel[3] = 0; for (tel[7] = 0; tel[7] < 5; tel[7]++) tafel[tel[7]].bezette_tijd = 0; for (tel[1] = 0; tel[1] < aantal_blokken; tel[1]++) // voor elk blok dat op de juiste dag valt ... if (blok[tel[1]].dag == tel[0] + 1) { // de blokstarttijd en _sluitingstijd opslaan, indi en relevant if (blok[tel[1]].aanvang < tel[2]) tel[2] = blok[tel[1]].aanvang; if (blok[tel[1]].aanvang + blok[tel[1]].duur > tel[3] ) tel[3] = blok[tel[1]].aanvang + blo k[tel[1]].duur; // de eindtijd van de laatste operatie opslaan, ind ien relevant if (blok[tel[1]].aanvang + blok[tel[1]].bezette_tijd > tel[3]) tel[3] = blok[tel[1]].aanvang + blo k[tel[1]].bezette_tijd; // de bezette tijd per tafel per dag nagaan for (tel[4] = 0; tel[4] < aantal_patienten[tel[30]]; t el[4]++) // voor alle tafels

77

for (tel[5] = 0; tel[5] < 5; tel[5]++) // enkel patienten binnen het juiste blok en met de juiste tafel if (patient[tel[30]][tel[4]].operatiedata.blok == blok[tel[1]].nummer && patient[tel[30]][tel[4]].ope ratiedata.tafel == tel[5] + 1) tafel[tel[5]].bezette_tijd = tafel[tel[5]].bezette_tijd + patient[tel[30]][tel[4]].operatiedata.totale_tijd; } // voor elke tafel ... for (tel[6] = 0; tel[6] < 5; tel[6]++) { // de beschikbare tijd vaststellen tafel[tel[6]].beschikbare_tijd = (tel[3] - tel[2]) * tafel[tel[6]].aantal; // indien nodig de sanctie aanpassen (dagen zonder blok overslaan) if (tafel[tel[6]].bezette_tijd > tafel[tel[6]].beschi kbare_tijd && (tel[3] - tel[2]) != -1500) sanctie[9] = sanctie[9] + (tafel[tel[6 ]].bezette_tijd - tafel[tel[6]].beschikbare_tijd) * sanctie_bij[9]; } } // [10] Een chirurg kan niet langer opereren dan de tijd die hij beschikbaar is for (tel[0] = 1; tel[0] < 15; tel[0]++) tel[tel[0]] = 0; // beschikbare tijd per dag per discipline nagaan for (tel[0] = 0; tel[0] < aantal_chirurgen[0]; tel[0]+ +) { if (chirurg[tel[0]].discipline == 1) tel[1] = tel[1] + chirurg[tel[0]].beschikb aar; if (chirurg[tel[0]].discipline == 2) tel[2] = tel[2] + chirurg[tel[0]].beschikb aar; if (chirurg[tel[0]].discipline == 3) tel[3] = tel[3] + chirurg[tel[0]].beschikb aar; if (chirurg[tel[0]].discipline == 4) tel[4] = tel[4] + chirurg[tel[0]].beschikb aar; if (chirurg[tel[0]].discipline == 5) tel[5] = tel[5] + chirurg[tel[0]].beschikb aar; if (chirurg[tel[0]].discipline == 6) tel[6] = tel[6] + chirurg[tel[0]].beschikb aar; } // bezette tijd per dag, per discipline nagaan for (tel[10] = 0; tel[10] < 5; tel[10]++) for (tel[0] = 0; tel[0] < 6; tel[0]++) { tel[9] = 0; // alle patienten overlopen for (tel[7] = 0; tel[7] < aantal_patienten[tel[30]]; t el[7]++) { // dag van de patient nagaan tel[8] = patient[tel[30]][tel[7]].opera tiedata.blok; tel[8] = blok[tel[8]].dag; // als de patient op de juiste dag valt en de juist e discipline heeft... if (tel[8] == tel[10] + 1 && patient[tel[30]][tel[7]].operatiedata.discipline == tel[0] + 1) tel[9] = tel[9] + patient[tel[30]][t el[7]].operatiedata.operatietijd; } // indien nodig de sanctie aanpassen if (tel[9] > tel[tel[0] + 1]) sanctie[10] = sanctie[10] + (tel[9] - t el[tel[0] + 1]) * sanctie_bij[10]; }

78

// [11] Verpleegsters en verplegers kunnen niet lan ger assisteren dan hun beschikbare tijd hiervoor for (tel[0] = 0; tel[0] < 5; tel[0]++) { for (tel[1] = 2; tel[1] < 15; tel[1]++) tel[tel[1]] = 0; //voor elke dag de totale tijd aan major- en minor operaties nagaan for (tel[1] = 0; tel[1] < aantal_patienten[tel[30]]; t el[1]++) { // de operatiedag van de patient nagaan tel[10] = patient[tel[30]][tel[1]].operati edata.blok; tel[10] = blok[tel[10]].dag; if (tel[10] == tel[0] + 1 && patient[tel[30]][tel[1]] .operatiedata.major_minor == 1) tel[2] = tel[2] + patient[tel[30]][tel[ 1]].operatiedata.totale_tijd; if (tel[10] == tel[0] + 1 && patient[tel[30]][tel[1]] .operatiedata.major_minor == 2) tel[3] = tel[3] + patient[tel[30]][tel[ 1]].operatiedata.totale_tijd; } // de bezette tijd van elke type verpleegster nagaa n tel[4] = (tel[2] * majorminor[0].aantal_instr umentele_verpleegsters_nodig) + (tel[3] * majorminor[1].aantal_instrumentele_verple egsters_nodig); tel[5] = (tel[2] * majorminor[0].aantal_lopen de_verpleegsters_nodig) + (tel[3] * majorminor[1].aantal_lopende_verpleegsters_nodig); tel[6] = (tel[2] * majorminor[0].aantal_anest hesie_verpleegsters_nodig) + (tel[3] * majorminor[1].aantal_anesthesie_verpleegsters_nod ig); // de beschikbare tijd van elke type verpleegster n agaan tel[7] = personeel[0].aantal * personeel[0].b eschikbaar; tel[8] = personeel[1].aantal * personeel[1].b eschikbaar; tel[9] = personeel[2].aantal * personeel[2].b eschikbaar; // indien nodig de sanctie aanpassen if (tel[4] > tel[7]) sanctie[11] = sanctie[11] + (tel[4] - tel[ 7]) * sanctie_bij[11]; if (tel[5] > tel[8]) sanctie[11] = sanctie[11] + (tel[5] - tel[ 8]) * sanctie_bij[11]; if (tel[6] > tel[9]) sanctie[11] = sanctie[11] + (tel[6] - tel[ 9]) * sanctie_bij[11]; } // [12] Bij elke operatie moet een anesthesist aanw ezig zijn for (tel[0] = 0; tel[0] < 5; tel[0]++) { tel[2] = tel[3] = 0; // de bezette tijd per dag nagaan for (tel[1] = 0; tel[1] < aantal_patienten[tel[30]]; t el[1]++) { // de operatiedag van de patient nagaan tel[4] = patient[tel[30]][tel[1]].operatie data.blok; tel[4] = blok[tel[4]].dag; if (tel[4] == tel[0] + 1) tel[2] = tel[2] + patient[tel[30]][tel[ 1]].operatiedata.totale_tijd; } // de beschikbare tijd van de anesthesisten nagaan tel[3] = personeel[3].aantal * personeel[3].b eschikbaar; // indien nodig de sanctie aanpassen if (tel[2] > tel[3]) sanctie[12] = sanctie[12] + (tel[2] - tel[ 3]) * sanctie_bij[12]; }

79

// [13] De kuis- en logistieke ploeg kan mag niet m eer tijd toegewezen worden dan hun beschikbare tijd for (tel[0] = 0; tel[0] < 5; tel[0]++) { for (tel[1] = 2; tel[1] < 20; tel[1]++) tel[tel[1]] = 0; // het totaal aantal major- en minoroperaties per d ag nagaan for (tel[1] = 0; tel[1] < aantal_patienten[tel[30]]; t el[1]++) { // de operatiedag van de patient nagaan tel[7] = patient[tel[30]][tel[1]].operatie data.blok; tel[7] = blok[tel[7]].dag; if (tel[7] == tel[0] + 1 && patient[tel[30]][tel[1]]. operatiedata.major_minor == 1) tel[2] = tel[2] + 1; if (tel[7] == tel[0] + 1 && patient[tel[30]][tel[1]]. operatiedata.major_minor == 2) tel[3] = tel[3] + 1; } // de totale kuis- en opsteltijd per dag berekenen tel[2] = tel[2] * majorminor[0].opsteltijd; tel[3] = tel[3] * majorminor[1].opsteltijd; tel[4] = tel[2] + tel[3]; // de beschikbare tijd van de kuis- en logistieke p loegen nagaan tel[5] = personeel[4].aantal * personeel[4].b eschikbaar; tel[6] = personeel[5].aantal * personeel[5].b eschikbaar; // indien nodig de sanctie aanpassen if (tel[4] > tel[5]) sanctie[13] = sanctie[13] + (tel[4] - tel[ 5]) * sanctie_bij[13]; if (tel[4] > tel[6]) sanctie[13] = sanctie[13] + (tel[4] - tel[ 6]) * sanctie_bij[13]; } // [14] Een blok mag enkel gevuld zijn met operatie s die dezelfde discipline als dat blok hebben for (tel[0] = 0; tel[0] < aantal_patienten[tel[30]]; t el[0]++) for (tel[1] = 0; tel[1] < aantal_blokken; tel[1]++) // indien de patient tot een blok behoort met de ve rkeerde discipline, de sanctie aanpassen if (patient[tel[30]][tel[0]].operatiedata.blok == blo k[tel[1]].nummer && patient[tel[30]][tel[0]].operatiedata.discipline != blok[tel[1]].discipline) sanctie[14] = sanctie[14] + sanctie_bij [14];

80

// totale sanctie berekenen sanctie[0] = sanctie[1] + sanctie[2] + sanctie[3 ] + sanctie[4] + sanctie[5] + sanctie[6] + sanctie[7] + sanctie[8] + sanctie[9] + sanctie[10] + sanctie[11] + sanctie[12] + sanctie[13] +sanctie[14]; } //------------------------------------------------- ----------------------------- // De bezette tijden van alle blokken nagaan //------------------------------------------------- ----------------------------- void bezette_bloktijd_nagaan ( void) { // alle bezette tijden op 0 zetten for (tel[0] = 0; tel[0] < aantal_blokken; tel[0]++) blok[tel[0]].bezette_tijd = 0; // voor alle blokken ... for (tel[0] = 0; tel[0] < aantal_blokken; tel[0]++) // alle patienten controleren for (tel[1] = 0; tel[1] < aantal_patienten[tel[30]]; t el[1]++) // als de patient aan het juiste blok gelinkt is .. . if (patient[tel[30]][tel[1]].operatiedata.blok == tel [0]) { // de kuis- en opsteltijd bepalen if (patient[tel[30]][tel[1]].operatiedata.major_minor == 1) tel[4] = majorminor[0].opsteltijd; if (patient[tel[30]][tel[1]].operatiedata.major_minor == 2) tel[4] = majorminor[1].opsteltijd; // de bezette tijd van het blok aanpassen blok[tel[0]].bezette_tijd = blok[tel[0] ].bezette_tijd + patient[tel[30]][tel[1]].operatiedata.totale_tijd + tel[4]; } }

81

Initiële planningen genereren //------------------------------------------------- ----------------------------- // Random de initiele planning opstellen en de lij st aanmaken //------------------------------------------------- ----------------------------- tel[50] = GetTickCount(); printf ("\n===================================== =============\n\n"); printf ("De initiele planningen worden random aa ngemaakt... \n\n"); // balkje: resterende tijd printf ("................... voortgang ......... ...........\n"); // voor alle probleeminstanties for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // de starttijd bepalen lijstvar[tel[30]].tijd = GetTickCount(); // de referentiesanctie hoog zetten sanctie_ref = 2100100100; // 5000 iteraties for (tel[31] = 0; tel[31] < aantal_iteraties; tel[31]+ +) { for (tel[32] = 0; tel[32] < aantal_patienten[tel[30]]; tel[32]++) { tel[33] = rand () % aantal_blokken + 0; patient[tel[30]][tel[32]].operatiedata. blok = tel[33]; } sancties_berekenen (); // de beginsanctie opslaan if (tel[31] == 0) lijstvar[tel[30]].beginsanctie = sancti e[0]; // indien een beter schedule tot stand kwam: de ref erentiesanctie ... if (sanctie[0] < sanctie_ref) { sanctie_ref = sanctie[0]; // ... en de blok_random aanpassen for (tel[34] = 0; tel[34] < aantal_patienten[tel[30]]; tel[34]++) patient[tel[30]][tel[34]].operatieda ta.blok_random = patient[tel[30]][tel[34]].operatiedata.blok; } } lijstvar[tel[30]].iteraties = tel[31]; // de beste planning in de patient...blok variabele n steken for (tel[34] = 0; tel[34] < aantal_patienten[tel[30]]; tel[34]++) patient[tel[30]][tel[34]].operatiedata.blo k = patient[tel[30]][tel[34]].operatiedata.blok_random; // de sanctieafname bepalen sancties_berekenen (); lijstvar[tel[30]].afname = lijstvar[tel[30]]. beginsanctie - sanctie[0]; // de eindtijd en cpu-tijd bepalen lijstvar[tel[30]].tijd = GetTickCount() - lij stvar[tel[30]].tijd; } printf ("\n"); // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lijstvar[tel[0]].tijd; tel[2] = tel[2] + lijstvar[tel[0]].iteraties;

82

tel[3] = tel[3] + lijstvar[tel[0]].afname; tel[4] = tel[4] + lijstvar[tel[0]].beginsanct ie; } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; //------------------------------------------------- ----------------------------- // met initiele heuristiek de initiele planning op stellen en de lijst aanvullen //------------------------------------------------- ----------------------------- printf ("\n===================================== =============\n\n"); printf ("De initiele planningen worden via een v erbeteringsmethode aangemaakt... \n\n"); // balkje: resterende tijd printf ("................... voortgang ......... ...........\n"); // voor alle probleeminstanties for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // alle sancties_heur hoog zetten for (tel[0] = 0; tel[0] < aantal_planningen; tel[0]++) sanctie_heur[tel[30]][tel[0]] = 2100100100 ; // de referentiesanctie iets lager zetten sanctie_ref = 2100100000; // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // de starttijd bepalen lijstvar[tel[30]].tijd = GetTickCount(); // 5000 iteraties for (tel[31] = 0; tel[31] < aantal_iteraties; tel[31]+ +) { // de bezette tijden van de blokken op 0 zetten for (tel[38] = 0; tel[38] < aantal_blokken; tel[38]++) blok[tel[38]].bezette_tijd = 0; for (tel[32] = 0; tel[32] < aantal_patienten[tel[30]]; tel[32]++) { // de discipline van de patient nagaan tel[35] = patient[tel[30]][tel[32]].ope ratiedata.discipline; tel[37] = 0; // nagaan of er nog blokken in die discipline met b eschikbare tijd zijn for (tel[36] = 0; tel[36] < aantal_blokken; tel[36]++) if ((blok[tel[36]].discipline == tel[35]) && (blok[te l[36]].bezette_tijd < blok[tel[36]].duur)) { tel[37] = 1; break; } // random een blok toewijzen, tot aan een aantal vo orwaarden is voldaan do tel[33] = rand () % aantal_blokken + 0; while ((blok[tel[33]].discipline != patient[tel[30]][tel[32]].operatiedata.discipline) || ((blok[tel[33]].bezette_tijd >= blok[tel[33]].duur) && (tel[37] == 1))); // patient aan het blok toewijzen patient[tel[30]][tel[32]].operatiedata. blok = tel[33]; // de bezette tijd van het blok aanpassen if (patient[tel[30]][tel[32]].operatiedata.major_mino r == 1) blok[tel[33]].bezette_tijd = blok[te l[33]].bezette_tijd + patient[tel[30]][tel[32]].operatiedata.totale_tijd + majorminor[0].opsteltijd; if (patient[tel[30]][tel[32]].operatiedata.major_mino r == 2)

83

blok[tel[33]].bezette_tijd = blok[te l[33]].bezette_tijd + patient[tel[30]][tel[32]].operatiedata.totale_tijd + majorminor[1].opsteltijd; } sancties_berekenen (); // de beginsanctie opslaan if (tel[31] == 0) lijstvar[tel[30]].beginsanctie = sancti e[0]; // de slechtste opgeslagen planning zoeken sanctie_ref = (sanctie_heur[tel[30]][0] - 1); for (tel[0] = 0; tel[0] < aantal_planningen; tel[0]++) if (sanctie_ref < sanctie_heur[tel[30]][tel[0]]) { sanctie_ref = sanctie_heur[tel[30]][ tel[0]]; tel[39] = tel[0]; } // indien een beter schedule tot stand kwam ... if (sanctie[0] < sanctie_ref) { sanctie_heur[tel[30]][tel[39]] = sancti e[0]; // de blok_heur aanpassen for (tel[34] = 0; tel[34] < aantal_patienten[tel[30]]; tel[34]++) patient[tel[30]][tel[34]].operatieda ta.blok_heur[tel[39]] = patient[tel[30]][tel[34]].operatiedata.blok; } } lijstvar[tel[30]].iteraties = tel[31]; // de beste planning zoeken sanctie_ref = (sanctie_heur[tel[30]][0] + 1); for (tel[0] = 0; tel[0] < aantal_planningen; tel[0]++) if (sanctie_ref > sanctie_heur[tel[30]][tel[0]]) { sanctie_ref = sanctie_heur[tel[30]][tel [0]]; tel[39] = tel[0]; } // ze in de patient...blok variabelen steken for (tel[34] = 0; tel[34] < aantal_patienten[tel[30]]; tel[34]++) patient[tel[30]][tel[34]].operatiedata.blo k = patient[tel[30]][tel[34]].operatiedata.blok_heur[te l[39]]; // de sanctieafname bepalen lijstvar[tel[30]].afname = lijstvar[tel[30]]. beginsanctie - sanctie_heur[tel[30]][tel[39]]; // de eindtijd en cpu-tijd bepalen lijstvar[tel[30]].tijd = GetTickCount() - lij stvar[tel[30]].tijd; } printf ("\n"); // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lijstvar[tel[0]].tijd; tel[2] = tel[2] + lijstvar[tel[0]].iteraties; tel[3] = tel[3] + lijstvar[tel[0]].afname; tel[4] = tel[4] + lijstvar[tel[0]].beginsanct ie; } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties;

84

Lokale zoekmethoden //------------------------------------------------- ----------------------------- // lokale functies //------------------------------------------------- ----------------------------- // vernieuw de blok_lokaal variabelen void prep_lokaal ( void) { // de sancties van de beginplanningen nagaan en de janee variabelen op 0 zetten for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) for (tel[32] = 0; tel[32] < aantal_planningen; tel[32] ++) { for (tel[31] = 0; tel[31] < aantal_patienten[tel[30]]; tel[31]++) patient[tel[30]][tel[31]].operatiedata. blok = patient[tel[30]][tel[31]].operatiedata.blok_heur[te l[32]]; sancties_berekenen (); sanctie_lokaal[tel[30]][tel[32]] = sanctie [0]; janee[tel[30]][tel[32]] = 0; } // de eindblokken in de lokale beginblok variabelen steken: de laagste sancties eerst for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[35] = 0; for (tel[32] = 0; tel[32] < aantal_planningen; tel[32] ++) { // de referentiesanctie hoog zetten tel[34] = 2100100100; // de laagste sanctie zoeken for (tel[33] = 0; tel[33] < aantal_planningen; tel[33] ++) if ((sanctie_lokaal[tel[30]][tel[33]] <= tel[34]) && (janee[tel[30]][tel[33]] == 0)) tel[34] = sanctie_lokaal[tel[30]][te l[33]]; // de planning met de laagste sanctie eerst in vari abelen steken for (tel[33] = 0; tel[33] < aantal_planningen; tel[33] ++) if ((sanctie_lokaal[tel[30]][tel[33]] == tel[34]) && (janee[tel[30]][tel[33]] == 0)) { for (tel[31] = 0; tel[31] < aantal_patienten[tel[30]]; tel[31]++) patient[tel[30]][tel[31]].operati edata.blok_lokaal[tel[35]] = patient[tel[30]][tel[31]].operatiedata.blok_heur[te l[33]]; janee[tel[30]][tel[33]] = 1; tel[35]++; break; } } } } // zoek random 2 patienten met dezelfde discipline en een verschillend blok en verwissel ze void lokaal_0 ( void) { // balkje: resterende tijd printf ("\n.................... lokaal 0 ....... .............\n"); // tel[39] wordt gebruikt om het aantal behandelde populatie elementen bij te houden tel[39] = 0; for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) {

85

// variabelen op 0 zetten lokaal[tel[30]][0].afname = lokaal[tel[30]][0 ].beginsanctie = lokaal[tel[30]][0].iteraties = lokaal[tel[30]][0].t ijd = tel[31] = tel[34] = tel[35] = 0; lokaal[tel[30]][0].tijd = GetTickCount(); // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // 5000 iteraties for (tel[36] = 0; tel[36] < aantal_iteraties; tel[36]+ +) { // beginsancties nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); tel[38] = sanctie[0]; // enkel de beginsanctie aanpassen bij een nieuw po pulatie element if (tel[35] == 0) { lokaal[tel[30]][0].beginsanctie = lokaa l[tel[30]][0].beginsanctie + sanctie[0]; tel[35] = tel[48] = 123; } // eerste patient zoeken tel[32] = rand () % aantal_patienten[tel[3 0]] + 0; // tweede patient zoeken do tel[33] = rand () % aantal_patienten[te l[30]] + 0; while ((patient[tel[30]][tel[32]].operatiedata.disciplin e != patient[tel[30]][tel[33]].operatiedata.discipline) || (patient[tel[30]][tel[32]].operatiedata.blok_lokaal [tel[31]] == patient[tel[30]][tel[33]].operatiedata.blok_lokaal[ tel[31]])); // de patienten verwisselen tel[40] = patient[tel[30]][tel[32]].operat iedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[32]].operatiedata.blo k_lokaal[tel[31]] = patient[tel[30]][tel[33]].operatiedata.blok_lokaal[ tel[31]]; patient[tel[30]][tel[33]].operatiedata.blo k_lokaal[tel[31]] = tel[40]; // de nieuwe sancties berekenen en eventueel de wij ziging ongedaan maken for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); if (sanctie[0] >= tel[38]) { tel[40] = patient[tel[30]][tel[32]].ope ratiedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[32]].operatiedata. blok_lokaal[tel[31]] = patient[tel[30]][tel[33]].operatiedata.blok_lokaal[ tel[31]]; patient[tel[30]][tel[33]].operatiedata. blok_lokaal[tel[31]] = tel[40]; tel[34]++; } else tel[34] = 0; // na een aantal iteraties zonder verbetering overg aan naar het volgende element if (tel[34] >= max_nul_iteraties) { // de eindsancties van het populatie element nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatieda ta.blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][0].afname = lokaal[tel[ 30]][0].afname + sanctie[0];

86

// volgende planning tel[31]++; tel[34] = tel[35] = tel[48] = 0; } // indien de 30 elementen zijn behandeld en we nog niet voldoende iteraties hebben if (tel[31] == aantal_planningen) break; } // de sanctieafname van het laatste element ook in rekening brengen if (tel[48] == 123) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][0].afname = lokaal[tel[30] ][0].afname + sanctie[0]; } // het correcte aantal bewerkte populatie elementen in rekening brengen if (tel[31] < aantal_planningen) tel[31]++; tel[39] = tel[39] + tel[31]; // lijstwaarden aanpassen lokaal[tel[30]][0].beginsanctie = lokaal[tel[ 30]][0].beginsanctie / tel[31]; lokaal[tel[30]][0].afname = lokaal[tel[30]][0 ].beginsanctie - (lokaal[tel[30]][0].afname / tel[31]); lokaal[tel[30]][0].tijd = GetTickCount() - lo kaal[tel[30]][0].tijd; lokaal[tel[30]][0].iteraties = tel[36]; } printf ("\nEr werd(en) gemiddeld %d populatie el ement(en) behandeld.\n\n", (tel[39] / aantal_probleeminstanties)); // voor elke probleeminstantie: de beste planning z oeken for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[31] = 2100100100; for (tel[36] = 0; tel[36] < aantal_planningen; tel[36] ++) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[36]]; sancties_berekenen (); if (tel[31] > sanctie[0]) { tel[31] = sanctie[0]; // onthouden welke planning de beste is tel[34] = tel[36]; } } for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata.ein d_lokaal[0] = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[34]]; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lokaal[tel[0]][0].tijd; tel[2] = tel[2] + lokaal[tel[0]][0].iteraties ; tel[3] = tel[3] + lokaal[tel[0]][0].afname; tel[4] = tel[4] + lokaal[tel[0]][0].beginsanc tie; } tel[1] = tel[1] / aantal_probleeminstanties;

87

tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; // de lijst aanvullen fprintf (lijst, "| lokaal 0\t\t| %d\t\t%d\t\t%d\ t\t%d\t|\n", tel[1], tel[2], tel[3], tel[4]); } // zoek random 1 patient en steek die in een ander blok met de juiste discipline void lokaal_1 ( void) { // balkje: resterende tijd printf ("\n.................... lokaal 1 ....... .............\n"); // tel[39] wordt gebruikt om het aantal behandelde populatie elementen bij te houden tel[39] = 0; for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // variabelen op 0 zetten lokaal[tel[30]][1].afname = lokaal[tel[30]][1 ].beginsanctie = lokaal[tel[30]][1].iteraties = lokaal[tel[30]][1].t ijd = tel[31] = tel[34] = tel[35] = 0; lokaal[tel[30]][1].tijd = GetTickCount(); // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // 5000 iteraties for (tel[36] = 0; tel[36] < aantal_iteraties; tel[36]+ +) { // beginsancties nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); tel[38] = sanctie[0]; // enkel de beginsanctie aanpassen bij een nieuw po pulatie element if (tel[35] == 0) { lokaal[tel[30]][1].beginsanctie = lokaa l[tel[30]][1].beginsanctie + sanctie[0]; tel[35] = tel[48] = 123; } // eerste patient zoeken tel[32] = rand () % aantal_patienten[tel[3 0]] + 0; // een nieuw blok zoeken do tel[33] = rand () % aantal_blokken + 0; while ((patient[tel[30]][tel[32]].operatiedata.blok_loka al[tel[31]] == tel[33]) || (patient[tel[30]][tel[32]].operatiedata .discipline != blok[tel[33]].discipline)); // de patienten in het nieuwe blok steken tel[40] = patient[tel[30]][tel[32]].operat iedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[32]].operatiedata.blo k_lokaal[tel[31]] = tel[33]; // de nieuwe sancties berekenen en eventueel de wij ziging ongedaan maken for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); if (sanctie[0] >= tel[38]) { patient[tel[30]][tel[32]].operatiedata. blok_lokaal[tel[31]] = tel[40]; tel[34]++; } else

88

tel[34] = 0; // na een aantal iteraties zonder verbetering overg aan naar het volgende element if (tel[34] >= max_nul_iteraties) { // de eindsancties van het populatie element nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatieda ta.blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][1].afname = lokaal[tel[ 30]][1].afname + sanctie[0]; // volgende planning tel[31]++; tel[34] = tel[35] = tel[48] = 0; } // indien de 30 elementen zijn behandeld en we nog niet voldoende iteraties hebben if (tel[31] == aantal_planningen) break; } // de sanctieafname van het laatste element ook in rekening brengen if (tel[48] == 123) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][1].afname = lokaal[tel[30] ][1].afname + sanctie[0]; } // het correcte aantal bewerkte populatie elementen in rekening brengen if (tel[31] < aantal_planningen) tel[31]++; tel[39] = tel[39] + tel[31]; // lijstwaarden aanpassen lokaal[tel[30]][1].beginsanctie = lokaal[tel[ 30]][1].beginsanctie / tel[31]; lokaal[tel[30]][1].afname = lokaal[tel[30]][1 ].beginsanctie - (lokaal[tel[30]][1].afname / tel[31]); lokaal[tel[30]][1].tijd = GetTickCount() - lo kaal[tel[30]][1].tijd; lokaal[tel[30]][1].iteraties = tel[36]; } printf ("\nEr werd(en) gemiddeld %d populatie el ement(en) behandeld.\n\n", (tel[39] / aantal_probleeminstanties)); // voor elke probleeminstantie: de beste planning z oeken for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[31] = 2100100100; for (tel[36] = 0; tel[36] < aantal_planningen; tel[36] ++) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[36]]; sancties_berekenen (); if (tel[31] > sanctie[0]) { tel[31] = sanctie[0]; // onthouden welke planning de beste is tel[34] = tel[36]; } } for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata.ein d_lokaal[1] = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[34]]; }

89

// tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lokaal[tel[0]][1].tijd; tel[2] = tel[2] + lokaal[tel[0]][1].iteraties ; tel[3] = tel[3] + lokaal[tel[0]][1].afname; tel[4] = tel[4] + lokaal[tel[0]][1].beginsanc tie; } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; // de lijst aanvullen fprintf (lijst, "| lokaal 1\t\t| %d\t\t%d\t\t%d\ t\t%d\t|\n", tel[1], tel[2], tel[3], tel[4]); } // zoek een blok dat overvol zit en steek een patie nt uit dat blok in een ander blok met de juiste discipline void lokaal_2 ( void) { // balkje: resterende tijd printf ("\n.................... lokaal 2 ....... .............\n"); // tel[39] wordt gebruikt om het aantal behandelde populatie elementen bij te houden tel[39] = 0; for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // variabelen op 0 zetten lokaal[tel[30]][2].afname = lokaal[tel[30]][2 ].beginsanctie = lokaal[tel[30]][2].iteraties = lokaal[tel[30]][2].t ijd = tel[31] = tel[34] = tel[35] = 0; lokaal[tel[30]][2].tijd = GetTickCount(); // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // 5000 iteraties for (tel[36] = 0; tel[36] < aantal_iteraties; tel[36]+ +) { // beginsancties nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); tel[38] = sanctie[0]; // enkel de beginsanctie aanpassen bij een nieuw po pulatie element if (tel[35] == 0) { lokaal[tel[30]][2].beginsanctie = lokaa l[tel[30]][2].beginsanctie + sanctie[0]; tel[35] = tel[48] = 123; } // indien er geen blok-sancties zijn, naar een volg end populatie element gaan if (sanctie[4] == 0) { tel[34] = max_nul_iteraties; goto einde_lokaal2; } // overvol blok zoeken do

90

{ tel[32] = rand () % aantal_blokken + 0; } while (blok[tel[32]].bezette_tijd <= blok[tel[32]].duur) ; // een patient binnen dat blok zoeken do tel[42] = rand () % aantal_patienten[te l[30]] + 0; while (patient[tel[30]][tel[42]].operatiedata.blok_lokaa l[tel[31]] != blok[tel[32]].nummer); // een nieuw blok zoeken tel[41] = 0; do { tel[43] = rand () % aantal_blokken + 0; tel[41]++; } while (((tel[43] == tel[32]) || (blok[tel[43]].bezette_t ijd >= blok[tel[43]].duur) || (patient[tel[30]][tel[42]].o peratiedata.discipline != blok[tel[43]].discipline)) && (tel[41] < (aantal_bl okken * 4))); //indien geen nieuwe blokken te vinden zijn die aan alle criteria voldoen: de patient in een ander gelijkaardig blok steken if (tel[41] >= (aantal_blokken * 4)) { do { tel[43] = rand () % aantal_blokken + 0; } while ((tel[43] == tel[32]) || (patient[tel[30]][tel[42]].operatiedata.discipline != blok[tel[43]].discipline)); } // de patienten in het nieuwe blok steken tel[40] = patient[tel[30]][tel[42]].operat iedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[42]].operatiedata.blo k_lokaal[tel[31]] = tel[43]; // de nieuwe sancties berekenen en eventueel de wij ziging ongedaan maken for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); if (sanctie[0] >= tel[38]) { patient[tel[30]][tel[42]].operatiedata. blok_lokaal[tel[31]] = tel[40]; tel[34]++; } else tel[34] = 0; einde_lokaal2: // na een aantal iteraties zonder verbetering overg aan naar het volgende element if (tel[34] >= max_nul_iteraties) { // de eindsancties van het populatie element nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatieda ta.blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][2].afname = lokaal[tel[ 30]][2].afname + sanctie[0]; // volgende planning tel[31]++; tel[34] = tel[35] = tel[48] = 0; } // indien de 30 elementen zijn behandeld en we nog niet voldoende iteraties hebben if (tel[31] == aantal_planningen) break; }

91

// de sanctieafname van het laatste element ook in rekening brengen if (tel[48] == 123) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][2].afname = lokaal[tel[30] ][2].afname + sanctie[0]; } // het correcte aantal bewerkte populatie elementen in rekening brengen if (tel[31] < aantal_planningen) tel[31]++; tel[39] = tel[39] + tel[31]; // lijstwaarden aanpassen lokaal[tel[30]][2].beginsanctie = lokaal[tel[ 30]][2].beginsanctie / tel[31]; lokaal[tel[30]][2].afname = lokaal[tel[30]][2 ].beginsanctie - (lokaal[tel[30]][2].afname / tel[31]); lokaal[tel[30]][2].tijd = GetTickCount() - lo kaal[tel[30]][2].tijd; lokaal[tel[30]][2].iteraties = tel[36]; } printf ("\nEr werd(en) gemiddeld %d populatie el ement(en) behandeld.\n\n", (tel[39] / aantal_probleeminstanties)); // voor elke probleeminstantie: de beste planning z oeken for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[31] = 2100100100; for (tel[36] = 0; tel[36] < aantal_planningen; tel[36] ++) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[36]]; sancties_berekenen (); if (tel[31] > sanctie[0]) { tel[31] = sanctie[0]; // onthouden welke planning de beste is tel[34] = tel[36]; } } for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata.ein d_lokaal[2] = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[34]]; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lokaal[tel[0]][2].tijd; tel[2] = tel[2] + lokaal[tel[0]][2].iteraties ; tel[3] = tel[3] + lokaal[tel[0]][2].afname; tel[4] = tel[4] + lokaal[tel[0]][2].beginsanc tie; } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; // de lijst aanvullen fprintf (lijst, "| lokaal 2\t\t| %d\t\t%d\t\t%d\ t\t%d\t|\n", tel[1], tel[2], tel[3], tel[4]); }

92

// zoek een patient die niet op zijn optimale dag g epland staat en plaats hem dichter bij die dag void lokaal_3 ( void) { // balkje: resterende tijd printf ("\n.................... lokaal 3 ....... .............\n"); // tel[39] wordt gebruikt om het aantal behandelde populatie elementen bij te houden tel[39] = 0; for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // variabelen op 0 zetten lokaal[tel[30]][3].afname = lokaal[tel[30]][3 ].beginsanctie = lokaal[tel[30]][3].iteraties = lokaal[tel[30]][3].t ijd = tel[31] = tel[34] = tel[35] = 0; lokaal[tel[30]][3].tijd = GetTickCount(); // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // 5000 iteraties for (tel[36] = 0; tel[36] < aantal_iteraties; tel[36]+ +) { // beginsancties nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); tel[38] = sanctie[0]; // enkel de beginsanctie aanpassen bij een nieuw po pulatie element if (tel[35] == 0) { lokaal[tel[30]][3].beginsanctie = lokaa l[tel[30]][3].beginsanctie + sanctie[0]; tel[35] = tel[48] = 123; } // indien er geen optimale dag-sancties zijn, naar een volgend populatie element gaan if (sanctie[8] == 0) { tel[34] = max_nul_iteraties; goto einde_lokaal3; } // een patient zoeken die niet op zijn optimale dag gepland staat do { tel[32] = rand () % aantal_patienten[te l[30]] + 0; tel[44] = patient[tel[30]][tel[32]].ope ratiedata.blok_lokaal[tel[31]]; tel[44] = blok[tel[44]].dag; } while (patient[tel[30]][tel[32]].optimale_dag == tel[44] ); // een nieuw blok zoeken do tel[43] = rand () % aantal_blokken + 0; while ((abs(patient[tel[30]][tel[32]].optimale_dag - blo k[tel[43]].dag) >= abs(patient[tel[30]][tel[32]].optimale_dag - tel[44 ])) || (blok[tel[43]].discipline != patient[tel[30]][tel[32]].operatiedata.discipline)) ; // de patienten in het nieuwe blok steken tel[40] = patient[tel[30]][tel[32]].operat iedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[32]].operatiedata.blo k_lokaal[tel[31]] = tel[43]; // de nieuwe sancties berekenen en eventueel de wij ziging ongedaan maken for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); if (sanctie[0] >= tel[38])

93

{ patient[tel[30]][tel[32]].operatiedata. blok_lokaal[tel[31]] = tel[40]; tel[34]++; } else tel[34] = 0; einde_lokaal3: // na een aantal iteraties zonder verbetering overg aan naar het volgende element if (tel[34] >= max_nul_iteraties) { // de eindsancties van het populatie element nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatieda ta.blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][3].afname = lokaal[tel[ 30]][3].afname + sanctie[0]; // volgende planning tel[31]++; tel[34] = tel[35] = tel[48] = 0; } // indien de 30 elementen zijn behandeld en we nog niet voldoende iteraties hebben if (tel[31] == aantal_planningen) break; } // de sanctieafname van het laatste element ook in rekening brengen if (tel[48] == 123) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][3].afname = lokaal[tel[30] ][3].afname + sanctie[0]; } // het correcte aantal bewerkte populatie elementen in rekening brengen if (tel[31] < aantal_planningen) tel[31]++; tel[39] = tel[39] + tel[31]; // lijstwaarden aanpassen lokaal[tel[30]][3].beginsanctie = lokaal[tel[ 30]][3].beginsanctie / tel[31]; lokaal[tel[30]][3].afname = lokaal[tel[30]][3 ].beginsanctie - (lokaal[tel[30]][3].afname / tel[31]); lokaal[tel[30]][3].tijd = GetTickCount() - lo kaal[tel[30]][3].tijd; lokaal[tel[30]][3].iteraties = tel[36]; } printf ("\nEr werd(en) gemiddeld %d populatie el ement(en) behandeld.\n\n", (tel[39] / aantal_probleeminstanties)); // voor elke probleeminstantie: de beste planning z oeken for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[31] = 2100100100; for (tel[36] = 0; tel[36] < aantal_planningen; tel[36] ++) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[36]]; sancties_berekenen (); if (tel[31] > sanctie[0]) { tel[31] = sanctie[0]; // onthouden welke planning de beste is tel[34] = tel[36];

94

} } for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata.ein d_lokaal[3] = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[34]]; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lokaal[tel[0]][3].tijd; tel[2] = tel[2] + lokaal[tel[0]][3].iteraties ; tel[3] = tel[3] + lokaal[tel[0]][3].afname; tel[4] = tel[4] + lokaal[tel[0]][3].beginsanc tie; } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; // de lijst aanvullen fprintf (lijst, "| lokaal 3\t\t| %d\t\t%d\t\t%d\ t\t%d\t|\n", tel[1], tel[2], tel[3], tel[4]); } // zoek een dag en een tafel die voor sancties zorg en (of kleinste buffer hebben) en verwissel een relevante patient met een patient met een andere dag, een kortere totale duur en eenzelfde discipline void lokaal_4 ( void) { // balkje: resterende tijd printf ("\n.................... lokaal 4 ....... .............\n"); // tel[39] wordt gebruikt om het aantal behandelde populatie elementen bij te houden tel[39] = 0; for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // variabelen op 0 zetten lokaal[tel[30]][4].afname = lokaal[tel[30]][4 ].beginsanctie = lokaal[tel[30]][4].iteraties = lokaal[tel[30]][4].t ijd = tel[31] = tel[34] = tel[35] = 0; lokaal[tel[30]][4].tijd = GetTickCount(); // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // 5000 iteraties for (tel[36] = 0; tel[36] < aantal_iteraties; tel[36]+ +) { // beginsancties nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); tel[38] = sanctie[0]; // enkel de beginsanctie aanpassen bij een nieuw po pulatie element if (tel[35] == 0) { lokaal[tel[30]][4].beginsanctie = lokaa l[tel[30]][4].beginsanctie + sanctie[0]; tel[35] = tel[48] = 123; } // indien er geen tafelsancties zijn, de tafel en d ag met de kleinste buffer kiezen

95

if (sanctie[9] == 0) tafel[tafel_dag_buffer[0]].dag[tafel_da g_buffer[1]] = 1; // een tafel en dag zoeken die sancties opleveren o f de kleinste buffer hebben do { tel[32] = rand () % 5 + 1; tel[44] = rand () % 5 + 1; } while (tafel[tel[32]-1].dag[tel[44]-1] == 0); // een relevante patient zoeken do { tel[43] = rand () % aantal_patienten[te l[30]] + 0; // de dag van de gekozen patient nagaan tel[45] = patient[tel[30]][tel[43]].ope ratiedata.blok_lokaal[tel[31]]; tel[45] = blok[tel[45]].dag; } while ((patient[tel[30]][tel[43]].operatiedata.tafel != tel[32]) || (tel[45] != tel[44])); // een tweede patient zoeken met eenzelfde discipli ne, een verschillende dag en een kortere totale tijd tel[41] = 0; do { tel[46] = rand () % aantal_patienten[te l[30]] + 0; // de dag van de gekozen patient nagaan tel[47] = patient[tel[30]][tel[46]].ope ratiedata.blok_lokaal[tel[31]]; tel[47] = blok[tel[47]].dag; tel[41]++; } while (((patient[tel[30]][tel[43]].operatiedata.discipli ne != patient[tel[30]][tel[46]].operatiedata.discipline) || (patient[tel[30]][tel[43]].operatiedata.totale_tijd <= patient[tel[30]][tel[46]].operatiedata.totale_tijd) || (tel[45] == tel[47])) && (tel[41] < (aantal_patienten[tel[30]] * 4))); // indien geen tweede patient gevonden kan worden if (tel[41] >= (aantal_patienten[tel[30]] * 4)) { tel[34]++; goto einde_lokaal4; } // de patienten omwisselen tel[40] = patient[tel[30]][tel[43]].operat iedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[43]].operatiedata.blo k_lokaal[tel[31]] = patient[tel[30]][tel[46]].operatiedata.blok_lokaal[ tel[31]]; patient[tel[30]][tel[46]].operatiedata.blo k_lokaal[tel[31]] = tel[40]; // de nieuwe sancties berekenen en eventueel de wij ziging ongedaan maken for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); if (sanctie[0] >= tel[38]) { tel[40] = patient[tel[30]][tel[43]].ope ratiedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[43]].operatiedata. blok_lokaal[tel[31]] = patient[tel[30]][tel[46]].operatiedata.blok_lokaal[ tel[31]]; patient[tel[30]][tel[46]].operatiedata. blok_lokaal[tel[31]] = tel[40]; tel[34]++; } else tel[34] = 0; einde_lokaal4: // na een aantal iteraties zonder verbetering overg aan naar het volgende element if (tel[34] >= max_nul_iteraties) { // de eindsancties van het populatie element nagaan

96

for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatieda ta.blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][4].afname = lokaal[tel[ 30]][4].afname + sanctie[0]; // volgende planning tel[31]++; tel[34] = tel[35] = tel[48] = 0; } // indien de 30 elementen zijn behandeld en we nog niet voldoende iteraties hebben if (tel[31] == aantal_planningen) break; } // de sanctieafname van het laatste element ook in rekening brengen if (tel[48] == 123) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][4].afname = lokaal[tel[30] ][4].afname + sanctie[0]; } // het correcte aantal bewerkte populatie elementen in rekening brengen if (tel[31] < aantal_planningen) tel[31]++; tel[39] = tel[39] + tel[31]; // lijstwaarden aanpassen lokaal[tel[30]][4].beginsanctie = lokaal[tel[ 30]][4].beginsanctie / tel[31]; lokaal[tel[30]][4].afname = lokaal[tel[30]][4 ].beginsanctie - (lokaal[tel[30]][4].afname / tel[31]); lokaal[tel[30]][4].tijd = GetTickCount() - lo kaal[tel[30]][4].tijd; lokaal[tel[30]][4].iteraties = tel[36]; } printf ("\nEr werd(en) gemiddeld %d populatie el ement(en) behandeld.\n\n", (tel[39] / aantal_probleeminstanties)); // voor elke probleeminstantie: de beste planning z oeken for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[31] = 2100100100; for (tel[36] = 0; tel[36] < aantal_planningen; tel[36] ++) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[36]]; sancties_berekenen (); if (tel[31] > sanctie[0]) { tel[31] = sanctie[0]; // onthouden welke planning de beste is tel[34] = tel[36]; } } for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata.ein d_lokaal[4] = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[34]]; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++)

97

{ tel[1] = tel[1] + lokaal[tel[0]][4].tijd; tel[2] = tel[2] + lokaal[tel[0]][4].iteraties ; tel[3] = tel[3] + lokaal[tel[0]][4].afname; tel[4] = tel[4] + lokaal[tel[0]][4].beginsanc tie; } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; // de lijst aanvullen fprintf (lijst, "| lokaal 4\t\t| %d\t\t%d\t\t%d\ t\t%d\t|\n", tel[1], tel[2], tel[3], tel[4]); } // zoek een discipline en een dag die voor sancties zorgen door overbezette chirurgen (of zoek een discipline en dag met de kleinste buff er) en verwissel een relevante patient met een patient met een andere dag, een kor tere operatieduur en eenzelfde discipline void lokaal_5 ( void) { // balkje: resterende tijd printf ("\n.................... lokaal 5 ....... .............\n"); // tel[39] wordt gebruikt om het aantal behandelde populatie elementen bij te houden tel[39] = 0; for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // variabelen op 0 zetten lokaal[tel[30]][5].afname = lokaal[tel[30]][5 ].beginsanctie = lokaal[tel[30]][5].iteraties = lokaal[tel[30]][5].t ijd = tel[31] = tel[34] = tel[35] = 0; lokaal[tel[30]][5].tijd = GetTickCount(); // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // 5000 iteraties for (tel[36] = 0; tel[36] < aantal_iteraties; tel[36]+ +) { // beginsancties nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); tel[38] = sanctie[0]; // enkel de beginsanctie aanpassen bij een nieuw po pulatie element if (tel[35] == 0) { lokaal[tel[30]][5].beginsanctie = lokaa l[tel[30]][5].beginsanctie + sanctie[0]; tel[35] = tel[48] = 123; } // indien er geen chirurgdisc-sancties zijn, de dis cipline en dag met de kleinste buffer kiezen if (sanctie[10] == 0) chirurgdisc_dag[chirurgdisc_dag_buffer[ 0]][chirurgdisc_dag_buffer[1]] = 1; // een discipline en dag zoeken die sancties opleve ren do { tel[32] = rand () % 6 + 1; tel[44] = rand () % 5 + 1; } while ((chirurgdisc_dag[tel[32]-1][tel[44]-1] == 0)); // een relevante patient zoeken do

98

{ tel[43] = rand () % aantal_patienten[te l[30]] + 0; // de dag van de gekozen patient nagaan tel[45] = patient[tel[30]][tel[43]].ope ratiedata.blok_lokaal[tel[31]]; tel[45] = blok[tel[45]].dag; } while ((patient[tel[30]][tel[43]].operatiedata.disciplin e != tel[32]) || (tel[45] != tel[44])); // een tweede patient zoeken met eenzelfde discipli ne, een verschillende dag en een kortere operatietijd tel[41] = 0; do { tel[46] = rand () % aantal_patienten[te l[30]] + 0; // de dag van de gekozen patient nagaan tel[47] = patient[tel[30]][tel[46]].ope ratiedata.blok_lokaal[tel[31]]; tel[47] = blok[tel[47]].dag; tel[41]++; } while (((patient[tel[30]][tel[43]].operatiedata.discipli ne != patient[tel[30]][tel[46]].operatiedata.discipline) || (patient[tel[30]][tel[43]].operatiedata.operatietij d <= patient[tel[30]][tel[46]].operatiedata.operatietijd ) || (tel[45] == tel[47])) && (tel[41] < (aantal_patienten[tel[30]] * 4))); // indien geen tweede patient gevonden kan worden e en nuliteratie in rekening nemen en opnieuw proberen met hetzelfde populatie e lement if (tel[41] >= (aantal_patienten[tel[30]] * 4)) { tel[34]++; goto einde_lokaal5; } // de patienten omwisselen tel[40] = patient[tel[30]][tel[43]].operat iedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[43]].operatiedata.blo k_lokaal[tel[31]] = patient[tel[30]][tel[46]].operatiedata.blok_lokaal[ tel[31]]; patient[tel[30]][tel[46]].operatiedata.blo k_lokaal[tel[31]] = tel[40]; // de nieuwe sancties berekenen en eventueel de wij ziging ongedaan maken for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); if (sanctie[0] >= tel[38]) { tel[40] = patient[tel[30]][tel[43]].ope ratiedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[43]].operatiedata. blok_lokaal[tel[31]] = patient[tel[30]][tel[46]].operatiedata.blok_lokaal[ tel[31]]; patient[tel[30]][tel[46]].operatiedata. blok_lokaal[tel[31]] = tel[40]; tel[34]++; } else tel[34] = 0; einde_lokaal5: // na een aantal iteraties zonder verbetering overg aan naar het volgende element if (tel[34] >= max_nul_iteraties) { // de eindsancties van het populatie element nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatieda ta.blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][5].afname = lokaal[tel[ 30]][5].afname + sanctie[0]; // volgende planning tel[31]++;

99

tel[34] = tel[35] = tel[48] = 0; } // indien de 30 elementen zijn behandeld en we nog niet voldoende iteraties hebben if (tel[31] == aantal_planningen) break; } // de sanctieafname van het laatste element ook in rekening brengen if (tel[48] == 123) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][5].afname = lokaal[tel[30] ][5].afname + sanctie[0]; } // het correcte aantal bewerkte populatie elementen in rekening brengen if (tel[31] < aantal_planningen) tel[31]++; tel[39] = tel[39] + tel[31]; // lijstwaarden aanpassen lokaal[tel[30]][5].beginsanctie = lokaal[tel[ 30]][5].beginsanctie / tel[31]; lokaal[tel[30]][5].afname = lokaal[tel[30]][5 ].beginsanctie - (lokaal[tel[30]][5].afname / tel[31]); lokaal[tel[30]][5].tijd = GetTickCount() - lo kaal[tel[30]][5].tijd; lokaal[tel[30]][5].iteraties = tel[36]; } printf ("\nEr werd(en) gemiddeld %d populatie el ement(en) behandeld.\n\n", (tel[39] / aantal_probleeminstanties)); // voor elke probleeminstantie: de beste planning z oeken for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[31] = 2100100100; for (tel[36] = 0; tel[36] < aantal_planningen; tel[36] ++) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[36]]; sancties_berekenen (); if (tel[31] > sanctie[0]) { tel[31] = sanctie[0]; // onthouden welke planning de beste is tel[34] = tel[36]; } } for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata.ein d_lokaal[5] = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[34]]; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lokaal[tel[0]][5].tijd; tel[2] = tel[2] + lokaal[tel[0]][5].iteraties ; tel[3] = tel[3] + lokaal[tel[0]][5].afname; tel[4] = tel[4] + lokaal[tel[0]][5].beginsanc tie; } tel[1] = tel[1] / aantal_probleeminstanties;

100

tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; // de lijst aanvullen fprintf (lijst, "| lokaal 5\t\t| %d\t\t%d\t\t%d\ t\t%d\t|\n", tel[1], tel[2], tel[3], tel[4]); } // zoek een dag waarop de anesthesisten het meest b ezet zijn en verzet een relevante patient naar een ander blok met de juiste disciplin e en op de dag waarop de anesthesisten het minst bezet zijn void lokaal_6 ( void) { // balkje: resterende tijd printf ("\n.................... lokaal 6 ....... .............\n"); // tel[39] wordt gebruikt om het aantal behandelde populatie elementen bij te houden tel[39] = 0; for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // variabelen op 0 zetten lokaal[tel[30]][6].afname = lokaal[tel[30]][6 ].beginsanctie = lokaal[tel[30]][6].iteraties = lokaal[tel[30]][6].t ijd = tel[31] = tel[34] = tel[35] = 0; lokaal[tel[30]][6].tijd = GetTickCount(); // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // 5000 iteraties for (tel[36] = 0; tel[36] < aantal_iteraties; tel[36]+ +) { // beginsancties nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); tel[38] = sanctie[0]; // enkel de beginsanctie aanpassen bij een nieuw po pulatie element if (tel[35] == 0) { lokaal[tel[30]][6].beginsanctie = lokaa l[tel[30]][6].beginsanctie + sanctie[0]; tel[35] = tel[48] = 123; } // een patient zoeken die gepland staat op de druks t bezette dag do { tel[32] = rand () % aantal_patienten[te l[30]] + 0; // de dag van de gekozen patient nagaan tel[42] = patient[tel[30]][tel[32]].ope ratiedata.blok_lokaal[tel[31]]; tel[42] = blok[tel[42]].dag; } while (tel[42] != anesthesie_dag[1]); // een blok zoeken met de juiste discipline en op d e dag die het minst belast wordt do { tel[43] = rand () % aantal_blokken + 0; } while ((blok[tel[43]].dag != anesthesie_dag[0]) || (blok [tel[43]].discipline != patient[tel[30]][tel[32]].operatiedata.disciplin e)); // de patient verzetten tel[40] = patient[tel[30]][tel[32]].operat iedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[32]].operatiedata.blo k_lokaal[tel[31]] = blok[tel[43]].nummer; // de nieuwe sancties berekenen en eventueel de wij ziging ongedaan maken

101

for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); if (sanctie[0] >= tel[38]) { patient[tel[30]][tel[32]].operatiedata. blok_lokaal[tel[31]] = tel[40]; tel[34]++; } else tel[34] = 0; einde_lokaal6: // na een aantal iteraties zonder verbetering overg aan naar het volgende element if (tel[34] >= max_nul_iteraties) { // de eindsancties van het populatie element nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatieda ta.blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][6].afname = lokaal[tel[ 30]][6].afname + sanctie[0]; // volgende planning tel[31]++; tel[34] = tel[35] = tel[48] = 0; } // indien de 30 elementen zijn behandeld en we nog niet voldoende iteraties hebben if (tel[31] == aantal_planningen) break; } // de sanctieafname van het laatste element ook in rekening brengen if (tel[48] == 123) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][6].afname = lokaal[tel[30] ][6].afname + sanctie[0]; } // het correcte aantal bewerkte populatie elementen in rekening brengen if (tel[31] < aantal_planningen) tel[31]++; tel[39] = tel[39] + tel[31]; // lijstwaarden aanpassen lokaal[tel[30]][6].beginsanctie = lokaal[tel[ 30]][6].beginsanctie / tel[31]; lokaal[tel[30]][6].afname = lokaal[tel[30]][6 ].beginsanctie - (lokaal[tel[30]][6].afname / tel[31]); lokaal[tel[30]][6].tijd = GetTickCount() - lo kaal[tel[30]][6].tijd; lokaal[tel[30]][6].iteraties = tel[36]; } printf ("\nEr werd(en) gemiddeld %d populatie el ement(en) behandeld.\n\n", (tel[39] / aantal_probleeminstanties)); // voor elke probleeminstantie: de beste planning z oeken for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[31] = 2100100100; for (tel[36] = 0; tel[36] < aantal_planningen; tel[36] ++) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[36]]; sancties_berekenen (); if (tel[31] > sanctie[0])

102

{ tel[31] = sanctie[0]; // onthouden welke planning de beste is tel[34] = tel[36]; } } for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata.ein d_lokaal[6] = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[34]]; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lokaal[tel[0]][6].tijd; tel[2] = tel[2] + lokaal[tel[0]][6].iteraties ; tel[3] = tel[3] + lokaal[tel[0]][6].afname; tel[4] = tel[4] + lokaal[tel[0]][6].beginsanc tie; } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; // de lijst aanvullen fprintf (lijst, "| lokaal 6\t\t| %d\t\t%d\t\t%d\ t\t%d\t|\n", tel[1], tel[2], tel[3], tel[4]); } // zoek een dag waarop de kuis- of logistieke ploeg (en) het meest bezet zijn en verwissel een relevante major-patient met een minor -patient uit een ander blok met de juiste discipline en op een dag waarop de ploegen n iet of het minst overbezet zijn void lokaal_7 ( void) { // balkje: resterende tijd printf ("\n.................... lokaal 7 ....... .............\n"); // tel[39] wordt gebruikt om het aantal behandelde populatie elementen bij te houden tel[39] = 0; for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { // variabelen op 0 zetten lokaal[tel[30]][7].afname = lokaal[tel[30]][7 ].beginsanctie = lokaal[tel[30]][7].iteraties = lokaal[tel[30]][7].t ijd = tel[31] = tel[34] = tel[35] = 0; lokaal[tel[30]][7].tijd = GetTickCount(); // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // 5000 iteraties for (tel[36] = 0; tel[36] < aantal_iteraties; tel[36]+ +) { // beginsancties nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); tel[38] = sanctie[0]; // enkel de beginsanctie aanpassen bij een nieuw po pulatie element if (tel[35] == 0) { lokaal[tel[30]][7].beginsanctie = lokaa l[tel[30]][7].beginsanctie +

103

sanctie[0]; tel[35] = tel[48] = 123; } // een major-patient zoeken die gepland staat op de dag die het meest kuis/opstel-sancties oplevert tel[41] = 0; do { tel[32] = rand () % aantal_patienten[te l[30]] + 0; // de dag van de gekozen patient nagaan tel[42] = patient[tel[30]][tel[32]].ope ratiedata.blok_lokaal[tel[31]]; tel[42] = blok[tel[42]].dag; tel[41]++; } while (((tel[42] != kuis_opstel_dag[6]) || (patient[tel[30]][tel[32]].operatiedata.major_minor != 1)) && (tel[41] < (aantal_patienten[tel[30]] * 4))); // indien geen major-patienten gevonden worden if (tel[41] >= (aantal_patienten[tel[30]] * 4)) { tel[34] = max_nul_iteraties; goto einde_lokaal7; } // indien er dagen zijn waarop de ploegen niet over belast worden: een blok met de juiste discipline op zo'n dag zoeken if ((kuis_opstel_dag[0] == 0) || (kuis_opstel_dag[1] == 0) || (kuis_opstel_dag[2] == 0) || (kuis_opstel_dag[3] == 0) || (kuis_opstel_dag[4] == 0)) do { tel[43] = rand () % aantal_blokken + 0; tel[46] = blok[tel[43]].dag; } while ((kuis_opstel_dag[tel[46]-1] != 0) || (blok[tel[43 ]].discipline != patient[tel[30]][tel[32]].operatiedata.discipline)) ; // indien alle dagen sancties opleveren: een blok z oeken met de juiste discipline op de dag die de minste kuis/opstel-sancties oplevert else do tel[43] = rand () % aantal_blokken + 0; while ((blok[tel[43]].dag != kuis_opstel_dag[5]) || (blok[tel[43]].discipline != patient[tel[30]][tel[3 2]].operatiedata.discipline)); // een minor-patient uit het gevonden blok zoeken tel[41] = 0; do { tel[47] = rand () % aantal_patienten[te l[30]] + 0; tel[41]++; } while (((patient[tel[30]][tel[47]].operatiedata.blok_lok aal[tel[31]] != tel[43]) || (patient[tel[30]][tel[47]].operatiedata .major_minor != 2)) && (tel[41] < (aantal_patienten[tel[30]] * 4))); // indien geen minor-patienten gevonden worden if (tel[41] >= (aantal_patienten[tel[30]] * 4)) { tel[34]++; goto einde_lokaal7; } // de patienten verwisselen tel[40] = patient[tel[30]][tel[32]].operat iedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[32]].operatiedata.blo k_lokaal[tel[31]] = patient[tel[30]][tel[47]].operatiedata.blok_lokaal[ tel[31]]; patient[tel[30]][tel[47]].operatiedata.blo k_lokaal[tel[31]] = tel[40]; // de nieuwe sancties berekenen en eventueel de wij ziging ongedaan maken for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]];

104

sancties_berekenen (); if (sanctie[0] >= tel[38]) { tel[40] = patient[tel[30]][tel[32]].ope ratiedata.blok_lokaal[tel[31]]; patient[tel[30]][tel[32]].operatiedata. blok_lokaal[tel[31]] = patient[tel[30]][tel[47]].operatiedata.blok_lokaal[ tel[31]]; patient[tel[30]][tel[47]].operatiedata. blok_lokaal[tel[31]] = tel[40]; tel[34]++; } else tel[34] = 0; einde_lokaal7: // na een aantal iteraties zonder verbetering overg aan naar het volgende element if (tel[34] >= max_nul_iteraties) { // de eindsancties van het populatie element nagaan for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatieda ta.blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][7].afname = lokaal[tel[ 30]][7].afname + sanctie[0]; // volgende planning tel[31]++; tel[34] = tel[35] = tel[48] = 0; } // indien de 30 elementen zijn behandeld en we nog niet voldoende iteraties hebben if (tel[31] == aantal_planningen) break; } // de sanctieafname van het laatste element ook in rekening brengen if (tel[48] == 123) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[31]]; sancties_berekenen (); lokaal[tel[30]][7].afname = lokaal[tel[30] ][7].afname + sanctie[0]; } // het correcte aantal bewerkte populatie elementen in rekening brengen if (tel[31] < aantal_planningen) tel[31]++; tel[39] = tel[39] + tel[31]; // lijstwaarden aanpassen lokaal[tel[30]][7].beginsanctie = lokaal[tel[ 30]][7].beginsanctie / tel[31]; lokaal[tel[30]][7].afname = lokaal[tel[30]][7 ].beginsanctie - (lokaal[tel[30]][7].afname / tel[31]); lokaal[tel[30]][7].tijd = GetTickCount() - lo kaal[tel[30]][7].tijd; lokaal[tel[30]][7].iteraties = tel[36]; } printf ("\nEr werd(en) gemiddeld %d populatie el ement(en) behandeld.\n\n", (tel[39] / aantal_probleeminstanties)); // voor elke probleeminstantie: de beste planning z oeken for (tel[30] = 0; tel[30] < aantal_probleeminstanties; tel[30]++) { tel[31] = 2100100100; for (tel[36] = 0; tel[36] < aantal_planningen; tel[36] ++) { for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[36]];

105

sancties_berekenen (); if (tel[31] > sanctie[0]) { tel[31] = sanctie[0]; // onthouden welke planning de beste is tel[34] = tel[36]; } } for (tel[37] = 0; tel[37] < aantal_patienten[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata.ein d_lokaal[7] = patient[tel[30]][tel[37]].operatiedata.blok_lokaal[ tel[34]]; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstanties; t el[0]++) { tel[1] = tel[1] + lokaal[tel[0]][7].tijd; tel[2] = tel[2] + lokaal[tel[0]][7].iteraties ; tel[3] = tel[3] + lokaal[tel[0]][7].afname; tel[4] = tel[4] + lokaal[tel[0]][7].beginsanc tie; } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / aantal_probleeminstanties; tel[4] = tel[4] / aantal_probleeminstanties; // de lijst aanvullen fprintf (lijst, "| lokaal 7\t\t| %d\t\t%d\t\t%d\ t\t%d\t|\n", tel[1], tel[2], tel[3], tel[4]); }

106

Genetisch algoritme 0 tel[50] = GetTickCount(); printf ("\n===================================== =============\n\n"); printf ("Genetisch algoritme 0 wordt uitgevoerd. .. \n\n"); // balkje: resterende tijd printf ("................... voortgang ......... ...........\n"); // de lijstvariabelen resetten: for (tel[0] = 0; tel[0] < aantal_probleeminstant ies; tel[0]++) lijstvar[tel[0]].tijd = lijstvar[tel[0]].iter aties = lijstvar[tel[0]].beginsanctie = lijstvar[tel[0]].af name = 0; // voor elke probleeminstantie... for (tel[30] = 0; tel[30] < aantal_probleeminsta nties; tel[30]++) { // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // de starttijd bepalen lijstvar[tel[30]].tijd = GetTickCount(); // de beginsanctie nagaan for (tel[37] = 0; tel[37] < aantal_planningen ; tel[37]++) lijstvar[tel[30]].beginsanctie = lijstvar[ tel[30]].beginsanctie + sanctie_heur[tel[30]][tel[37]]; // 5000 iteraties for (tel[31] = 0; tel[31] < aantal_iteraties; tel[31]++) { // random 2 verschillende parents selecter en tel[40] = rand () % aantal_planningen + 0; do tel[41] = rand () % aantal_planningen + 0; while (tel[40] == tel[41]); for (tel[33] = 0; tel[33] < aantal_patient en[tel[30]]; tel[33]++) { patient[tel[30]][tel[33]].operatiedata. ouder[0] = patient[tel[30]][tel[33]].operatiedata.blok_heur[te l[40]]; patient[tel[30]][tel[33]].operatiedata. ouder[1] = patient[tel[30]][tel[33]].operatiedata.blok_heur[te l[41]]; } // dit voor elke discipline doen for (tel[36] = 1; tel[36] < 7; tel[36]++) { // aantal patienten met de juiste disci pline tellen tel[35] = 0; for (tel[33] = 0; tel[33] < aantal_pati enten[tel[30]]; tel[33]++) if (patient[tel[30]][tel[33]].operat iedata.discipline == tel[36]) { tel[35]++; } // het crossover-punt bepalen: elk deel bevat minstens 1 patient tel[34] = (rand () % (tel[35] - 1) + 1) ; // de switch maken tel[33] = tel[38] = 0; do { if (patient[tel[30]][tel[33]].operat iedata.discipline == tel[36]) { patient[tel[30]][tel[33]].operati edata.kind[0] = patient[tel[30]][tel[33]].operatiedata.ouder[0]; patient[tel[30]][tel[33]].operati edata.kind[1] = patient[tel[30]][tel[33]].operatiedata.ouder[1]; tel[38]++;

107

} tel[33]++; } while (tel[38] < tel[34]); do { if (patient[tel[30]][tel[33]].operat iedata.discipline == tel[36]) { patient[tel[30]][tel[33]].operati edata.kind[0] = patient[tel[30]][tel[33]].operatiedata.ouder[1]; patient[tel[30]][tel[33]].operati edata.kind[1] = patient[tel[30]][tel[33]].operatiedata.ouder[0]; tel[38]++; } tel[33]++; } while (tel[38] < tel[35]); } // sancties nagaan for (tel[37] = 0; tel[37] < aantal_patient en[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.ouder[0]; sancties_berekenen (); sanctie_ouder[0] = sanctie[0]; for (tel[37] = 0; tel[37] < aantal_patient en[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.ouder[1]; sancties_berekenen (); sanctie_ouder[1] = sanctie[0]; for (tel[37] = 0; tel[37] < aantal_patient en[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.kind[0]; sancties_berekenen (); sanctie_kind[0] = sanctie[0]; for (tel[37] = 0; tel[37] < aantal_patient en[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok = patient[tel[30]][tel[37]].operatiedata.kind[1]; sancties_berekenen (); sanctie_kind[1] = sanctie[0]; // eventueel de ouders vervangen door de k inderen if ((sanctie_ouder[0] + sanctie_ouder[1]) > (sanctie_kind[0] + sanctie_kind[1])) for (tel[37] = 0; tel[37] < aantal_pati enten[tel[30]]; tel[37]++) { patient[tel[30]][tel[37]].operatieda ta.blok_heur[tel[40]] = patient[tel[30]][tel[37]].operatiedata.kind[0]; patient[tel[30]][tel[37]].operatieda ta.blok_heur[tel[41]] = patient[tel[30]][tel[37]].operatiedata.kind[1]; } } // de laatste kinderen in een kindvariabele s teken for (tel[32] = 0; tel[32] < aantal_planningen ; tel[32]++) for (tel[37] = 0; tel[37] < aantal_patient en[tel[30]]; tel[37]++) patient[tel[30]][tel[37]].operatiedata. blok_kind[tel[32]] = patient[tel[30]][tel[37]].operatiedata.blok_heur[te l[32]]; // de lijstvariabelen aanpassen lijstvar[tel[30]].iteraties = tel[31]; lijstvar[tel[30]].tijd = GetTickCount() - lij stvar[tel[30]].tijd; } // de sanctieafname nagaan for (tel[30] = 0; tel[30] < aantal_probleeminsta nties; tel[30]++) { for (tel[37] = 0; tel[37] < aantal_planningen ; tel[37]++) {

108

for (tel[38] = 0; tel[38] < aantal_patient en[tel[30]]; tel[38]++) patient[tel[30]][tel[38]].operatiedata. blok = patient[tel[30]][tel[38]].operatiedata.blok_kind[te l[37]]; sancties_berekenen(); lijstvar[tel[30]].afname = lijstvar[tel[30 ]].afname + sanctie[0]; } lijstvar[tel[30]].afname = lijstvar[tel[30]].beg insanctie - lijstvar[tel[30]].afname; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstant ies; tel[0]++) { tel[1] = tel[1] + lijstvar[tel[0]].tijd; tel[2] = tel[2] + lijstvar[tel[0]].iteraties; tel[3] = tel[3] + (lijstvar[tel[0]].afname / aantal_planningen); tel[4] = tel[4] + (lijstvar[tel[0]].beginsanc tie / aantal_planningen); // hier gebeuren 200 afrondingen, maar anders wordt de waar de groter dan de int-range. } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / (aantal_probleeminstanties); tel[4] = tel[4] / (aantal_probleeminstanties);

Genetisch algoritme 1 tel[50] = GetTickCount(); printf ("\n===================================== =============\n\n"); printf ("Genetisch algoritme 1 wordt uitgevoerd. .. \n\n"); // balkje: resterende tijd printf ("................... voortgang ......... ...........\n"); // de lijstvariabelen resetten: for (tel[0] = 0; tel[0] < aantal_probleeminstant ies; tel[0]++) lijstvar[tel[0]].tijd = lijstvar[tel[0]].iter aties = lijstvar[tel[0]].beginsanctie = lijstvar[tel[0]].af name = 0; // voor elke probleeminstantie... for (tel[30] = 0; tel[30] < aantal_probleeminsta nties; tel[30]++) { // het voortgangbalkje if ((tel[30] % 4) == 0) printf ("."); // de starttijd bepalen lijstvar[tel[30]].tijd = GetTickCount(); // de beginsanctie nagaan for (tel[37] = 0; tel[37] < aantal_planningen ; tel[37]++) lijstvar[tel[30]].beginsanctie = lijstvar[ tel[30]].beginsanctie + sanctie_heur[tel[30]][tel[37]]; // 5000 iteraties for (tel[31] = 0; tel[31] < aantal_iteraties; tel[31]++) { // de janee variabelen resetten for (tel[36] = 0; tel[36] < aantal_patient en[tel[30]]; tel[36]++) for (tel[32] = 0; tel[32] < aantal_plan ningen; tel[32]++) patient[tel[30]][tel[36]].operatieda ta.janee[tel[32]] = 0; // een volledige kindpopulatie - 2 vullen for (tel[34] = 0; tel[34] < (aantal_planni ngen - 2); tel[34]++) {

109

// voor elke patient... for (tel[32] = 0; tel[32] < aantal_pati enten[tel[30]]; tel[32]++) { // random 2 verschillende ongebruikt e parents selecteren do tel[40] = rand () % aantal_planni ngen + 0; while (patient[tel[30]][tel[32]].ope ratiedata.janee[tel[40]] != 0); do tel[41] = rand () % aantal_planni ngen + 0; while ((tel[40] == tel[41]) || (patient[tel[30]][tel[32]].operatiedata.janee[tel[4 1]] != 0)); // 2 kinderen aanmaken en de ouders markeren patient[tel[30]][tel[32]].operatieda ta.kind[0] = patient[tel[30]][tel[32]].operatiedata.blok_ouder[t el[40]]; patient[tel[30]][tel[32]].operatieda ta.janee[tel[40]] = 1; patient[tel[30]][tel[32]].operatieda ta.kind[1] = patient[tel[30]][tel[32]].operatiedata.blok_ouder[t el[41]]; patient[tel[30]][tel[32]].operatieda ta.janee[tel[41]] = 2; } // de sancties van de kinderen nagaan for (tel[33] = 0; tel[33] < aantal_pati enten[tel[30]]; tel[33]++) patient[tel[30]][tel[33]].operatieda ta.blok = patient[tel[30]][tel[33]].operatiedata.kind[0]; sancties_berekenen (); sanctie_kind[0] = sanctie[0]; for (tel[33] = 0; tel[33] < aantal_pati enten[tel[30]]; tel[33]++) patient[tel[30]][tel[33]].operatieda ta.blok = patient[tel[30]][tel[33]].operatiedata.kind[1]; sancties_berekenen (); sanctie_kind[1] = sanctie[0]; // het beste kind in een nieuwe populat ie steken en de niet gebruikte janee variabelen resetten if (sanctie_kind[0] <= sanctie_kind[1]) for (tel[33] = 0; tel[33] < aantal_p atienten[tel[30]]; tel[33]++) { patient[tel[30]][tel[33]].operati edata.blok_kind[tel[34]] = patient[tel[30]][tel[33]].operatiedata.kind[0]; for (tel[35] = 0; tel[35] < aanta l_planningen; tel[35]++) { if (patient[tel[30]][tel[33]]. operatiedata.janee[tel[35]] == 1) patient[tel[30]][tel[33]].o peratiedata.janee[tel[35]] = 3; if (patient[tel[30]][tel[33]]. operatiedata.janee[tel[35]] == 2) patient[tel[30]][tel[33]].o peratiedata.janee[tel[35]] = 0; } } if (sanctie_kind[0] > sanctie_kind[1]) for (tel[33] = 0; tel[33] < aantal_p atienten[tel[30]]; tel[33]++) { patient[tel[30]][tel[33]].operati edata.blok_kind[tel[34]] = patient[tel[30]][tel[33]].operatiedata.kind[1]; for (tel[35] = 0; tel[35] < aanta l_planningen; tel[35]++) { if (patient[tel[30]][tel[33]]. operatiedata.janee[tel[35]] == 1) patient[tel[30]][tel[33]].o peratiedata.janee[tel[35]] = 0; if (patient[tel[30]][tel[33]]. operatiedata.janee[tel[35]] == 2) patient[tel[30]][tel[33]].o peratiedata.janee[tel[35]] = 3; } } } // de laatste 2 kindelementen aanmaken en de kindpopulatie vervolledigen for (tel[32] = 0; tel[32] < aantal_patient en[tel[30]]; tel[32]++)

110

{ // random 2 verschillende ongebruikte p arents selecteren do tel[40] = rand () % aantal_planninge n + 0; while (patient[tel[30]][tel[32]].operat iedata.janee[tel[40]] != 0); do tel[41] = rand () % aantal_planninge n + 0; while ((tel[40] == tel[41]) || (patient[tel[30]][tel[32]].operatiedata.janee[tel[4 1]] != 0)); // 2 kinderen aanmaken patient[tel[30]][tel[32]].operatiedata. kind[0] = patient[tel[30]][tel[32]].operatiedata.blok_ouder[t el[40]]; patient[tel[30]][tel[32]].operatiedata. kind[1] = patient[tel[30]][tel[32]].operatiedata.blok_ouder[t el[41]]; } // beide kinderen in de nieuwe populatie s teken for (tel[33] = 0; tel[33] < aantal_patient en[tel[30]]; tel[33]++) { patient[tel[30]][tel[33]].operatiedata. blok_kind[aantal_planningen - 2] = patient[tel[30]][tel[33]].operatiedata.kind[0]; patient[tel[30]][tel[33]].operatiedata. blok_kind[aantal_planningen - 1] = patient[tel[30]][tel[33]].operatiedata.kind[1]; } // de kindpopulatie wordt ouderpopulatie for (tel[33] = 0; tel[33] < aantal_patient en[tel[30]]; tel[33]++) for (tel[35] = 0; tel[35] < aantal_plan ningen; tel[35]++) patient[tel[30]][tel[33]].operatieda ta.blok_ouder[tel[35]] = patient[tel[30]][tel[33]].operatiedata.blok_kind[te l[35]]; } // de lijstvariabelen aanpassen lijstvar[tel[30]].iteraties = tel[31]; lijstvar[tel[30]].tijd = GetTickCount() - lij stvar[tel[30]].tijd; } // de sanctieafname nagaan for (tel[30] = 0; tel[30] < aantal_probleeminsta nties; tel[30]++) { for (tel[37] = 0; tel[37] < aantal_planningen ; tel[37]++) { for (tel[38] = 0; tel[38] < aantal_patient en[tel[30]]; tel[38]++) patient[tel[30]][tel[38]].operatiedata. blok = patient[tel[30]][tel[38]].operatiedata.blok_kind[te l[37]]; sancties_berekenen(); lijstvar[tel[30]].afname = lijstvar[tel[30 ]].afname + sanctie[0]; } lijstvar[tel[30]].afname = lijstvar[tel[30]].beg insanctie - lijstvar[tel[30]].afname; } // tellers resetten for (tel[0] = 1; tel[0] < 5; tel[0]++) tel[tel[0]] = 0; // gemiddelde waarden berekenen for (tel[0] = 0; tel[0] < aantal_probleeminstant ies; tel[0]++) { tel[1] = tel[1] + lijstvar[tel[0]].tijd; tel[2] = tel[2] + lijstvar[tel[0]].iteraties; tel[3] = tel[3] + (lijstvar[tel[0]].afname / aantal_planningen); tel[4] = tel[4] + (lijstvar[tel[0]].beginsanc tie / aantal_planningen); // de /30 zorgt voor 200 afrondingen, maar anders wordt de wa arde groter dan de int-range. } tel[1] = tel[1] / aantal_probleeminstanties; tel[2] = tel[2] / aantal_probleeminstanties; tel[3] = tel[3] / (aantal_probleeminstanties); tel[4] = tel[4] / (aantal_probleeminstanties);

111

Bibliografie

COLTON SIMON (2005). Search in Problem Solving, URL:

<http://www.doc.ic.ac.uk/~sgc/teaching/v231/lecture3.html>. (03/05/2007).

COULIER CLINT en ALVARADO-VARGAS MARCELO JOAN (2006). Modeling and Simulating of

Operating Room Scheduling, Internship report, Faculty of Economics and Business

Administration, Ghent University.

DE KREUK A.C.C., WINANDS E.M.M. en VISSERS J.M.H. (2005). Master scheduling of medical

specialists, Health Operations Management, blz. 184-201.

DENTON BRIAN en GUPTA DIWAKAR (2003). A sequential bounding approach for optimal

appointment scheduling, IIE Transactions, vol. 35, blz. 1003-1016.

DEXTER F. (2001). Cost implications of various operating room scheduling strategies, American

Society of Anesthesiologist’s Clinical Update Program, vol. 52, nr. 262, blz. 1-6.

GERCHAK YIGAL, GUPTA DIWAKAR en HENIG MORDECHAI (1996). Reservation Planning for

Elective Surgery Under Uncertain Demand for Emergency Surgery, Management Science, vol.

42, nr. 3, blz. 321-334.

GUINET ALAIN en CHAABANE SONDES (2003). Operation theatre planning, Int. J. Production

Economics, vol. 85, blz. 69-81.

HANS E.W., WULLINK GERHARD, VAN HOUDENHOVEN MARK en KAZEMIER GEERT (2006). Robust

surgery loading, European Journal Of Operations Research, In Press, Corrected Proof.

HANSEN PIERRE EN MLADENOVIĆ NENAD (2001). Variable neighborhood search: Principles and

applications, European Journal of Operational Research, vol. 130, blz. 449-467.

112

JEBALI AÏDA , HADJ ALOUANE ATIDEL B. en LADET PIERRE (2006). Operating rooms scheduling,

Int. J. Production Economics, vol. 99, blz. 52-62.

MARCON ERIC, KHARRAJA SAÏD en SIMONNET GERARD (2003). The operating theatre planning by

the follow-up of the risk of no realization, Int. J. Production Economics, vol. 85, blz. 83-90.

Mladenović N. en Hansen P. (1997). Variable Neighborhood Search. Computers Operations

Research, vol. 24, nr. 11, blz. 1097-1100.

REEVES R. COLIN (1993). Modern Heuristic Techniques for Combiorial Problem. Oxford:

Blackwell Scientific Publicaions.

SIER D., TOBIN P. en MCGURK C. (1997). Scheduling Surgical Procedures, The Journal of the

Operational Research Society, vol. 48, nr. 9, blz. 884-891.

TEIL A. (2000). Coûts d’une minute de bloc opératoire et Coûts d’un service d’anesthésie :

Quelles utilisations, Actes du colloque SNPHAR, Djerba, blz. 165-181.

VAN OOSTRUM J.M., VAN HOUDENHOVEN M., HURINK J.L., HANS E.W., WULLINK G. en

KAZEMIER G. (2005). A Master Surgical Scheduling approach for cyclic scheduling in operating

room departments, Department of Applied Mathematics, Faculty of EEMCS, University of

Twente, The Netherlands.