Netwerkmodellen en Algoritmen Netwerkmodellen en algoritmen Technisch Universiteit Delft Docent: Datum: Dr. J.B.M. Melissen 20 juli 2017 1 Netwerkmodellen en Algoritmen 2 0 Inhoudsopgave 0 1 2 3 Inhoudsopgave Inleiding Netwerken Netwerkmodellen 3.1 Terminologie 3.2 Transformaties 3.3 Niet-lineaire doelfuncties 4 Bomen 5 Algoritmes voor Max-Flow 5.1 Max-flow min-cut stelling 5.2 Algoritme van Ford en Fulkerson 5.3 Convergentieproblemen in Ford-Fulkerson 5.4 Algoritme van Karzanov 5.5 Toelaatbare stromen 6 7 Netwerkalgoritmen Literatuur 2 3 3 6 6 7 10 11 12 12 13 16 17 19 23 24 Netwerkmodellen en Algoritmen 3 1 Inleiding 2 Netwerken In dit hoofdstuk zullen we eerst een paar algemeenheden over netwerken herhalen. Een netwerk of graaf is een paar (N, A), waarbij N een (eindige) verzameling van zogenaamde knopen (hoekpunten, vertices, nodes, points, elements) is en A een deelverzameling van NN. A is dus een verzameling van paren knopen die een verbinding tussen begin- en eindknoop voorstellen en takken (zijden, arcs, edges, lines, branches) worden genoemd. Een tak (a, b) heeft een beginknoop a en een eindknoop b en dus ook een richting. Als richtingen in een graaf niet van belang zijn spreek je van een ongerichte graaf. Formeel kun je dat doen door te eisen dat er voor elke tak ook een tak in tegengestelde richting loopt (de verzameling A is symmetrisch), of door te zeggen dat de verzameling A een deelverzameling is van de verzameling van deelverzamelingen van N die twee elementen bevatten. Een tak is dan niet meer een geordend paar, maar een verzameling met twee elementen uit N (zonder volgorde dus). Een netwerk of graaf is in principe een abstract gegeven. Het is een verzameling van knopen en een verzameling van paren knopen, de takken. Toch denken we vaak concreet over een netwerk. De knopen tekenen we bijvoorbeeld als punten in het vlak en de takken zijn verbindingslijntjes tussen de punten, eventueel voorzien van een pijlpunt om de richting aan te geven. Een bekend probleem dat met het tekenen van netwerken in het vlak te maken heeft is het volgende: Je hebt een elektriciteitscentrale, een waterleidingbedrijf en een gasleverancier. Drie huizen moeten elk van gas, water en licht worden voorzien. De vraag is nu of je elk huis met elk van de leveranciers kunt verbinden zonder dat leidingen elkaar kruisen. Het wiskundige probleem is: je neemt een volledige bipartite graaf K3,3 van drie punten op drie punten (d.w.z. je tekent links drie punten en rechts ook. Elk van de punten links moet met elk van de punten rechts worden verbonden). Is het mogelijk om deze graaf vlak te tekenen, d.w.z. zonder doorsnijdingen van zijden? Als je dit gaat proberen zul je al snel merken dat het telkens bijna lijkt te lukken, maar net niet helemaal. Dat komt omdat het niet kan. Een bewijs dat het niet gaat kun je bijvoorbeeld halen uit een topologisch resultaat van Euler. Als je in het vlak een vlakke graaf tekent en je telt het aantal knopen k, het aantal zijden z, en het aantal gebiedjes v dat ontstaat (het buitengebied telt ook mee), dan geldt altijd: k – z + v = 2 (dit is eenvoudig met volledige inductie te bewijzen). Voor een n-hoek geldt bijvoorbeeld: n - n + 2 = 2, voor een vierkant met zijn twee diagonalen geldt: 5 – 8 + 5 = 2. Stel nu dat we K3,3 vlak kunnen tekenen, dan ontstaan er door doorsnijdingen geen nieuwe knopen en zijden, dus k = 6 en z = 9. Dit betekent dat het aantal vlakjes v = 5 moet zijn. De graaf kan circuits (cycles) bevatten, maar dat kunnen alleen vierhoeken of zeshoeken zijn, omdat je alleen maar van een huis naar een voorziener kunt gaan of omgekeerd, maar tussen de huizen onderling, of de voorzieners onderling is er geen verbinding. Het aantal zijden is dus even, maar het aantal hoekpunten is maximaal 6. Stel dat je p vierhoeken hebt en q zeshoeken, dan geldt: p + q = v = 5 (het aantal vlakjes), en 4p + 6q = 2z = 18 (elke zijde wordt dubbel geteld). Deze twee vergelijkingen hebben als oplossing: p = 6, q = -1, dat kan dus niet. Hieronder zie je K3,3 met één zelfdoorsnijding k = 7 knopen, z = 11 takken en v = 5 vlakjes. Netwerkmodellen en Algoritmen 4 Een precieze karakterisering van de grafen die vlak zijn geeft de volgende stelling: Stelling (Kuratowski, 1922): Een graaf is vlak (zonder doorsnijdingen in het vlak te tekenen) precies dan als K3,3 en K5 niet optreden als deelgraaf. Hierin is K5 is de volledige graaf op 5 punten: Neem 5 punten en verbind elk punt met elk ander punt. De stelling van Kuratowski is niet van groot praktisch belang omdat het in de praktijk niet meevalt om alle deelgrafen op 5 en 6 punten te onderzoeken. De beste bekende algoritmen om de vlakheid van een graaf te onderzoeken hebben een complexiteit O(n). Na dit negatieve resultaat is er ook een positief resultaat: Je kunt een willekeurige graaf wel altijd in R3 “tekenen” zonder doorsnijdingen, en zelfs met rechte lijntjes: Stelling Elke graaf is in te bedden in R3 waarbij de zijden (rechte) lijnstukken zijn die elkaar alleen in knopen snijden. Bewijs: We beelden de j-de knoop af op het punt (j, j2, j3) in R3. Het idee is dat de punten dan zodanig “krom” in de ruimte liggen dat twee verschillende lijnstukjes elkaar hooguit in eindpunten kunnen snijden. We nemen nu vier verschillende knopen met kentallen i, j, k en l en we moeten laten zien dat het lijnstuk tussen de i-de en de j-de knoop niet het lijnstuk tussen de k-de en de l-de knoop kan snijden. Dit betekent het voldoende is om te laten zien dat de lijnstukken tussen i en j, tussen j en k en tussen k en l niet in één vlak liggen (want dat is wel het geval als de twee lijnstukjes elkaar snijden), d.w.z. dat de vectoren i j jk k l 2 2 2 2 2 2 i j , j k en k l i3 j3 j3 k 3 k3 l3 lineair onafhankelijk zijn. We moeten dus laten zien dat de determinant i j i j 2 jk 2 i3 j3 j k k l 2 k2 l2 j3 k 3 k3 l3 2 ongelijk is aan 0. In deze determinant kun je per kolom factoren i-j, j-k en k-l buiten haakjes halen: Netwerkmodellen en Algoritmen 5 i j jk i2 j2 j2 k2 k 2 l 2 = (i j )( j k )( k l ) i j j k k l 3 3 3 k l 3 3 1 1 i j jk i ij j 3 1 2 2 k l j jk k 2 2 k kl l 2 2 Vegen met de eerste rij levert: 1 (i j )( j k )( k l ) 1 ik i ij jk k 2 (i j )( j k )( k l ) 1 l j 0 2 = 0 l kl jk j 2 ik l j i k i j k l j j k l (i j )( j k )( k l )(i k )(l j ) 1 1 i j k j k l (i j )( j k )( k l )(i k )(l j )(l i ) . 2 = = De determinant is ongelijk aan nul, omdat alle kentallen ongelijk aan elkaar zijn. Netwerkmodellen en Algoritmen 6 3 Netwerkmodellen 3.1 Terminologie Een bronknoop (source node) is een knoop waar alleen maar takken uit weggaan. Bij een bronknoop hoort meestal een niet-negatief aanbod (supply) b > 0, de hoeveelheid stroom die de bronknoop levert. Een putknoop (sink node) is een knoop waar alleen maar takken in uitkomen. Bij een putknoop hoort meestal een niet-positieve vraag (demand) b < 0, de hoeveelheid stroom die de bronknoop nodig heeft Een doorvoerknoop (transshipment node) is een knoop waar zowel takken in uitkomen als uit weggaan. Een zuivere doorvoerknoop is een doorvoerknoop waar de vraag en het aanbod gelijk is aan nul. Een stroom is een toewijzing van niet-negatieve waarden aan de takken van het netwerk zodanig dat er in elke knoop behoud van stroom is: alle stroom die de knoop in gaat moet er ook weer uitkomen. We bekijken in het algemeen stromingsproblemen (capacitated minimum cost flow problem), waarbij de minimale stroming door een netwerk wordt gezocht, waarbij er op de takken grenzen voor de stroom zijn aangegeven, en de knopen nog een externe vraag of aanbod kunnen hebben. Een stromingsprobleem kan als volgt worden geformuleerd: minimaliseer c ij xij i, j onder voorwaarde dat xij x ji bi , voor elke knoop i. j j lij xij u ij , voor elke zijde (i,j). Hierin is xij de stroom tussen knoop i en knoop j. De coëfficiënten cij stellen de kosten (lineair per eenheid) van een eenheidsstroom tussen knoop i en knoop j voor (als deze tak bestaat). De bi stellen de waarden van de vraag (>0) of het aanbod (<0) voor. De lij en uij zijn de onder- en bovengrenzen op de stroom per tak. De gegevens van een netwerkprobleem kunnen grafisch in het netwerk worden uitgebeeld: Een speciaal geval van een stromingsprobleem is een transportprobleem. Dit is een stromingsprobleem zonder doorvoerknopen. elke knoop in het netwerk is dus een bronknoop óf een putknoop. Tussen bronknopen onderling zijn er geen takken, want uit bronknopen gaan alleen takken weg. Evenzo zijn er geen takken tussen putknopen. Je kunt alle Netwerkmodellen en Algoritmen 7 bronknopen links zetten en alle putknopen rechts, dan ontstaat er een bipartite graaf: er zijn alleen takken tussen de knopen links en de knopen rechts. Het transportprobleem modelleert bijvoorbeeld het transport van goederen: Hoe kun je een gegeven aanbod het goedkoopst naar afnemers met een bepaalde vraag brengen. Het is duidelijk dat een transportprobleem alleen oplosbaar is als de totale vraag gelijk is aan het totale aanbod. Vanwege de balansvergelijkingen – het behoud van stroom – kan er geen stroom verdwijnen of bijkomen. Een eis voor de toelaatbaarheid van een transportprobleem is dus dat bi 0 . i Een speciaal geval van een transportprobleem is het toewijzingsprobleem (assignment problem). Dat is een transportprobleem waarin elk aanbod in een bronknoop 1 is en elke vraag in een putknoop ook 1. Wil een toewijzingsprobleem toelaatbare oplossingen hebben, dan moet het aantal bronknopen gelijk zijn aan het aantal putknopen. De stromen in het netwerk zullen 0 of 1 zijn. Dat betekent dat elke bron aan precies één put wordt gekoppeld, en dat op de goedkoopste manier, vandaar de naar “toewijzingsprobleem”. Het kortste padprobleem kan ook worden geformuleerd als een netwerkprobleem. Het startpunt wordt dan een bron met aanbod 1, de eindknoop een put met vraag 1. De kosten van een zijde is zijn lengte. De minimale koststroming is dus een stroom ter grootte 1 die langs de kortst mogelijke route van beginpunt naar eindpunt zal stromen. 3.2 Transformaties We zullen nu een aantal transformaties bespreken waarmee netwerken kunnen worden aangepast tot een equivalent netwerk. 1. Van een transportprobleem met geheeltallig aanbod en vraag kun je in principe een (meestal zeer groot) toewijzingsprobleem maken. Dit kun je doen door elke bron- en putknoop te splitsen in een aantal knopen dat gelijk is aan de betreffende aanbods- of vraagwaarde van die knoop. De gesplitste knopen krijgen allemaal een bron-/putwaarde gelijk aan 1. De takken lopen hetzelfde als bij de moederknopen, de kostencoëfficiënten blijven ook gelijk. Voorbeeld: Deze constructie geeft alleen aan dat een geheeltallig transportprobleem equivalent is met een toewijzingsprobleem, maar enig praktisch nut is er niet, omdat het toewijzingsprobleem Netwerkmodellen en Algoritmen 8 in het algemeen erg groot is, en de grootte niet alleen afhangt van het oorspronkelijke netwerk van het transportprobleem, maar ook van de grootte van de vraag en aanbod. 2. Elke zuivere doorvoerknoop kan worden gesplitst in een bron- en een putknoop: Door het splitsen krijgen we twee knopen, waarbij de bronknoop Vs alle takken krijgt die de originele knoop uitgingen (want een bronknoop kan alleen uitgaande takken hebben), terwijl alle takken die erin kwamen naar de putknoop Vd gaan. Een verbinding tussen de nieuwe bron- en putknoop kan dus automatisch alleen van bron naar put lopen. Verder geven we de bronknoop een aanbod ter grootte M1 en de putknoop een vraag ter grootte M2, waarvan we de waarde nog even niet vastleggen. De tak tussen Vs en Vd krijgt een kostencoëfficiënt 0, zodat de extra tak geen invloed heeft op de doelfunctie. Als we de twee nieuwe balansvergelijkingen, die naast de nieuwe knopen staan, bij elkaar optellen zien we dat M 2 M1 xVi x jV 0 , i j vanwege de balansvergelijking in de originele knoop. Hieruit volgt dat M1 = M2 =: M. Verder moet voor de stroom tussen Vs en Vd gelden dat 0 x sd M xVi . Dit betekent i dat M groter moet zijn dan de stroom die maximaal door V kan stromen (bijvoorbeeld de som van de capaciteiten van de inkomende takken). Het splitsen van een knoop is ook uit te voeren als er in de originele knoop een extra vraag of aanbod is. 3. Een tak met grenzen kan worden vervangen door het invoegen van een extra knoop en takken zonder grenzen: Netwerkmodellen en Algoritmen 9 In het originele netwerk gelden de volgende twee balansvergelijkingen in de knopen: xVi x jV xVW 0 , i j x Wk xlW xVW 0 . k l In het nieuwe netwerk hebben we drie knopen en dus ook drie balansvergelijkingen: xVi x jV xVI a , i j xVI xW I b a , x Wk k xW xW I b . l De eerste balansvergelijking in het nieuwe netwerk zegt dat de stroom die uit V richting I en dus W loopt gelijk is aan xVi + a. De derde balansvergelijking in het nieuwe netwerk zegt dat de stroom die in W komt vanuit de richting I en dus vanuit W gelijk is aan –xWi + b. De middelste balansvergelijking zegt dat deze twee stromen gelijk zijn. Dat moet ook, want ze moeten gelijk zijn aan xVW uit het originele netwerk. We hebben nu dus dat xVW = xVI + a = –xWI + b. Omdat de stromen xVI en xWI in het nieuwe netwerk niet-negatief zijn volgt uit deze representatie automatisch dat a xVW b, zonder dat er expliciete capaciteitsgrenzen zijn gegeven in het nieuwe model. De bijdrage aan de doelfunctie is in het tweede netwerk: cxVI + ca = cxVW, dus ook dat klopt. De speciale gevallen a = 0 (geen ondergrens) en b = (geen bovengrens) staan hieronder. Netwerkmodellen en Algoritmen 10 3.3 Niet-lineaire doelfuncties De hele theorie van netwerkmodellen is erop gebaseerd dat de doelfunctie lineair is. De kostencoëfficiënt van elke tak geeft de kosten van een stroom door die tak per eenheid. Toch kun je ook een beperkte vorm van niet-lineariteit in het model stoppen door middel van meerdere takken tussen dezelfde knopen met capaciteitsgrenzen en verschillende kostencoëfficiënten. Maak bijvoorbeeld tussen twee knopen twee takken in dezelfde richting, één met kostencoëfficiënt 2 en begrenzend interval [0,10], en de tweede met coëfficiënt 3 en interval [0,5]. Dit levert een verbinding op tussen de twee knopen met een begrenzend interval [0,15] en een niet-lineaire kostenfunctie op de tak. als de stroom tussen 0 en 10 ligt zal deze stroom volledig door de eerste tak gaan, omdat daarvan de kosten het laagst zijn. De richtingscoëfficiënt van de doelfunctie is daar 2. Pas als de stroom groter wordt dan 10 gaat alles wat boven de 10 uitkomt door de tweede tak, omdat dat het eerstvolgend goedkoopste alternatief is. De richtingscoëfficiënt van de doelfunctie is daar 3. Voor een stroom x van de eerste naar de tweede knoop hebben we dus de volgende kostenfunctie: c(x) = 2x, c(x) = 3x –10 0 x 10 10 x 15 De term -10 komt van de continuïteit van de kostenfunctie. Als we ook nog een tak van de tweede naar de eerste knoop terug opnemen met kostencoëfficiënt 5 en interval [0,7], dan krijgen we de volgende kostenfunctie: c(x) = -5x, c(x) = 2x, c(x) = 3x –10 -7 x 0 0 x 10 10 x 15 In het algemeen kun je zo op een tak een stuksgewijs lineaire kostenfunctie maken door elk lineair interval met een extra tak te associëren. Omdat de kosten door de tak geminimaliseerd worden is er echter wel de restrictie dat de richtingscoëfficiënten niet dalend zijn, m.a.w. de stuksgewijs lineaire functie moet convex zijn (de grafiek is hol naar boven). Netwerkmodellen en Algoritmen 11 4 Bomen Een belangrijke klasse van netwerken wordt gevormd door de bomen. Dat zijn netwerken met een “minimaal” aantal verbindingen tussen knopen. Een boom is een goedkope manier om knopen met elkaar te verbinden, omdat er geen overbodige verbindingen in voorkomen, er is tussen elk paar knopen maar één mogelijke verbinding. Een boom definiëren we dus als een netwerk zonder circuits (cycles, loops). Een boom heet leeg als er geen takken in voorkomen. Een lege boom kan dus wel knopen bevatten! De graad van een knoop is het aantal takken dat die knoop als begin- of eindpunt heeft. Lemma 1: Elke niet-lege boom heeft een knoop met graad gelijk aan 1. Bewijs: Stel dat we een niet-lege boom hebben zonder knopen van graad 1. De graad van een willekeurige knoop is dus gelijk aan 0, of is minstens twee. Omdat de boom niet leeg is, is er een tak. Het eindpunt van die tak heeft niet graad 0, dus minstens 2. Er is dus nog een andere tak die in dat punt begint of eindigt. Volg die tak naar zijn andere knoop. Dit is weer een knoop met graad minstens 2. Zo kun je doorgaan en elke keer weer een knoop vinden. Omdat het totale aantal knopen eindig is, moet je op een gegeven moment op een punt terecht komen dat je al eerder hebt bezocht. Dat kan echter niet omdat je dan een circuit zou hebben gevonden, maar die zijn er niet in de boom. Dit is een tegenspraak: er is dus wél een knoop met graad 1. Een niet-lege boom blijkt zelfs altijd twee punten met graad 1 te hebben: Lemma 2: Elke niet-lege boom heeft twee knopen waarvan de graad gelijk is aan 1. Bewijs: Volgens Lemma 1 is er een punt met graad 1. Stel nu weer dat er verder geen punten met graad 1 zijn. Je kunt dan vanuit het eerste punt weer een pad maken dat zichzelf moet snijden. Dat levert een tegenspraak op. Het is duidelijk dat we deze tendens niet kunnen generaliseren. Een pad tussen n knopen bevat maar twee punten van graad 1, namelijk het begin- en het eindpunt. De andere n-2 punten hebben graad 2. Stelling: Een samenhangend netwerk bevat geen circuits dan en slechts dan als het aantal knopen gelijk is aan het aantal zijden plus 1. Bewijs: “”. We nemen eerst aan dat we een samenhangend netwerk hebben zonder circuits en gaan met inductie bewijzen dat het aantal knopen gelijk is aan het aantal zijden plus 1. Als er maar één knoop is, is het duidelijk dat het aantal zijden gelijk is aan 0, dus dat klopt. Stel nu dat de uitspraak klopt voor netwerken met n knopen en stel dat we een samenhangend netwerk zonder circuits hebben met n+1 knopen. Volgens Lemma 1 is er een knoop met graad 1. Laat deze knoop met zijn verbindende tak even weg en je houdt een samenhangend netwerk over met n knopen. Er zijn dan dus n-1 takken. Voeg nu de knoop en de tak toe en je hebt n = (n+1) –1 takken. We bekijken nu een belangrijk soort boom, namelijk de opspannende boom van een netwerk. Dat is een boom die je krijgt door in een netwerk zoveel mogelijk takken weg te laten, maar wel zodanig dat alle knopen die verbonden waren ook verbonden blijven. “Opspannend” betekent dat alle knopen van het netwerk ook knopen zijn van de opspannende boom en aan de boom verbonden zijn. Voor een (ongericht) netwerk met n knopen definiëren we een opspannende boom als een deel-netwerk dat n-1 takken heeft. Netwerkmodellen en Algoritmen 12 5 Algoritmes voor Max-Flow Een belangrijk type netwerkprobleem is het maximale stroomprobleem (Max-Flow problem). Het probleem bestaat uit het vinden van een zo groot mogelijke stroom door een netwerk van een gegeven beginknoop naar een gegeven eindknoop, waarbij capaciteiten (bovengrenzen voor de stroom) op de takken zijn vastgelegd. Het netwerkje hierboven is daar een simpel voorbeeldje van. Je kunt er 9 eenheden doorheen laten stromen: 5 via n1 en 4 via n2. We zullen hier twee algoritmes behandelen waarmee in het algemeen zo’n maximale stroom kan worden bepaald. 5.1 Max-flow min-cut stelling Als je naar een netwerk kijkt, waarin een maximale stroom wordt gezocht, kun je vaak op voorhand al uitspraken doen over de mogelijke waarden van een stroom door het netwerk. Als je bijvoorbeeld de capaciteiten optelt van alle takken die van de beginknoop uitgaan geeft dit een bovengrens voor de stroom die door het netwerk kan lopen, want alle stroom moet vanuit de beginknoop het netwerk in. In het bovenstaande voorbeeldje levert dat 5 + 7 = 12. Evenzo is de som van de capaciteiten van alle takken die in de eindknoop uitkomen ook een bovengrens. In het voorbeeld: 7 + 4 = 11. De kleinste van de twee waarden die we zo vinden is weer een bovengrens voor de stroom. (In het voorbeeld is dit 11). Wat algemener kunnen we ook een “muurtje” maken dat het netwerk doorsnijdt, zodanig dat de in- en de uitgang van het netwerk aan verschillende kanten van het muurtje liggen. Alle stroom moet van de ingang naar de uitgang en moet de muur passeren. Als je dus de totale capaciteit neemt van alle takken die door het muurtje heen gaan krijg je een bovengrens voor de stroom door het netwerk. Bij het optellen van de capaciteiten moet je opletten dat je alleen de takken telt die de muur in de goede richting snijden, van ingang naar uitgang, want alleen die takken kunnen een positieve bijdrage leveren aan de totale stroom. De takken die terug lopen kunnen de totale stroom alleen verkleinen. Hun grootste positieve bijdrage is 0. Netwerkmodellen en Algoritmen 13 Een muurtje dat het netwerk in tweeën deelt, met de in- en uitgang elk aan verschillende kanten heet ook wel een snede (cut). Wat formeler geformuleerd is een snede een partitie (verdeling) van alle knopen in twee verzamelingen, waarbij de in- en uitgangsknoop in verschillende verzamelingen zitten. Voor een netwerk met n knopen zijn er dus 2n-2 verschillende sneden mogelijk, als je de ingangsknoop in de eerste verzameling doet en de uitgangsknoop in de tweede. Elke andere knoop kun je namelijk telkens in de eerste óf in de tweede verzameling stoppen. Bij elke snede hoort een bovengrens voor de stroom die je vindt door de capaciteiten van de (doorsneden) takken van verzameling 1 naar verzameling 2 op te tellen. Het minimum van deze bovengrenzen over alle mogelijk sneden blijkt een scherpe bovengrens te zijn voor de totale stroom door het netwerk, d.w.z., er is een stroom te vinden die deze bovengrens aanneemt. Dit is de inhoud van de Max-flow min-cut stelling: Er is een stroom (max-flow) in het netwerk die de waarde van de kleinste capaciteitsgrens over alle snedes (min-cut) aanneemt. Voorbeeld: In het simpele netwerk van hierboven zijn vier mogelijke sneden. Dit levert de volgende bovengrenzen op: 5+7 = 12, 7+2+7 = 16, 5+4 = 9, 7+4 = 11. De kleinste bovengrens is 9 en er is inderdaad een stroom 9 door het netwerk mogelijk: 5 eenheden door de bovenste twee takken en 4 door de onderste twee. 5.2 Algoritme van Ford en Fulkerson Hoe kun je nu een maximale stroming door een netwerk bepalen? Een voor de hand liggend idee is het volgende: Je zoekt in het netwerk een pad van begin- naar eindpunt, kijkt hoeveel er langs dit pad kan (het minimum van de capaciteiten langs dit pad) en laat dit er vervolgens doorheen lopen. Je kunt alle capaciteiten langs het pad dan met het bedrag van de stroom verlagen. Vervolgens zoek je weer een ander pad, etc. Dit kun je doen totdat er geen pad meer mogelijk is waarlangs nog stroom kan lopen. Heb je dan de maximale stroom gevonden? Het antwoord is helaas: Nee! Kijk maar naar het volgende voorbeeld: Netwerkmodellen en Algoritmen 14 Kies als eerste pad: ns n1 n2 nt. Langs dit pad kan maximaal 2 stromen. Kies vervolgens: ns n1 nt. Hierlangs kan nog 3 stromen. Tenslotte kun je langs ns n2 nt nog 2 eenheden laten stromen. Nu lijkt het netwerk verzadigd. Vanuit s kan er niets meer naar n1, die stroom is al maximaal. Er kan nog wel iets naar n2, maar dat kan niet naar nt omdat de stroom van n2 naar nt al maximaal is en de pijl van n2 naar n1 de verkeerde kant op staat. We hebben nu een totale stroom van 7 gevonden, maar die is niet maximaal. We zagen namelijk eerder dat de minimale snede van dit netwerk 9 oplevert en dat je inderdaad een stroom 9 kunt realiseren door 5 eenheden langs n1 te sturen en 4 eenheden langs n2. Waarom werkt dit algoritme niet? Dat komt omdat we door de keuze van het eerste pad twee eenheden die over n1 kwamen de verkeerde kant uit hebben gestuurd, via n2, en daardoor capaciteit van de tak n2 nt hebben opgesnoept, die beter gebruikt had kunnen worden voor de stroom over ns n2 nt. Ook kun je zeggen dat we onszelf gefopt hebben door te denken dat er vanuit n2 geen stroom naar n1 kan lopen omdat de pijl de verkeerde kant opstaat. Je kunt namelijk wél een stroom ter grootte 2 van n2 naar n1 laten lopen, omdat er al een stroom van n1 naar n2 liep. Die twee eenheden kun je terugnemen, waarmee je feitelijk een extra stroom van n2 naar n1 hebt laten lopen. Effectief komt het er op neer dat er dan geen stroom meer loopt van n1 naar n2, je hebt de eerder ingestelde stroom in tweede instantie teruggenomen. Dat betekent dat het mogelijk is om nog een stroom ter grootte 2 te laten lopen over ns n2 n1 nt. Dit brengt de stroom in totaal op 9, en dat is wel maximaal. Om duidelijker te maken hoe het zit met het “terugnemen” van stroom kun je het beter hebben over de residuele graaf (residual graph) die hoort bij een stroom in de graaf. In deze graaf wordt elke gerichte tak in het originele netwerk vervangen door twee takken met tegengestelde richtingen met elk een capaciteit. De capaciteiten geven aan hoeveel er maximaal nog in die richting kan stromen. Een tak met een capaciteit van 5 waardoor momenteel 3 eenheden stromen wordt dus vervangen door een tak met capaciteit 2 en een tak terug met capaciteit 3. Als er nog niets doorheen stroomt blijft het een tak met capaciteit 5 (de tak terug heeft capaciteit 0), en als er een maximale stroom doorheen gaat is er alleen een tak terug met capaciteit 5 (de tak heen heeft capaciteit 0). Ter illustratie volgen hieronder Netwerkmodellen en Algoritmen 15 de verschillende residuele grafen als gevolg van de drie stappen die we eerder hebben beschreven. Je ziet aan de laatste residuele graaf onmiddellijk dat er nog 2 eenheden over het pad s 2 1 t kunnen stromen. Dit levert de volgende residuele graaf: In deze graaf is er geen stroom meer mogelijk van s naar t. Dat betekent dat het algoritme hier stopt. We hebben nu een stroom van in totaal 9 eenheden gevonden van s naar t en dat is maximaal. De oplossing die we nu gevonden hebben is: De oplossing krijg je door de capaciteiten van de takken uit de residuele graaf te nemen die tegengesteld lopen aan takken uit de originele graaf en deze waarden als stroom bij de bijbehorende tak in de originele graaf te nemen. Het algoritme dat we nu aan de hand van een voorbeeld hebben beschreven is het algoritme van Ford en Fulkerson (1956): Start met een toelaatbare stroom (meestal stroom 0 op alle takken). while er is nog een pad waar een stroom kan lopen do Kies een pad waarlangs nog een stroom kan lopen. Laat de maximale stroom langs dit pad lopen. Pas de residuele graaf aan. Netwerkmodellen en Algoritmen 16 od Een illustratie met behulp van Java applets is bijvoorbeeld te vinden op http://www-b2.is.tokushima-u.ac.jp/~ikeda/suuri/maxflow/Maxflow.shtml 5.3 Convergentieproblemen in Ford-Fulkerson Het algoritme van Ford-Fulkerson is zeer geschikt om met de hand kleine voorbeeldjes op te lossen. Uiteindelijk is het natuurlijk bedoeld om grotere problemen op een computer aan te pakken. Dat betekent dat de stap “zoek een pad van begin- naar eindpunt” ook geautomatiseerd moet worden. In principe levert dat een zoektocht op via knopen over takken, waarbij met backtracking een geschikt pad moet worden gevonden. Hoewel het algoritme in de praktijk best goed voldoet zijn er toch een aantal problemen. De eindigheid van het algoritme is duidelijk als alle capaciteiten geheeltallig zijn. Bij elke stap in het proces treedt namelijk een verbetering op en wordt de totale stroom een geheel aantal eenheden groter. Omdat de kleinste snede en dus de maximale stroom ook geheeltallig is, bereikt het algoritme deze waarde in een eindig aantal stappen. Dit argument werkt niet als de capaciteiten geen rationale verhoudingen hebben. Het algoritme hoeft dan ook niet eindig te zijn. Wel convergeert het proces natuurlijk, omdat de stroom na elke iteratie groter wordt en er een bovengrens is. Helaas hoeft deze limietstroom niet de maximale stroom in het netwerk te zijn. Een ander probleem is, dat er bij veel strategieën om tot een mogelijke stroom van beginnaar eindpunt te komen voorbeelden te verzinnen zijn waarvoor het Ford-Fulkerson algoritme erg veel stappen doet voor het vinden van de beste oplossing. Als voorbeeld bekijken we het onderstaande netwerk. Als strategie om een toelaatbaar pad te bepalen kiezen we backtracking, waarbij om de andere iteratie eerst de knopen met de laagste, resp. hoogste index worden afgezocht. De capaciteiten op de takken zijn allemaal M, een erg groot getal, behalve op de tak 3 4, daar is de capaciteit 1. Je ziet eenvoudig de beste oplossing voor dit probleem: Er moet een stroom M door de bovenste takken lopen en ook een stroom M door de onderste takken. Dat deze stroom van 2M maximaal is zie je met behulp van een snede. Het algoritme van Ford-Fulkerson doet met de gekozen strategie het volgende. Eerst wordt een toelaatbaar pad gezocht met minimale indices. Dat betekent dat er vanuit 1 wordt gekozen voor 1, vervolgens voor 3 (ligt vast), dan voor 4, vervolgens voor 2 en dan naar t. Over het pad ns n1 n3 n4 n2 t kan maximaal 1 eenheid lopen. De tweede iteratie kiest de hoogste indices en levert het pad ns n6 n4 n3 n5 nt. Ook over dit pad kan 1 eenheid stromen. Het is duidelijk wat er gebeurt: Bij elke iteratie wordt de stroom met 1 eenheid verhoogd. Dit betekent dat het 2M iteraties duurt voordat je de maximale stroom hebt gevonden. Dat staat in schril contrast met de twee iteraties die nodig zijn als je de paden geschikt kiest. Netwerkmodellen en Algoritmen 17 Om deze convergentieproblemen te vermijden werd door Edmonds en Karp (1972) voorgesteld om altijd het pad met de grootst mogelijk stroom op te zoeken, in plaats van de eerste de beste. Dit brengt natuurlijk wel meer rekenwerk met zich mee. Later (Gabow 1985 en Ahuja/Orlin, 1991) werd aangetoond dat het niet noodzakelijk is om de grootst mogelijke stroom op te zoeken, als de stroom maar “groot genoeg” is in een of andere zin. Het rekenwerk voor het zoeken van een geschikt pad wordt hierdoor minder. Voor gehele capaciteitsgrenzen op het netwerk is de rekentijd van de orde O(n4log U), waarbij n het aantal knopen is en U de grootste capaciteitsgrens in het netwerk. 5.4 Algoritme van Karzanov Het idee van het algoritme van Karzanov is dat je, in plaats van één pad per keer te gebruiken, zoals bij Ford-Fulkerson, ook de stroom op meer paden tegelijk kunt aanpassen. We zullen het algoritme bekijken aan de hand van een simpel voorbeeld: De minimale snede van dit netwerk ligt om knoop nt, en heeft een waarde van 3 + 5 =8. Er is ook eenvoudig een maximale stroom 8 te maken. Een iteratie uit het algoritme van Karzanov bestaat telkens uit de volgende drie stappen: 1. Maak een zogenaamd gelaagd netwerk. Dat zijn een aantal paden die van beginpunt naar eindpunt lopen en waarover de stroom nog kan worden aangevuld. Dit werkt als volgt: Eerst verdeel je het netwerk in lagen. Laag 0 bevat de beginknoop. Laag k bevat de knopen die via k takken van het residuele netwerk (takken waarover nog stroom kan lopen) verbonden zijn met de beginknoop. Uit het residuele netwerk worden alle takken weggelaten die niet naar een volgende laag lopen, en ten slotte worden alle takken en knopen weggelaten die niet op een pad van begin- naar eindpunt liggen. 2. “Advance”: Loop de paden van begin- naar eindpunt af en laat er per tak de maximale hoeveelheid stroom doorlopen. Alles wat er in een knoop komt mag door, tenzij het beperkt wordt door de capaciteit van de uitgaande tak. Er is soms een vrijheid hoe je de stroom over de uitgaande takken verdeelt. Er ontstaat een zogenaamde “preflow”. Dat is nog geen gewone stroom, omdat er in de knopen geen balans hoeft te zijn. Er kan meer ingaan dan er uitkomt. Dat is ook meteen de definitie van een preflow: Een toewijzing van stroomwaarden aan de takken zodanig dat in elke knoop minstens evenveel stroom gaat als er uit komt. Hierin kan ook keuzevrijheid zijn. 3. “Balance”. Om van deze preflow een echte stroom te maken, worden vanuit het eindpunt naar het beginpunt alle knopen gebalanceerd: de stroom op de ingaande tak wordt zodanig gereduceerd dat er balans in de knoop ontstaat. Netwerkmodellen en Algoritmen 18 In het voorbeeld van hierboven werkt dit als volgt. Eerst geef je de lagen aan en laat je alle overbodige takken weg. In ons geval verdwijnt tak n1 n2, omdat die in laag 1 blijft: Vervolgens laten we grootst mogelijke preflow door dit netwerk lopen (a): Door de tak ns n1 loopt een stroom ter grootte van de capaciteitsgrens: 6. Door de capaciteitsgrens 3 op tak n1 nt blijft er op deze tak van de ingaande stroom 6 maar 3 over. Dit levert een preflow. De stroom is in n1 niet gebalanceerd. Vervolgens moet het netwerk gebalanceerd worden (b). Dit betekent dat de stroom op ns n1 moet worden gereduceerd tot 3, om knoop n1 in balans te krijgen. De andere knopen zijn al in balans. We hebben nu van de preflow een stroom gemaakt en deze stroom is het resultaat van de eerste iteratie. We maken nu de gereduceerde graaf van deze stroom (a): Netwerkmodellen en Algoritmen 19 Er zijn in dit gereduceerde netwerk 4 lagen. Vervolgens laten we weer alle takken weg die niet naar een volgende laag lopen en krijgen het netwerk (b). De preflow die hierbij hoort is hieronder te zien (a) Door de preflow te balanceren ontstaat een stroom (zie (b) hierboven). In het gereduceerde netwerk dat ontstaat (hieronder (b)) zie je dat er nu geen pad meer is met een stroom van begin- naar eindpunt. De oplossing die door het algoritme is gevonden staat in (a). Bij een goede implementatie heeft het algoritme van Karzanov een complexiteit van O(n3). De meeste state-of-the-art algoritmes zijn tegenwoordig gebaseerd op het concept preflow. 5.5 Toelaatbare stromen De algoritmes die we hebben bekeken voor Max-Flow zijn gebaseerd op het successievelijk verbeteren van een bestaande stroming. Dat is geen probleem als er op de takken alleen maar bovengrenzen zijn gegeven voor de stromen. We kunnen dan altijd starten met de nulstroom, een stroom 0 op alle takken. Dat is een toelaatbare stroom en tijdens het algoritme wordt ervoor gezorgd dat de stroom toelaatbaar blijft. Als er ook ondergrenzen voor de stromen gegeven zijn zitten we met een probleem, want er moet dan eerst een toelaatbare stroom worden gevonden. Dat kan wel eens tegenvallen voor een groot netwerk. We zullen nu zien hoe je een toelaatbare stroom kunt vinden. Als xij de stroom van knoop i naar knoop j is, dan gelden er in het algemeen boven- en ondergrenzen voor deze stroom: lij xij uij Verder moet in elke knoop i de balansvergelijking gelden: Netwerkmodellen en Algoritmen x x ij j ji 20 0 j Als er geen ondergrenzen zijn: lij = 0, dan is de nulstroom xij = 0 (voor alle i en j) toelaatbaar. Als dat niet zo is halen we de volgende truc uit: We definiëren nieuwe variabelen xij’ = xij - lij Het effect van dit verschuiven van de variabelen is dat de ondergrenzen verdwijnen maar dat in de knopen kunstmatige bronnen en putten ontstaan: x j ij ' x ji ' l ji lij : bi j j j 0 xij’ uij - lij Dit is niet meer dan een herverdeling, want alle bronnen en putten die zo ontstaan heffen elkaar in totaal gezien op. Het idee is nu dat we deze artificiële tekorten en overschotten gaan koppelen aan een superbron ns* en een superput nt*, die we als twee extra knopen aan het originele netwerk zullen toevoegen. Als bi > 0 dan wordt ni verbonden met ns*, met capaciteit bi. Als bi < 0 dan wordt ni verbonden met nT*, met capaciteit -bi. De oorspronkelijke takken uit het netwerk krijgen als capaciteit uij - lij. Als voorbeeld bekijken we het volgende netwerk, waar bij elke tak de ondergrens en de bovengrens voor de stroom staat aangegeven. De tak van eind- naar beginknoop geeft aan dat we een maximale stroom zoeken. De capaciteit M heeft een grote waarde, groter dan de totale stroom kan zijn (bijvoorbeeld de waarde van een snede). Door de ondergrenzen is een stroom 0 op alle takken niet toelaatbaar. De balansvergelijkingen in dit netwerk zijn: Knoop ns: – xts + xs1 + xs2 = 0. Knoop n1: – xs1 + x1t + x12 = 0. Knoop n2: – xs2 – x12 + x2t = 0. Knoop nt: – x1t – x2t + xts = 0. Nu gaan we over op nieuwe variabelen : xts = xts’. xs1 = xs1’ + 1. xs2 = xs2’ + 1. x1t = x1t’. x12 = x12’ + 2. x2t = x2t’ + 2. Met deze nieuwe variabelen krijgen we de volgende balansvergelijkingen : Knoop ns: – xts’ + xs1’ + xs2’ = – 2 = bs. Knoop n1: – xs1’ + x1t’ + x12’ = – 1 = b1. Netwerkmodellen en Algoritmen 21 Knoop n2: – xs2’ – x12’ + x2t’ = 1 = b2. Knoop nt: – x1t’ – x2t’ + xts’ = 2 = bt. Het uitgebreide netwerk met de superbron en de superknoop ziet er nu als volgt uit : In dit netwerk moeten we een toelaatbare stroom vinden, met de kanttekening dat de superknoop 3 eenheden moet leveren en de superput 3 eenheden moet afvoeren. We gaan nu wegen van de superbron naar de superput zoeken waar stroom doorheen kan. Als eerste pakken we ns* nt ns n1 nt*. Over dit pad kan 1 eenheid stromen. Het pad ns* n2 nt ns nt* is ook goed voor 1 eenheid. Ten slotte kan er nog 1 eenheid langs ns* nt ns nt*. De drie paden staan getekend in de volgende figuur: Hiermee hebben we een stroom gevonden die aan alle grenzen voldoet. Vervolgens schrijven we op wat dit voor het oorspronkelijke netwerk betekent. Hierbij moet worden gecorrigeerd voor de verschuivingen die we in de stromen hebben geïntroduceerd door over te gaan op nieuwe variabelen: Een toelaatbare stroom in het netwerk die we zo hebben geconstrueerd is te zien in het volgende plaatje: Netwerkmodellen en Algoritmen 22 Netwerkmodellen en Algoritmen 23 6 Netwerkalgoritmen In het voorgaande hebben we veel moeite gedaan om optimaliseringsproblemen te formuleren als een netwerkprogrammeringsprobleem (kortweg NP). Een reden daarvoor is dat er voor netwerkproblemen snellere algoritmen zijn dan voor gewone LP problemen. We zullen hier een aantal algoritmen bespreken aan de hand van het volgende NP probleem: Uitgeschreven als LP-probleem ziet het er als volgt uit: min {cTx | Ax = d, a x b}. Netwerkmodellen en Algoritmen 24 7 Literatuur C. Roos, Netwerkmodellen en algoritmen, dictaat TU Delft, 1991. H.A. Eiselt and C.-L. Sandblom, Integer Programming and Network Models, Springer, 2000, F. Glover, D. Klingman, and N.V. Phillips, Network Models in Optimization and their Applications in Practice. Wiley, 1992.