Inleiding Natuurkundige Informatica
dr. H.J.W. Spoelder Faculteit Natuurkunde en Sterrenkunde Vrije Universiteit
Inhoud
Hoofdstuk 1 Historisch Overzicht en Terreinafbakening 1.1 1.2 1.3 1.4 1.5
Hoofdstuk 2 Enige basisbegrippen 2.1 2.2 2.3 2.4 2.5
21
Inleiding ..................................................................................................................................................... 22 Sensors en discretisatie. ............................................................................................................................. 23 Discrete wiskunde: Boolse Algebra ........................................................................................................... 27 Logicals in programmeertalen ................................................................................................................... 30 Informatie, Codes en Compressie .............................................................................................................. 33
Hoofdstuk 3 Enige Problem Solving Environments 3.1 3.2 3.3 3.4
3.5
3.6
3.7
3.8 3.9
5
Historisch overzicht ..................................................................................................................................... 6 Exponentiële groei ....................................................................................................................................... 9 Software ..................................................................................................................................................... 11 Computers en wetenschap.......................................................................................................................... 13 Natuurkundige Informatica ........................................................................................................................ 19
38
Inleiding ..................................................................................................................................................... 39 Enige basisbegrippen van talen.................................................................................................................. 43 User interface ............................................................................................................................................. 46 C/Fortran .................................................................................................................................................... 48 3.4.1 Case study ........................................................................................................................................48 3.4.2 Achtergrondinformatie .....................................................................................................................49 3.4.3 Uitwerking Case study .....................................................................................................................51 Mathematica............................................................................................................................................... 52 3.5.1 Case Study........................................................................................................................................52 3.5.2 Achtergrond informatie ....................................................................................................................53 3.5.3 Een eerste Tour van Mathematica ....................................................................................................55 3.5.4 Uitwerking van de case studies ........................................................................................................55 3.5.5 Visualiseren van de oplossing ..........................................................................................................56 3.5.6 Gevaren van het gebruik van Mathematica......................................................................................57 ScilImage ................................................................................................................................................... 58 3.6.1 Case Study........................................................................................................................................58 3.6.2 Achtergrond ScilImage ....................................................................................................................59 3.6.3 Uitwerking case study. .....................................................................................................................61 HTML/Netscape......................................................................................................................................... 62 3.7.1 Case study ........................................................................................................................................62 3.7.2 Achtergrondinformatie .....................................................................................................................62 3.7.3 Uitwerking van de case study...........................................................................................................64 Java............................................................................................................................................................. 64 Samenvatting.............................................................................................................................................. 67
Hoofdstuk 4 Datatypes en Datastructuren
68
4.1 Inleiding ..................................................................................................................................................... 69 4.2 Datatypes.................................................................................................................................................... 70 4.2.1 Getallen stelsels................................................................................................................................70 4.2.2 Enumeraties ......................................................................................................................................71 4.2.2.1 Characters ......................................................................................................................... 72 4.2.2.2 Integers ............................................................................................................................. 74 4.2.3 Fixed point........................................................................................................................................75 4.2.4 Floating Point ...................................................................................................................................75 4.2.5 Rekenen in perspectief .....................................................................................................................79 4.3 Datastructuren ............................................................................................................................................ 79 4.3.1 Introductie ........................................................................................................................................79 4.3.2 Vector/Array.....................................................................................................................................81 4.3.3 Gelinkte lijst .....................................................................................................................................84 4.3.3.1 Gelinkte lijst: element access ........................................................................................... 85 4.3.3.2 Gelinkte lijst: invoegen van elementen ............................................................................ 87
Inleiding Natuurkundige Informatica
2
1997
Inhoud
4.3.4 Binaire bomen ..................................................................................................................................88 4.3.5 Stack .................................................................................................................................................91 4.4 Voorbeelden ............................................................................................................................................... 94 4.4.1 Bresenham algoritme........................................................................................................................94 4.4.2 Numerieke bepaling van afgeleiden .................................................................................................96 4.4.3 Command interpreter........................................................................................................................98 4.4.4 Polynoom representatie ....................................................................................................................99
Hoofdstuk 5 Algoritmen
101
5.1 Inleiding ................................................................................................................................................... 102 5.2 Orde van een algoritme ............................................................................................................................ 106 5.3 Recursieve algoritmen.............................................................................................................................. 108 5.3.1 Boom van Pythagoras.....................................................................................................................110 5.3.2 Adaptieve quadratuur .....................................................................................................................111 5.4 Random getallen ...................................................................................................................................... 115 5.5 Verdeel-en-heers algoritmes .................................................................................................................... 119 5.6 Dynamisch programmeren ....................................................................................................................... 122 5.7 Lineaire stelsels........................................................................................................................................ 124 5.7.1 Gauss eliminatie .............................................................................................................................125 5.7.2 Jacobi methode ...............................................................................................................................126 5.7.3 Numerieke stabiliteit ......................................................................................................................127 5.7.4 Toepassing: Weerstandsnetwerk ....................................................................................................130 5.8 Conclusie.................................................................................................................................................. 132 5.9 Appendix: Performance meting ............................................................................................................... 132 5.9.1 Tijd .................................................................................................................................................132 5.9.2 C/Unix ............................................................................................................................................133 5.9.3 Mathematica ...................................................................................................................................136 5.9.4 Operaties.........................................................................................................................................136 5.9.5 Benchmark maten...........................................................................................................................137 5.9.5.1 Integer Benchmarks........................................................................................................ 137 5.9.5.2 Floating Point Benchmarks ............................................................................................ 137
Hoofdstuk 6 Architecturen
139
6.1 Inleiding en Overzicht.............................................................................................................................. 140 6.2 Scalaire architectuur................................................................................................................................. 140 6.2.1 CPU ................................................................................................................................................143 6.2.1.1 CISC ............................................................................................................................... 143 6.2.1.2 RISC ............................................................................................................................... 144 6.2.1.3 Pipelining........................................................................................................................ 144 6.2.2 Geheugen........................................................................................................................................146 6.2.2.1 Basis begrippen .............................................................................................................. 146 6.2.2.2 Registers ......................................................................................................................... 148 6.2.2.3 Cache .............................................................................................................................. 148 6.2.2.4 (Virtueel) geheugen ........................................................................................................ 150 6.2.2.5 Disk................................................................................................................................. 152 6.2.2.6 Tape ................................................................................................................................ 153 6.2.3 I/O...................................................................................................................................................154 6.2.3.1 Topologie........................................................................................................................ 155 6.2.3.2 Voorbeelden. .................................................................................................................. 156 6.3 High Performance Computing ................................................................................................................. 157 6.3.1 Super Scalar architecture................................................................................................................157 6.3.2 Vector Processoren (SIMD) ...........................................................................................................158 6.3.3 Parallel computing (MIMD)...........................................................................................................162 6.4 Netwerken ................................................................................................................................................ 165 6.4.1 Enige basis begrippen.....................................................................................................................165
Inleiding Natuurkundige Informatica
3
1997
Inhoud
6.4.2 TCP/IP............................................................................................................................................166 6.4.3 Network layer: Ethernet .................................................................................................................169 6.5 Computer Instrument koppeling .............................................................................................................. 170 6.5.1 IEEE-488.1: hardware ....................................................................................................................170 6.5.2 IEEE-488.1: Software ....................................................................................................................173 6.5.3 IEEE-488 en verder ........................................................................................................................173 6.5.4 SCPI Instrument model ..................................................................................................................174
Referenties Trefwoorden Trefwoordenregister
Inleiding Natuurkundige Informatica
175 176 179
4
1997
Hoofdstuk 1:Historisch Overzicht en Terreinafbakening
Historisch Overzicht en Terreinafbakening
1.1
Historisch overzicht
Historisch overzicht
De ENIAC die tussen 1943 en 1946 gebouwd werd en operationeel geweest is tussen 1946 en 1955, wordt door velen gezien als het begin van het computertijdperk1. In Figuur 1 krijgt men een impressie van de grootte van deze machine: de kamer vol apparatuur bevat ongeveer 18.000 buizen en een geheugen van 20 bits (zie ook tabel 3). Het werk aan computers, in de zin van rekenautomaten, gaat overigens (veel) verder terug. Bekend, maar zeker niet het enige voorbeeld, is het werk van Charles Babbage aan zijn zogenaamde ‘analytical engine’ dat rond 1845 plaatsvond. In deze machine werden alle functies mechanisch afgehandeld. Het werk van Leonardo da Vinci aan de eerste antropomorfe robot, dat hij tussen 1495 en 1497 uitvoerde, kan nog steeds wedijveren met courante ontwerpen. In dit ontwerp was ook al sprake van een programma voor de besturing van de robot2. Het ontwerp voor de beweging van de robot was overigens minstens even revolutionair als het idee van een programma. Bestaat er al verwarring over de exacte ’geboorte datum’ van de eerste computer, dan zal het geen verwondering wekken dat een exacte datering van het begin van ’informatica’ als discipline en van de toegepaste informatica’s ook aan verschil van inzicht onderhevig is. Vragen of elektronica, digitale signaalverwerking
Figuur 1
Foto van de ENIAC (Electronic Numerical Integrator and Computer) die het begin vormde van het computertijdperk. Meer informatie is te vinden op de ENIAC homepagina (http:// www.seas.upenn.edu/~museum)
en discrete wiskunde nu als ’informatica ante rem’ gekenmerkt moeten worden spelen hierin mee. Het is echter duidelijk wanneer men kijkt naar de mijlpalen in de informatica dat er een duidelijke verdichting optreedt in de tweede helft van de 20ste eeuw. Een illustratie hiervan ziet men in tabel 1. Eén van de tendensen uit die tabel is miniaturisatie. Men krijgt men hiervoor gevoel als men de Cray-I (Figuur 2), ontworpen door de inmiddels legendarische Seymour Cray vergelijkt met de ENIAC. De Cray-I, beschikbaar vanaf 1975, was de eerste zogenaamde supercomputer die gebaseerd was op het principe van vectorprocessing. De clock cycle van de Cray was voor die tijd revolutionair, 12.5 ns (80 MHz), terwijl het maximale geheugen 4 Mwoorden bedroeg (een woord bij Cray is 64 bits). De snelheid had wel zijn prijs: het door het geheugen geconsumeerde elektrische vermogen bedroeg, bij maximaal geheugen, 118 kWatt, ongeveer 1.
Geïnteresseerden wordt aangeraden eens te kijken in het virtuele computer museum: http://www.comlab.ox.ac.uk/archives/other/museums/computing.html 2. Zie verder: "In the footsteps of Leonardo", M. E. Rosheim, IEEE Robotics and Automation, Vol 4, No. 2, pp 12 - 14, 1997
Inleiding Natuurkundige Informatica
6
1997
Historisch Overzicht en Terreinafbakening
Historisch overzicht
Jaar
Gebeurtenis
1946
ENIAC operationeel (zie figuur 1)
1947
Transistor (Bardeen & Brittain, Bell Labs)
1951
Univac I, eerste commerciële machine
1952
IBM 701, eerste serie produktie (19 in totaal)
1953
Core geheugen uitgevonden
1956
Harddisk uitgevonden (IBM)
1959
IBM 7090, eerste transistor machine
1961
Eerste IC (Fairchild)
1964
IBM 360 mainframe
1964
CDC-6600 (wetenschappelijke machine van Seymour Cray)
1965
DEC PDP-7 mini-computer
1970
Illiac IV, eerste parallelle machine
1976
Cray-I, eerste super-computer (zie Figuur 2)
1976
Ethernet (Xerox), Token ring (IBM)
1978
16-bits micro processor
1980
“Introduction to VLSI design”, Mead en Conway
1980
32 bits microprocessor Motorola 68000
1981
Introductie IBM PC
1981
Introductie muis gebaseerde user interface (Xerox)
1982
Apollo 6000, eerste werkstation met een grafische user interface (GUI)
1984
Apple Macintosh: het eerste kleine systeem met een GUI
1990
Connection machine, parallel systeem 64000 processoren
1993
Introductie PowerPC (IBM)
tabel 1
Overzicht van enige relevante ontwikkelingen in de hardware
evenveel als de ENIAC. Een overeenkomst tussen beide machines is het aantal verkochte systemen: gering. Voor typische Cray systemen is het aantal tussen de 10 en 100. De massa produktie van systemen begint pas met de zogenaamde PDP systemen, die door Bell ontworpen werden: over een periode van 5 jaar werden er van de PDP-8 systemen 50.000 verkocht. Voor de toenmalige tijd een ongekend aantal. De introductie van de PC, gebaseerd op zogenaamde microprocessor technologie, voert deze tendens naar zijn logische extremum. Vanaf die tijd wordt de computer een gebruiksartikel. Ter vergelijking met de eerder genoemde verkoop cijfers van de Cray staan in tabel 2 de wereldwijde verkoopcijfers van microprocessoren in 1995 en 1996. Per dag worden er nu 250.000 chips verkocht! Dat getal is overigens niet identiek aan het aantal verkochte computersystemen want microprocessoren worden in toenemende mate geïntegreerd in een veelheid van systeem. Een, nog steeds ietwat dure, auto als een Mercedes Benz heeft tegenwoordig twee 2 Mbit netwerken om de verschillende elektronische componenten van de auto met elkaar te verbinden. De massa produktie is van essentieel belang voor de snelle ontwikkeling van de computermarkt.
Inleiding Natuurkundige Informatica
7
1997
Historisch Overzicht en Terreinafbakening
Historisch overzicht
1995
1996
Toename
Aantal (106)
59
80
35%
Omzet (109 $)
10
12.7
27%
tabel 2
Overzicht van de verkopen, in aantallen en dollars, van de chips. Het marktaandeel hierin van Intel is volstrekt dominant.
De kosten die geassocieerd zijn met de ontwikkeling van hardware moeten niet onderschat worden; voor een ’chip’ lopen zij in de miljarden dollars. Vandaar dat bijvoorbeeld voor de ontwikkeling van de PowerPC chip (zie tabel 1) IBM, Motorola en Apple hebben samengewerkt. Minder bekend dan chips maar minstens even invloedrijk is kennis van hardware en software. Het leerboek van Mead en Conway, "Introduction to VLSI design", betekende de start van het VLSI tijdperk en kwam als een antwoord van de Amerikanen op de aankondiging van de Japanners van de zogenaamde ’vijfde generatie’ systemen (die overigens nooit echt gekomen is). Een gevoel voor de aard van de veranderingen die plaats gehad hebben kan men krijgen als men de ENIAC vergelijkt met de machine waarop grote delen van dit dictaat getypt zijn, een IBM ThinkPad 360C, en een state-of-the-art parallelle machine zoals die op het ogenblik bij het rekencentrum SARA3 aanwezig is. Een aantal karakteristieken wordt gegeven in tabel 3.
Figuur 2
De machine die als het begin wordt gezien van de supercomputers is de Cray-I. Zijn ronde vorm is vermoedelijk even karakteristiek geweest als zijn performance.
De eerste toepassing waarvoor de ENIAC gebruikt werd was het waterstofbom (Manhattan) project. Voor een stelsel differentiaal vergelijkingen dat ten behoeve van dit project opgelost moest worden dienden ongeveer 8 miljoen vermenigvuldigingen en een vergelijkbaar aantal optellingen uitgevoerd te worden. Op een Pentium processor van tegenwoordig is dat hoogstens een seconden kwestie, op de ENIAC nam het 15 uur in beslag waarvan 20% gebruikt werd voor test runs. Een Mean Time Between Failure (MTBF) die schril afsteekt bij getallen die in tabel 5 gegeven worden. Desondanks werd de berekening een factor 1000 sneller uitgevoerd dan tot dan toe mogelijk was. Enigszins krom kan men dit omrekenen naar een snelheid van ongeveer 0.0002 MFlop. Voor het einde van deze eeuw verwacht men de Tera Flop (1012) grens te zullen halen (sommige leveranciers claimen nu al deze grens gepasseerd te zijn). Een prestatie toename in een kleine 50 jaar van 1015. In geen enkele andere vorm van menselijke activiteit kan men een dergelijke toename van prestatie vinden. Het antwoord op de (logische) vraag naar het specifieke van deze ontwikkeling is moeilijk te geven. Met de nodige simplificatie kan men zeggen dat de wisselwerking tussen massaproduktie en technologische mogelijkheden de drijvende kracht vormt achter de toename van hardware mogelijkheden. 3.
SARA (Stichting Academisch Rekencentrum Amsterdam) is het universitaire rekencentrum dat door VU en UvA gebruikt wordt, zie ook http://www.sara.nl
Inleiding Natuurkundige Informatica
8
1997
Historisch Overzicht en Terreinafbakening
Karakteristiek
IBM ThinkPad 360C
ENIAC
IBM SP/2 (74 processor)
Jaar van produktie
1946
1994
1994
Oppervlak
233 m2
0.0604 m2
5.02
Power consumptie
140 kWatt
30 Watt
-
Gewicht
30 000 kg
2.8 kg
500 - 635 kg
Memory
20 bits
20 Mbyte
70 * 256 Mb + 4 * 512 Mb
Storage
-
400 Mbyte
16 * 9 Gb
CPU
18000 buizen
Intel 80486 800.000 transistoren
IBM Power2 Chip 2.8 miljoen transistoren
tabel 3
1.2
Exponentiële groei
Vergelijking van één van de eerste computers met state-of-the-art systemen.
Exponentiële groei
Ervaring in de afgelopen 30 jaar heeft duidelijk gemaakt dat de kosten van computerapparatuur afgenomen zijn met ongeveer 20-30% per jaar. Dat is een factor van 105 over een periode van 30 jaar. Voor kosten kan men denken aan zaken als prijs/prestatie verhouding, kosten per bit geheugen enz. enz. In 1965 voorspelde G.E. Moore dat het aantal componenten op een chip jaarlijks zou toenemen met een factor 1.5 tot 2. Sedertdien spreekt men over deze groei als de ’Wet van Moore’. Het oppervlak van de chip blijft overigens constant op ongeveer 1 cm2, op de reden hiervoor komen wij nog terug. Een duidelijke illustratie van de exponentiële groei van Moore is te zien in de karakteristieken van Intel processoren (zie tabel 4) over de afgelopen 25 jaar. Introductie
4004
1971
2,300
0.06
8080
1974
6,000
0.6
8086
1978
29,000
0.8
80286
1982
134,000
2.7
80386
1985
275,000
6.0
80486
1991
1,185,000
13.0
Pentium
1993
3,100,000
100.0
Pentium Pro
1995
5,500,000
440.0
tabel 4
Aantal Transistoren
Snelheid (MIPS)
Naam
Ontwikkeling van de Intel processoren wat betreft complexiteit en snelheid (bron Intel)
Uit deze gegevens, en andere data die voorhanden is, kan worden afgeleid dat de computer industrie een perfect voorbeeld is van een exponentiële technologische groei. Een dergelijke groei kan gemodelleerd worden als T (t) = T (t 0) × r
Inleiding Natuurkundige Informatica
9
t – t0
(1)
1997
Historisch Overzicht en Terreinafbakening
Exponentiële groei
Exponentiële groei kan alleen gevonden worden als aan een aantal voorwaarden voldaan is: 1) het produkt moet al een groot aantal jaren vervaardigd worden, 2) het produktie proces moet voortdurend verbeterd worden door opgedane ervaring, 3) het ontwerp moet voortdurend aangepast worden. Een gevolg van deze voorwaarden is dat het produkt een korte economische cyclus heeft en dat de groei niet beperkt wordt door fysieke grenzen (zoals bij vliegtuigmotoren en auto’s het geval is). Een vaak aangehaalde illustratie van de exponentiële groei is de ontwikkeling op het gebied van geheugen technologie. Voor 1952 was er sprake van een rudimentaire geheugen technologie: kwik vertragingslijnen, magnetische drums en de zogenaamde ’Williams buizen’ werden gebruikt. Vanaf 1952 schakelt men over op de magnetische kernen (core memory). In 1960 bedroeg de prijs per bit 20 dollarcent en nam sedert dien met 19% per jaar af. In 1974 vond een belangrijke technische innovatie plaats met de overschakeling naar halfgeleider geheugen. Als eerste kwam de 4 Kbit chip op de markt. De kosten hiervan bedroegen 1 dollarcent per bit. (Overigens moet opgemerkt worden dat ’core’ geheugen nog lange tijd populair geweest is vanwege twee redenen. Ten eerste behoudt het de informatie als de stroom afgeschakeld wordt en ten tweede is het bestand tegen straling (ruimtevaart toepassingen!)). Voor het geheugen hebben wij de empirische relatie production price per bit = 0.3 × 0.72
( t – 1974 )
(2)
De toenemende complexiteit van de systemen stelt ook toenemende eisen aan de betrouwbaarheid, ook wel aangegeven met de zogenaamde ’mean time between failure’ (MTBF). We hebben al opgemerkt dat het oppervlak van chips constant gehouden wordt rond de vierkante centimeter. Dit is omdat met een toenemend oppervlak de kans dat het silicium een onzuiverheid bevat of een mechanisch gebrek - een breuk bijvoorbeeld - (te) snel toeneemt. Vandaar dat de toename van het aantal componenten hand-in-hand gaat met een afname van de grootte per component. De voordelen van deze voortgaande miniaturisatie zijn onder andere: 1) toename in de snelheid van het systeem door kleinere transistoren, kleinere on-chip verbindingen en kleinere inter-chip verbindingen, 2) toenemende betrouwbaarheid door kortere verbindingen, 3) beperking van de warmte ontwikkeling door reductie van het aantal hoog vermogen line-driver transistoren. Het overall effect voor de, tijden lang zeer populaire, Motorola 68000 processor wordt geïllustreerd aan de hand van de gegevens in tabel 5. Failure Rate/MTBF Motorola 68000 Jaar
Failure Rate [Percent 1000 uur]
MTBF [uren(jaren)]
1974
1.27
1975
0.5
200,000 (23)
1976
0.12
833,000 (95)
1977
0.08
1,700,000 (194)
1978
0.013
7,700,000 (877)
1979
0.006
16,666,666 (1901)
tabel 5
Inleiding Natuurkundige Informatica
78,000 (9)
Faalkans en MTBF voor de Motorola MC68000 processor.
10
1997
Historisch Overzicht en Terreinafbakening
Software
Het laatste punt, de warmte dissipatie, begint in toenemende mate belangrijk te worden. Men kan laten zien dat het probleem van warmte dissipatie intrinsiek aanwezig is op de chip en ten minste gelijk is aan kT (10-20 J bij 350 K) per bit, we komen hier nog op terug in hoofdstuk 2. Moderne halfgeleiders consumeren op zijn minst 105 kT. De behuizing van de chip zorgt voor de afvoer van de warmte waarbij als grens geldt een maximaal toegestane temperatuur van 75-85o C. Dat er speciale voorzorgsmaatregelen nodig zijn is duidelijk uit de overweging dat een 60 W lamp, die 0.5 W/cm2 produceert veel te heet is om aan te pakken, terwijl de DEC Alpha processor 30 W dissipeert op een oppervlak van 2.3 cm2. Een nadeel van de toenemende miniaturisatie is de toenemende complexiteit van de bedrading. Teneinde dit te kwantificeren is voor het eerst door E.F. Rent in 1960 een empirische relatie vastgesteld die bekend staat als de wet van Rent: T = CN
p
(3)
Hierin is T het aantal pins dat een groep van N elementen nodig heeft om met de rest van het systeem te kunnen communiceren, terwijl C het aantal connecties is die nodig zijn voor één element. De karakteristieke constante p bevindt zich tussen de 0.1 en 0.3. Beschouw bijvoorbeeld de Motorola 68000, Deze dankte haar naam aan het aantal transistoren dat zij bevatte: N = 68000. De behuizing van de 68000 omvatten 60 pins (T). Uitgaande van transistoren, d.w.z. C = 3, vindt men dan p = 0.27. Overigens is er dan nog sprake van een forse toename van het aantal pins van bijvoorbeeld 64 (MC 68000, 1980) naar 482 (IBM PowerPC 620, 1995). Het zal duidelijk zijn dat deze toename van de complexiteit ook voor de bedrading op de zogenaamde boards, en de algoritmen die deze bedradingen doorrekenen, forse consequenties heeft. Gevolg van al deze ontwikkelingen is wel dat de levensduur van de computer gereduceerd is tot een periode van ongeveer drie (tot vier) jaar. De veroudering wordt veroorzaakt door economische factoren (kosten van onderhoud) en explosieve ontwikkeling van de eisen van software. Vier jaar geleden was de tekstverwerker WP 4.1 een state-of-the-art produkt dat comfortabel draaide op een 2 MB machine. De huidige versie WP7 vereist een veelvoud aan memory.
1.3
Software
De immense toename van de prestaties van de hardware heeft ook zijn sporen achtergelaten in de software, niet alleen in de vorm van de gehanteerde talen en omgevingen maar ook wat betreft de gebruikte en mogelijke methodes (algoritmen). De hiermee geassocieerde namen zijn misschien minder sprekend maar het vermelden evenzeer waard aangezien voor het ontsluiten van de hardware de software in toenemende mate van belang is. Een tamelijk willekeurig overzicht van enige belangrijke data wordt gegeven in tabel 6. Het is goed om te bedenken dat voor 1946 er nog geen sprake was van het begrip programmeertaal maar evenzeer dat het begrip ’parallel rekenen’ slechts een theoretisch implicatie had voor de 80er jaren. Daarnaast moet opgemerkt worden dat de algoritmen waarvan wij ons bedienen niet onbeperkt ’schaalbaar’ zijn. Voor het zoeken van een naam in een kleine lijst is elke methode toereikend. Voor de telefoongids van Amsterdam, waarin grootte orde van 400,000 namen opgenomen staan, moet al een andere strategie gevolgd worden. Voor een verzadigde ruimte van IP adressen (de basis van Internet) waarin grootte orde van 2 miljard entries voorkomen is weer een volstrekt nieuwe benadering nodig. De lange tijd die software nodig heeft om tot rijping te komen wordt geïllustreerd in tabel 6: Fortran wordt na veertig jaar, zij het in steeds nieuwe vormen, nog steeds veelvuldig gehanteerd als programmeertaal voor wetenschappelijke berekeningen en hoewel zij in 1990 een grondige revisie heeft ondergaan, kan het dialect uit de oertijd nog bijna zonder modificatie door een huidige Fortran compiler vertaald worden. Wij komen hier in hoofdstuk drie nog op terug. Met andere woorden: de keuze van software lijkt een redelijk ingrijpende zaak: daar waar bij hardware een technische (en economische) levensduur van ongeveer 3 jaar gehanteerd wordt, praat men in de software over een andere grootte orde van tijdschalen: in bijvoorbeeld de bankwereld
Inleiding Natuurkundige Informatica
11
1997
Historisch Overzicht en Terreinafbakening
Software
Jaar
Gebeurtenis
1946
‘Stored program concept’ (Von Neumann)
1951
Eerste assembler (Univac)
1954
Fortran (John Backus, IBM), rekenwerk
1956
LISP, symbolische processing
1959
Cobol, administratieve toepassingen
1972
Prolog, kunstmatige intelligentie
1972
C, voornamelijk operating systemen
1974
SQL, databases
1986
C++, object georiënteerd C
1988
Mathematica, integratie symbolisch/numeriek
tabel 6
Enige belangrijke trends in de software
is het gebruik van Cobol code nog volstrekt normaal. Dit alles heeft ertoe geleid dat daar waar in de 50er jaren de hardware kosten dominant waren vanaf ongeveer de 70er jaren het omgekeerde het geval geworden is. Tegenwoordig zijn de kosten van software absoluut dominant. Met name in het domein van de PC heeft dit geleid tot grote vindingrijkheid bij het illegaal copiëren van software. De werkelijke kosten van software ontwikkeling en maintenance zijn enorm, precieze, up-to-date gegevens zijn moeilijk te verkrijgen maar schattingen geven aan dat er jaarlijks $ 450 miljard dollar omgaat terwijl de toename van de markt ongeveer 12% bedraagt. Sedert eind 60er jaren is de term ’software engineering’ ingeburgerd geraakt. Dit geeft aan dat vanaf die tijd het ontwerpen van software een zelfde ’engineering’ status kreeg als het maken van ’hardware’. Omschrijvingen van het gebied verschillen maar stemmen overeen in het uitgangspunt dat het gaat om het werk van een groep en niet alleen ’code’ maar ook documentatie en onderhoudbaarheid omvat. De term werd voor het eerst gebruikt op een conferentie waarop men de gevolgen besprak van de nieuwe (derde generatie) hardware die ordes krachtiger was dan de oude hardware en nieuwe eisen stelde aan de software. Gezien het onvermogen om snel te kunnen reageren op deze nieuwe mogelijkheden sprak men van een ’software crisis’. Dat de crisis nog steeds niet is opgelost moge duidelijk zijn uit alle problemen die men verwacht met het passeren van het jaar 2000. Wel zijn de inzichten rondom software scherper omschreven en gekarakteriseerd. Vier factoren kunnen als zeer belangrijk gezien worden: Onderhoudbaarheid. Aangezien de levensduur van software substantieel is, en veranderingen regelmatig noodzakelijk zijn, moet het zo gedocumenteerd en opgezet zijn dat de veranderingen gemaakt kunnen worden zonder onnodige kosten. Robuustheid. Dit impliceert dat het onder alle omstandigheden zodanig moet functioneren als de gebruiker verwacht en niet vaak onverwacht moet falen. Efficiency. Dit betekent dat er niet nodeloos een beroep op dure systeem resources gedaan moet worden. Overigens is dat niet hetzelfde als een maximalisatie van de performance. Userinterface. De software moet een zodanige gebruikers interface aanbieden dat een (onervaren) gebruiker eenvoudig met de software overweg kan. Het belang van een consistente, veelal grafische, userinterface (GUI) wordt in toenemende mate onderkend. Er moet overigens opgemerkt worden dat het realiseren van deze doelstelling vaak een kwestie is van een kostenafweging. Optimalisatie van alle criteria is moeilijk. Daarnaast moet men constateren dat de vraag
Inleiding Natuurkundige Informatica
12
1997
Historisch Overzicht en Terreinafbakening
Computers en wetenschap
naar software steeds groter wordt omdat steeds meer apparaten in ons dagelijk leven grote hoeveelheden software nodig hebben wat de crisis alleen maar erger maakt. Enige voorbeelden worden gegeven in tabel 7 op pagina 13. Teneinde deze getallen in perspectief te zetten is het goed om te vermelden dat uit een onderToepassing
Regels code
Source
< 5.000
Symbol Technologies
Automatische versnellingsbak
19.000
Chrysler
Draagbare telefoon
30.000
Motorola
Air traffic control computer
130.000
L. Lee
ATM
600.000
IBM
Telefoon call routing computer
2.100.000
DSC Comm. Corp.
B-2 stealth
3.500.000
L. Lee
Gevechtscomputer onderzeeër
3.600.000
L. Lee
Barcode scanner
tabel 7
Overzicht van hoeveelheden software gebruikt in systemen.
zoek van de fabrikant Hewlett Packard gebleken is dat een programmeur ongeveer 400 regels code per maand (i.e. 160 werkuren) kan produceren (inclusief documentatie en debugging). De diverse disciplines hebben elk hun eigen karakteristieke groottes van software projecten. Voor de wiskunde/informatica loopt dit in de 103 regels, de fysica is een orde groter (104) en de scheikunde is weer een orde groter (105). Meer belangrijk dan de absolute grootte is de constatering dat het voor de fysica niet mogelijk is om (alle) bestaande programmatuur om te schrijven in nieuwe programmeertalen: er is dus sprake van een afhankelijkheid van elkaars werk. Nadat leveranciers zich jarenlang hebben kunnen permitteren om ’eigen’ software te ontwikkelen en zo klanten aan zich te binden is er een beweging op gang gekomen om naar een grotere standaardisatie te komen. Unix is daar een goed voorbeeld van maar wel enigszins gedateerd aangezien elke leverancier tegenwoordig zijn eigen implementatie hiervan heeft. Voor de rest begint de software wereld angstig eenvoudig te worden: Microsoft domineert met al haar produkten de markt.
1.4
Computers en wetenschap
Teneinde de invloed van de computer op de wetenschapsbeoefening in kaart te kunnen brengen dienen wij eerst de wetenschappelijke activiteit zelf nader onder de loupe te nemen. Met de nodige vereenvoudiging kan zij gezien worden als een cyclische activiteit waarin experiment en modellering elkaar afwisselen. De stap van experiment naar modellering kan men wel aanduiden met die van abstractie, de omgekeerde stap zou men kunnen aanduiden met voorspelling en verificatie. Het concept van deze cyclus hoeft binnen deze context amper toegelicht te worden. De stormachtige ontwikkelingen binnen de computerindustrie die hiervoor geschetst zijn hebben op een drietal manieren de wetenschapsbeoefening beïnvloed. Dit is symbolisch aangegeven in Figuur 3. Bewust wordt hier gesuggereerd dat het om drie, onafhankelijke, ontwikkelingen gaat die elk een (steeds toenemend) beroep doen op de beschikbare rekenkracht: tijdsschaal van interactie, schaal van de modellen en type van de presentatie. Tijdsschaal van interactie De tijdsschaal van de interactie is intuïtief het meest duidelijk. De verandering die hier heeft plaatsgevonden is dat de machine steeds minder, in negatieve zin, een snelheidsbepalende factor geworden is en in toenemende mate processen zich in de tijdsruimte van de gebruiker, zogenaamd real-time, afspelen. Een systeem waaraan dit geïllustreerd kan worden is aangegeven in Figuur 4. Bepalende grootheden hier zijn de snelheid waarmee de informatie aangevoerd wordt (van transducer naar ADC naar Computer), de
Inleiding Natuurkundige Informatica
13
1997
Historisch Overzicht en Terreinafbakening
Computers en wetenschap
relevant
schaal van de modellen
niet realistisch type van presentatie numeriek traag (mens machine interaktie)
real-time
tijdsschaal interactie virtual reality Figuur 3
Inventarisatie van invloed van informatie op de wetenschap
verwerkingssnelheid (in de computer) en de snelheid waarmee de informatie afgevoerd wordt (in commuAnaloog
User
Analoog Transducer
ADC
Control
DAC
Computer
Netwerk interface domein
sensor domein Figuur 4
verwerkingsdomein
Schematische representatie van een computer in een experimentele opstelling
nicatie met de mens of via een netwerk of als terugkoppeling op het experiment). Voor real-time systemen moet afvoer en verwerking in evenwicht zijn met de aanvoer die hier geldt als externe factor i.e. niet beïnvloedbaar is. Alle regelsystemen zijn een voorbeeld hiervan met als één van de meest complexe de besturing van de CERN versneller. In Figuur 5 wordt een idee gegeven van de complexiteit van de diverse versnellerbanen. Het bedienen van een dergelijk omvangrijk instrument is handmatig volstrekt onmogelijk. Het zal duidelijk zijn dat de karakteristieke tijdsconstanten afhangen van de specifieke situatie. Bevindt de computer zich in een cyclus waarin hij communiceert met de mens dan is een reactie op 50 milliseconde schaal voldoende om als ’direkt’ ervaren te worden, immers optische frequenties hoger dan 20 Hz ervaart de mens als continu. Op zich lijkt dat goed te halen zeker als het slechts numerieke output betreft. Maar indien we uitgaan van bijvoorbeeld standaard video beelden met een afmeting van 768 bij 576 pixels, betekent dit dat er 8.847.360 pixels per seconde bewerkt moeten worden wat een ’per pixel’ tijd van ongeveer 100 ns impliceert onafhankelijk van de achterliggende berekeningen. Op de snelste Pentium processoren komt dat overeen met (maximaal) 20 bewerking per pixel. Als de computer dus gebruikt wordt voor visuele output is er nog een forse behoefte aan rekenkracht. Het schema van Figuur 4 kan men ook zien als een in kaart brengen van de factoren die een rol spelen bij het inzetten van een computer bij een experiment. Ten eerste is er het sensor domein dat de fysische verschijnselen omzet naar signalen die verwerkbaar zijn en vice versa. Vervolgens is er het domein van de discretisatie: zowel in signaalhoogte als in de tijd worden de signalen bemonsterd via zogenaamde Analoog
Inleiding Natuurkundige Informatica
14
1997
Historisch Overzicht en Terreinafbakening
Computers en wetenschap
Figuur 5
Overzicht van de CERN versneller structuur (zie ook www.cern.ch)
naar Digitaal Converters (ADC). Tenslotte is er het verwerkingsdomein. Tegenwoordig worden dit soort systemen in toenemende mate gedistribueerd dat wil zeggen bevinden zij zich op verschillende locaties die onderling door een netwerk verbonden zijn. Vooruitlopend op een behandeling in de volgende hoofdstukken laten wij aan de hand van Figuur 6 zien dat het discretiseren van signalen wel een ingrijpende operatie is. In het linker gedeelte van de figuur staat een signaal in 6 decimalen nauwkeurig, terwijl in het rechter figuur gediscretiseerd is naar 2 decimalen in fixed point notatie. 2.5
tijd resolutie a.u.
2.0
waarde resolutie
1.5
1.0
0.5
0.0 0
20
40
60
80
100
120
0
20
40
60
80
100
120
tijd Figuur 6
Voorbeeld van het effect van discretisatie op een signaal: links zijn 6 decimalen gebruikt, rechts 2 in fixed point notatie.
Inleiding Natuurkundige Informatica
15
1997
Historisch Overzicht en Terreinafbakening
Computers en wetenschap
Schaal van de modellen Ook het modelleren is sterk beïnvloed door de computer. Een schematische representatie is gegeven in Figuur 7. Is in de aangegeven cyclus geen mens opgenomen dan spreken we over een autonoom systeem. De cruciale verandering die plaatsgevonden heeft is dat voor een toenemend aantal problemen de cyclus interactief uitgevoerd kan worden met de wetenschapper. Een tweede minstens even belangrijke ontwikkeling is Opstellen van het model
Oplossen van het model
Modificatie van het model
Validatie Figuur 7
Schematische representatie van een modelleringsproces.
Voorspelling
dat de beperking dat alle verwerking numeriek plaats moet vinden steeds meer gereduceerd wordt en het in toenemende mate mogelijk is om analytische expressies met de computer te verwerken. Kenmerkend daarbij is dat de gebruiker steeds meer in staat is om in zijn eigen taal te communiceren met het systeem. Een extreme vorm van modellering is het zoeken in grote databases met ongestructureerde gegevens. In dat geval is men gaan spreken over ’data mining’. Het is moeilijk om een idee te geven welke problemen al berekenbaar zijn. Het volgende dient om enig inzicht te krijgen. Veel problemen in de fysica zijn reduceerbaar tot het oplossen van de (tijdsonafhankelijke) Schrödinger vergelijking HΨ = EΨ . De complexiteit van het bestudeerde systeem bepaalt hierbij hoeveel eigenwaarden (E) er bepaald moeten worden. Deze eigenwaarden laten in spectra hun ’vingerafdruk’ achter. Voor het modelleren van dergelijke complexe systemen kan het dus van belang zijn om zeer grote systemen op te kunnen lossen. Op het ogenblik ligt de limiet bij systemen met grootte orde van 106 eigenwaarden. Dergelijke berekeningen worden overigens niet in real-time gedaan. Type van interactie In toenemende mate wordt rekenkracht gebruikt om informatie aan de menselijke gebruiker op een eenvoudiger manier te presenteren. Het begrip ’user interface’ is hier een duidelijk voorbeeld geworden. In plaats van de, voor sommigen, cryptische opdracht ’rm a’ is het nu voldoende om het icon met de naam ’a’ naar een prullenbak te brengen. Deze ontwikkeling (zie tabel 1 op pagina 7) is in 1981 in gang gezet met de komst van de Xerox 8010 (Star) die om een aantal redenen een commerciële flop was. De opvolgers in 1982 (Apollo, inmiddels overgenomen door HP) en in 1984 (Apple MacIntosh, al jaren in de problemen) hebben de aard van de computer dramatisch beïnvloed. Maar ook het presenteren van gegevens is veranderd onder de toenemende rekenkracht. In Figuur 8 en Figuur 9 worden twee representaties van hetzelfde gegeven. Het verschil tussen beiden zal direct duidelijk zijn. In het zogenaamde grijswaarden plaatje (Figuur 8) staan 1800 punten weergegeven die de associatie, terecht, oproepen met een plaats, Venus in dit geval. In Figuur 9 staat slechts een deel van de corresponderende numerieke representatie. Deze ontwikkeling wordt in algemene zin aangeduid als wetenschappelijke visualisatie, ter onderscheid van de ’Disney’ toepassingen. Ook deze trend is geïnitieerd door de National Science Foundation met een even belangrijk rapport in 1987 waarin de naam voor het eerst gebruikt werd. Met name in de laatste jaren heeft de veranderende interactie gestalte gekregen in de vorm van ’multi media’ toepassingen waaronder een integratie van geluids- en beeldinformatie. Er zij overigens bij opgemerkt dat
Inleiding Natuurkundige Informatica
16
1997
Historisch Overzicht en Terreinafbakening
Computers en wetenschap
Figuur 8
Grijswaarden representatie van de gegegevens uit Figuur 9. In totaal zijn hier ongeveer 1800 meetpunten weergegeven.
PIONEER VENUS OCPP POLARIMETRY MAP 73 YR=79 DOY= 25 YR=79 DOY= 25 TIME=09:01:48 WAVELENGTH=935 NM GAIN=3 10 SECTORS FOL 50.6 2.98 -87.62 0.0 0.0 -2.00000 -2.00000 0.0 97.6 6.25 79.18 0.0 0.0 -2.00000 -2.00000 0.0 128.7 6.25 88.94 0.0 0.0 -2.00000 -2.00000 0.0 136.3 6.33 88.31 -83.25 -97.89 0.11931 0.05933 143.61 128.3 6.45 -88.61 -87.28 -95.58 0.16233 0.06110 144.10 107.2 6.60 -86.35 -88.95 146.26 0.17929 0.04242 144.77 78.6 7.13 -86.28 -85.84 124.86 0.17780 0.00927 145.56 50.0 4.39 89.21 -82.47 129.77 0.15734 -0.03891 146.42 27.0 3.88 -78.70 -78.49 139.47 0.10760 -0.11073 147.21 15.9 5.99 59.24 0.0 0.0 -2.00000 -2.00000 0.0 Figuur 9
Numerieke representatie van lichtverstrooiingsgegevens van de planeet Venus die gemeten zijn met behulp van het OCPP experiment.
het visuele systeem van de mens ordes meer informatie kan verwerken dan het auditieve systeem. De tactiele interaktie is hierbij de slechtste derde. Een extreme grens die op het ogenblik erg in de belangstelling staat als extreme vorm van de ’multi media’ representatie is de zogenaamde ’virtual reality’ waarbij een niet bestaande werkelijkheid door een computer aan de mens gesimuleerd wordt. Hierbij bevindt de menselijke waarnemer zich in een ’grot’ van beeldschermen. De positie van zijn hoofd, zes vrijheidsgraden, wordt gemeten en de beelden op de schermen worden overeenkomstig aangepast. Teneinde de realiteitszin van een en ander te vergroten wordt er ook gedaan aan stereoprojectie. Realisatie van een dergelijk systeem is alleen mogelijk met state-of-the-art Silicon Graphics systemen (gebaseerd op de R10000). Bij SARA staat een dergelijke CAVE (Figuur 10). Maar ook minder spectaculaire toepassingen maken nuttig gebruik van de mogelijkheden van visualisatie. In Figuur 11 is een gesimuleerd meetapparaat weergegeven waarvan de functionaliteit door de gebruiker gedefinieerd kan worden maar het onduidelijk is of deze functionaliteit in software of in hardware gerealiseerd wordt. Vanwege deze ontwikkeling is het gebruikelijk geworden om over ’virtuele instrumenten’ te spreken. De knoppen en displays die op het computerscherm zijn aangebracht dienen met name om de menselijke gebruiker snel in staat te stellen relevante informatie te overzien. Het besturen van complexe systemen zou onmogelijk zijn als er niet de mogelijkheid bestond om de overweldigende hoeveelheid numerieke informatie om te zetten naar, mens georiënteerde, visuele systemen. Modellen revisited: Simulatie Tenslotte is de simulatie als derde mogelijkheid gekomen naast theorie en experiment zoals aangegeven in
Inleiding Natuurkundige Informatica
17
1997
Historisch Overzicht en Terreinafbakening
Figuur 10
Computers en wetenschap
Schematische voorstelling van een CAVE. Drie projectie schermen zijn zichtbaar. De totale ruimte is ongeveer 3x3x3 meter.
Figuur 11
Virtueel instument ontwikkeld in LabView. In dit geval gaat het om een digitale thermometer die gekoppeld is aan een x-t schrijver.
Figuur 12. Het is moeilijk te omschrijven waar de simulatie van een kwantiteit (het even doorrekenen van een paar mogelijkheden) tot een kwaliteit geworden is, maar uit het huidige veld van de wetenschapsbeoefening is zij niet meer weg te denken. De simulatie, een typische representant van de penetratie van de comExperiment
Simulatie
scientific computing wiskunde/ statistiek Model Figuur 12
Model van de bèta-wetenschappelijke cyclus. De methodologieën voor modelvorming/simulatie en experiment zijn onvolledig aangegeven.
puter in de wetenschapsbeoefening, heeft haar plaats in deze cyclus met name in 1984 gekregen met het rapport van de "Advanced Scientific Computing" adviescommissie van de National Science Foundation, waar-
Inleiding Natuurkundige Informatica
18
1997
Historisch Overzicht en Terreinafbakening
Natuurkundige Informatica
in de commissie onder andere stelde: “Science is undergoing a structural transition from two broad methodologies to three - namely from experimental and theoretical science to include the additional category of computational and information science. A comparable example of such a change occurred with the development of systematic experience science at the time of Galileo”. Hoewel de uitspraak wel gebracht wordt met gevoel voor dramatiek is simulatie niet meer weg te denken uit de hedendaagse wetenschapsbeoefening en techniek. Bijvoorbeeld 80% van alle autobotsingsproeven die er gedaan worden wordt nu in simulatie uitgevoerd. Daarbij dient wel aangetekend te worden dat men dan nog praat over weken rekentijd op state-of-the-art Cray computer systemen en in toenemende mate worden ook in simulatie kernproeven uitgevoerd om voor de hand liggende redenen. De complexiteit van dat soort berekeningen is echter dermate groot dat in 1997 een systeem met 9000 (!) Pentium Pro processoren specifiek voor dit doel in de USA in gebruik is genomen. Eerst zullen wij een verkenning maken langs de verschillende componenten van de cyclus en de relatie tot de (toegepaste) informatica schetsen. Simulatie kan gezien worden als een soort ‘experimenteren op het droge’ waarbij de werkelijkheid niet de bepalende factor is. Men onderscheidt twee soorten simulaties: deterministisch en Monte Carlo. Een voorbeeld van het eerste is het simuleren van een gas. Indien men de fundamentele wisselwerking tussen de gasdeeltjes kent, en een initiële configuratie kent kan de (tijds)ontwikkeling van het gas berekend worden uit deze begintoestand. De berekening is volstrekt deterministisch. Gegeven een bepaalde beginsituatie zal er altijd sprake zijn van dezelfde eindsituatie. De wisselwerking en de bewegingsvergelijkingen zijn essentieel in dit geval. Werkt men met systemen waarin de kans dominant is dan spreekt men om duidelijke redenen van Monte Carlo systemen. In alle gevallen is een vorm van modellering vereist, b.v. de dynamica van een ideaal gas. De omstandigheden waaronder ‘geexperimenteerd’ wordt hoeven echter niet fysisch te zijn: het gas kan in een twee dimensionaal vat opgesloten zitten of men kan naar de individuele botsingen van de gas moleculen op de wand kijken, terwijl de tijdsschaal waarop gekeken wordt bepaald wordt door het programma. De beperkingen zijn echter ook evident: een simulatie kan niet meer in kaart brengen dan haar basis regels toelaten! Simulatie kan ook ter validatie van een model gebruikt worden om zo de invloeden van parameters in kaart te brengen. Denkt men aan het voorbeeld van het fitten van data met een exponentiële decay dan kan in een simulatie studie een (groot aantal) mogelijke combinatie van ruis, bijdragen en vervalconstanten doorgerekend worden om te zien of de gevonden resultaten ook op andere manieren verklaard kunnen worden of dat zij uniek zijn (validatie). De beperkingen van de computer zowel op hardware als op software gebied waren in de eerste decennia dermate groot dat alle toepassingsgebieden uniform benaderd werden: de toepassing was ondergeschikt aan het systeem. Zou men de software ontwikkelingen die in tabel 6 op pagina 12 aangeduid worden nader onder de loupe nemen dan zou de conclusie zijn dat er in het laatste decennium een belangrijke trend is in de richting van de toepassingsdomeinen, sterker nog, dat het toepassingsdomein bepalend wordt voor aard van de software, iets wat in mindere mate ook geldt voor de hardware.
1.5
Natuurkundige Informatica
De constatering dat er een nauwe relatie bestaat tussen het toepassingsgebied en de soort informatica technieken die men nodig heeft, heeft geleid tot de ontwikkeling van toegepaste informatica’s, waaronder de ‘natuurkundige informatica’. Met klem moet erop gewezen worden dat het hier niet gaat om de ’natuurkunde’ van de informatica maar om de structuur van de informatica binnen fysische toepassingen. In de voorafgaande sektie is er al toegewerkt naar een intuitieve terreinafbakening. Definitie en terreinafbakening hangen nauw samen. Een eenduidige definitie van het gebied is (helaas nog) niet te geven, als werkdefinitie zullen wij hanteren:
Inleiding Natuurkundige Informatica
19
1997
Historisch Overzicht en Terreinafbakening
Natuurkundige Informatica
Onder de natuurkundige informatica verstaan we de verzameling van hulpmiddelen, technieken en theorieën, die nodig zijn om met behulp van de computer problemen uit de fysica (mede) op te lossen. Dat de computer op al de velden van experiment, modellering en simulatie, een verandering gebracht heeft is duidelijk, de vraag is of het een kwantitatieve is of een kwalitatieve: helpt simulatie/visualisatie bij het krijgen van (meer) inzicht in de fysica of kan men zonder! In het kader van dit college zullen wij niet proberen om deze vraag te beantwoorden. Een aantal gebieden die tot de natuurkundige informatica gerekend kunnen worden zullen uit de inleiding duidelijk geworden zijn. Vanuit de experimentele natuurkunde (zie ook figuur 4 op pagina 14) worden sensor/actuator informatica, de interfacing, en de acquisitie aangedragen. Het is goed om te bedenken dat bijvoorbeeld de standaarden voor de instrumentatie (IEEE 488) aangedragen zijn vanuit de instrumentatie techniek. Binnen het kader zijn instrumentatie en meten van groot belang. Vanuit de theoretisch studies zijn de vragen naar nauwkeurigheid, bereik van het model en verwerkingssnelheid van belang. In dit inleidende college willen we proberen om een aantal aspecten van de natuurkundige informatica aan bod te laten komen als inleiding op latere uitwerking. In hoofdstuk twee zullen wij ons met name richten op de basis begrippen van de bemonstering en de gevolgen die dat heeft voor dataverwerkende systemen. Centraal staat hierbij de overgang van continue naar discrete informatie en een intuïtieve definitie van het begrip informatie. Met dat in handen zullen wij een aantal coderingsschema’s bekijken en zien welke mogelijkheden er zijn voor compressies. In hoofdstuk drie zullen wij enige eigenschappen van omgevingen introduceren aan de hand van een aantal software pakketten die binnen de natuurkunde veel gebruikt worden. Zaken als userinterfaces, executie modellen en volledigheid van omgevingen komen hier aan bod. Met die ervaring zullen wij in hoofstuk vier een aantal klassieke datastructuren introduceren en wat fundamentele operaties daarop definiëren. In hoofdstuk vijf zullen de datastructuren gebruikt worden om een aantal algoritmen nader toe te lichten. Tenslotte zullen wij in de hoofdstukken zes en zeven ons richten op meer hardware georiënteerde zaken Dan nog kan men vragen ‘wat het fysische’ is aan al deze activiteiten en waar het zich onderscheidt van de wiskunde en/of de informatica. Het antwoord hierop moet tweeledig zijn: de natuurkundige informatica is specifiek door haar accent op de fysische applicaties, zij is algemeen in de zin dat een aantal basistechnieken vereist zijn. In het navolgende college dictaat zal uitgaande van de algemene technieken steeds getracht worden om de specifieke fysische toepassingen te belichten.
Inleiding Natuurkundige Informatica
20
1997
Hoofdstuk 2:Enige basisbegrippen
Enige basisbegrippen
2.1
Inleiding
Inleiding
In dit hoofdstuk zullen wij ons bezighouden met de bespreking van enige basisbegrippen van de Natuurkundige Informatica. Wij zullen daarbij twee invalshoeken nemen: enerzijds de fysica en anderzijds de informatica en laten zien hoe die twee op elkaar van toepassing zijn. In paragraaf 2.2 zullen wij ons concentreren op het meten van verschijnselen in de ’werkelijke wereld’. Daarbij zullen sensors en het proces van discretisatie centraal staan. Hierbij moet opgemerkt worden dat computers niet volkomen synoniem zijn met discretisatie. In de 40er en 50er jaren is intensief gewerkt met analoge en hybride (mengvorm van analoge en digitale) computers. Deze zijn met name gebaseerd op de differentiërende en integrerende werking van electrische circuits. Ook nu staat het bewerken van analoge signalen weer in de belangstelling. De beperking van de analoge systemen is gelegen in het feit dat elke bewerking die toegepast wordt onvermijdelijk ruis en vervorming introduceert. Hoewel deze invloeden per stap misschien beperkt mogen zijn, kan het cumulatieve effect na miljoenen of miljarden bewerkingen zo groot zijn dat het output signaal niet langer de ware uitkomst representeert. Anders geformuleerd, de hoeveelheid informatie die in het analoge signaal aanwezig is, is te groot voor dit soort practische toepassingen. In discrete systemen is dit duidelijk beperkter. Daardoor is het ook mogelijk om de vervorming die door een bewerking wordt geïntroduceerd te corrigeren. In vrijwel alle digitale systemen wordt informatie als energie opgeslagen, hoogte van een spanning etc. Het herstellen van het signaal vereist dan de toevoer van een zeker hoeveelheid energie. Hierdoor komt men tot het principe schema in Figuur 13. De horizontale lijn stelt hier de signaal gang voor, de verEnergie Toevoer
Figuur 13 Signaal Energie in
Restoring Logic Device
Energie flow in een logisch ciurcuit.
Signaal Energie uit
Warmte afvoer
tikale rij de energie die nodig is voor het proces. Men zou zich kunnen afvragen of het niet mogelijk zou zijn om een circuit te construeren dat ideaal gedrag had, en dus geen externe energie toegevoerd zou moeten krijgen. Het antwoord op die vraag komt vanuit de fysica en luidt dat zulks niet mogelijk is. De reden is dat er één wet in de fysica is die niet symmetrisch is ten opzichte van de tijd: de tweede wet van thermodynamica die zegt dat processen zich bewegen in de richting van toenemende entropie. Dus de toevoer van energie aan een systeem is noodzakelijk om het een ’beslissing’ te kunnen laten maken. Anders geformuleerd Ten einde een richting te introduceren in de overgang tussen toestanden moet er onomkeerbaar energie verloren gaan. Een systeem dat energie behoudt kan niet een onomkeerbare overgang maken en dus geen beslissing nemen. Wat is het minimale verschil tussen de discrete niveaus ? In een fysische limiet kan men zeggen dat het groot moet zijn ten opzichte van de thermische energie kT, wij hebben al gespeculeerd op deze limiet in hoofdstuk 1. De waarschijnlijkheid P van een thermische fluctuatie ter grootte van E wordt gegeven door de Boltzmann statistiek P = exp [ – E ⁄ ( kT ) ] (4) Een ’veilig’ verschil tussen twee niveaus wordt dan gegeven door een energie van (b.v.) 100 kT. Belangrijker echter dan deze, nog lang niet bereikte, ondergrens aan de energie niveaus van logische systemen is de con-
Inleiding Natuurkundige Informatica
22
1997
Enige basisbegrippen
Sensors en discretisatie.
statering dat energie verlies onvermijdelijk is en gerelateerd is aan het aantal beslissingen, lees operaties, dat uitgevoerd wordt! Dus zal in principe een systeem dat meer operaties uitvoert een grotere hoeveelheid warmte dissiperen! Een verdere behandeling valt buiten het kader van dit dictaat men. Met name hoofdstuk 9 van Mead en Conway wordt in deze van harte aanbevolen. Wij volstaan hier met de conclusie dat het manipuleren van waarden (discreet of analoog) energie kost. Een inleiding in de manipulatie van discrete grootheden zal in de volgende paragraaf (2.3) gegeven worden waar elementen van de Boolse algebra besproken worden. Als illustratie van de Boolse algebra zullen de mogelijkheden van een aantal programmeertalen op dit gebied besproken worden in paragraaf 2.4. De samenhang tussen bits en informatie zal besproken worden in paragraaf 2.5. Op grond van voorkennis over de informatie, of kennis die dynamisch opgebouwd wordt is het mogelijk om efficiënt informatie te comprimeren. Dit zal in de laatste paragraaf aan bod komen in de sektie over compressietechnieken.
2.2
Sensors en discretisatie.
De ons omringende werkelijkheid openbaart zich aan ons door signalen. Het waarnemen hiervan vindt plaats in twee stappen. Eerst wordt het verschijnsel omgezet in een elektrisch signaal dat daarna bemonsterd Fysische verschijnselen Figuur 14
Electrisch Signaal
Sensor
ADC
Basismodel van een meetinstrument.
wordt en in een aantal discrete waarden om gezet. Het is gebruikelijk om signalen in zes groepen onder te brengen. Deze staan opgegeven in tabel 8. De tabel pretendeert niet uitputtend te zijn. Het zal duidelijk zijn Type signaal
Voorbeelden
Straling
Intensiteit, golflengte, polarisatie, fase, reflectiviteit, optische dichtheid
Mechanisch
plaats, afstand, snelheid, versnelling, kracht, koppel, geluidsdruk
Thermisch
temperatuur, specifieke warmte, warmte stroming
Elektrisch
spanning, stroom, lading, weerstand, inductie, capaciteit, diëlectrische constante, polarisatie, frequentie
Magnetisch
veldsterkte, flux dichtheid, moment, magnetisatie
Chemisch
samenstelling, concentratie, reactiviteit, pH
tabel 8
Fysische eigenschappen van enige signalen
dat een groot aantal signalen gerelateerd zijn. Men denke maar aan de mechanische als apert voorbeeld. De conversie van signalen van het ene domein naar het andere is gebaseerd op fysische en chemische eigenschappen. Sensorontwikkeling en integratie is met name voor de instrumentatie van belang en leidt meestal tot nieuwe meetinstrumenten. Jaarlijks wordt onder andere de internationale Instrumentation and Measurement Technical Conference (IMTC) gehouden onder auspiciën van de IEEE waarop nieuwe meetinstrumenten bediscussieerd en gepresenteerd worden. Een duidelijke trend op deze conferenties is de toenemende invloed van software op het instrument ontwerp. Binnen dit kader spreekt men meer en meer over virtuele instrumentatie. In hoofdstuk 6 komen wij hierop terug. Sensors genereren een elektrisch signaal. Als de sen-
Inleiding Natuurkundige Informatica
23
1997
Enige basisbegrippen
Sensors en discretisatie.
sor zelf het elektrische signaal genereert, en geen externe voeding nodig is, dan spreekt men over ’self-generating’ sensors anders spreekt men over ’modulating’ sensors. Een zonnecel is een duidelijk voorbeeld van de eerste categorie, een Hall sensor, waarin een elektrische stroom door een magneetveld gemoduleerd wordt, is een voorbeeld van de tweede categorie. De afmetingen en gebruikte meetmethodologie bepalen in sterke mate de nauwkeurigheid en bruikbaarheid van het meetapparaat. Gezien de sterk toenemende belangstelling zou het te overwegen zijn om aan de 6 categorieën een nieuwe toe te voegen voor multi-media signalen. Een discussie hiervan valt buiten het kader van dit college. Voor de behandeling van enige aspecten van discretisatie zullen wij als uitgangspunt het elektrische signaal nemen dat door de sensor gegenereerd wordt. Een dergelijk signaal heeft een dimensionaliteit en een tijdsafhankelijkheid. Binnen de instrumentatie begint er een toenemende aandacht te komen voor twee en hoger dimensionale sensors. Beelden zijn een typisch voorbeeld van 2-dimensionale informatie, terwijl dradenkamers, die in de kernfysica gebruikt worden, zelfs gezien kunnen worden als 3-dimensionale sensors. Voor de eenvoud van de beschrijving zullen wij ons echter concentreren op 1 dimensionale signalen. Een voorbeeld is weergegeven in Figuur 15. Als representatie van de fysische situatie nemen wij het continue linker
t
∆t Figuur 15
Overgang van continue beschrijving naar een discrete. De inter sample tijd t en sample tijd ∆t zijn bepalend voor dit proces.
geval. Voor de eenvoud zullen wij aannemen dat het om tijdsafhankelijke signalen gaat. In de mathematische abstractie wordt het signaal door een functioneel verband gegeven f (t) = sin αt . Het continue karakter van het signaal impliceert dat we voor willekeurig dicht bij elkaar liggende waarden van t en t+∆t een waarde kunnen bepalen. (Overigens is dit in praktische toepassingen wel lastig te realiseren.) De bemonstering van de sensor en ADC zal het omzetten in discrete vorm zoals aangegeven in het rechter gedeelte van Figuur 15. Daarbij spelen drie grootheden een rol: de sample resolutie, de sample frequentie, de sample tijd. De sample ∆ Figuur 16 −128
1 2
+127
Typisch voorbeeld van een uniforme, bipolaire, quantificatie en mapping op een 8-bits ADC.
256 ∆
10000000
Inleiding Natuurkundige Informatica
01111111
24
1997
Enige basisbegrippen
Sensors en discretisatie.
resolutie is de nauwkeurigheid waarmee monsters genomen worden, de overgang van continu naar discreet, van het signaal. Dit gebeurt door de ’analoog naar digitaal converter’ (ADC). De kwantisatie vindt plaats op een aantal niveaus, een ondergrens wordt gevormd door twee. In dat geval spreekt men van een 1-bits ADC, immers er kunnen maar twee toestanden onderscheiden worden. In Figuur 16 is een geval weergegeven van een kwantisatie in 256 niveaus, oftewel een 8-bits ADC. Merk op dat we hier werken met een relatieve nauwkeurigheid doordat het aantal niveaus onafhankelijk van het signaal bereik gedefinieerd wordt, standaard worden deze niveaus uniform verdeeld. Functioneel kan een ADC beschouwd worden als een vergelijker die test of een spanning binnen een bepaald bereik ligt. De resolutie van de ADC is duidelijk gekoppeld aan het aantal kwantisatie niveaus. De minimale stap ∆ (zie Figuur 16) noemt men wel het quantum interval of LSB (Least Significant Bit). Beschouw als voorbeeld een systeem waarin wij een signaal willen meten dat een maximaal bereik heeft van 10 V. Voor een 8-bits ADC, d.w.z. 256 kwantisatie niveaus, betekent dat ∆ = 39 mV. Voor een 16-bits ADC reduceert dit interval tot ∆ = 153 µV. Het zal duidelijk zijn dat voor een dergelijke nauwkeurigheid een uiterst stabiele spanningsbron en een zeer gecontroleerde meetsituatie nodig is. Voor de kwantisatie wordt vrijwel altijd de strategie toegepast dat als x ligt tussen de ( m – 1 ⁄ 2 )∆ en ( m + 1 ⁄ 2 )∆ , de waarde van x gesteld wordt op m. Deze benadering dient om de kwantisatiefout te minimaliseren. Wij komen hier nog op terug in formele zin maar grafisch is dit aan de hand van Figuur 16 te zien. Stel dat wij een signaal x(t) bemonsteren met kwantisatie stap ∆ en niveaus yk k=1,.., L. Er geldt dan dus dat yk+1 - yk = ∆. De kwantisatie fout Qn kan men dan algemeen schrijven als (5) Qn = X – Y waar X het continue input signaal voorstelt en Y het gekwantiseerde output signaal. Als we aannemen dat er gekwantiseerd word in L niveaus, dan geldt dat de gemiddelde kwadratische fout gegeven wordt door L
2 qn
=
2 E [ Qn ]
=
vk + 1
∑ ∫v
k=1
2
( x – y k ) p ( x ) dx
(6)
k
Met een paar aannames kan deze vergelijking vereenvoudigd worden. Ten eerste, aannemende dat de kwantisatie niet te grof gebeurt, is het redelijk om te onderstellen dat p(x) benaderd kan worden door de constante p(yk) van het kde kwantum interval, oftewel p(x) = p(y k) = P k ⁄ ∆ k (7) waarbij Pk de kans is dat x in het kde kwantum interval ligt. Ten tweede kan bewezen worden, zoals boven al aangegeven dat minimalisatie van de fout geschiedt als x symmetrisch in het interval ligt, dat wil zeggen yk – ∆k ⁄ 2 ≤ x ≤ yk + ( ∆k ⁄ 2 ) (8) Combinatie van deze twee aannames levert op dat L
2 qn
=
L Pk ( yk + ( ∆k ⁄ 2 ) ) 2 2 1 -----Pk ∆k ( x – y k ) dx = --6 ∆k ( yk – ∆k ⁄ 2 )
∑ ∫
k=1
∑
(9)
k=1
Voor het speciale, maar vaak voorkomende geval van uniforme kwantisatie geldt dat 2
2
qn = ∆ ⁄ 6
(10)
Dus als de kwantisatie nauwkeurig en uniform is, geldt dat de kwantisatie fout onafhankelijk is van de waarschijnlijkheidsdichtheidsfunctie van het input signaal. Het zal duidelijk zijn dat in echte toepassingen de kwantisatie ruis klein moet zijn ten opzichte van de variaties die in het signaal aanwezig zijn. In het optische domein is het goed mogelijk om een twee-dimensionale sensor te construeren: de camera. Bij deze sensor wordt (twee dimensionaal) plaatsafhankelijk de intensiteit (kleur) gemeten, en vervolgens
Inleiding Natuurkundige Informatica
25
1997
Enige basisbegrippen
Sensors en discretisatie.
gediscretiseerd. De camera’s zijn tegenwoordig allemaal gebaseerd op CCD devices. In dit twee dimensionale array van CCD’s wordt gedurende een bepaalde tijd lading geaccumuleerd die proportioneel is met de intensiteit van het (licht) signaal. Voor het omzetten van het (twee dimensionale) videosignaal naar discrete waarden gebruikt men een speciaal apparaat: een framegrabber. Rijgewijs wordt de lading uit de CCD geschoven en door de framegrabber gedigitaliseerd. Het videosysteem is ontwikkeld in de consumentenelektronica markt. Helaas bestaan er verschillende maten voor Amerika (EIA norm) en Europa (CCIR norm). 64 µs
Figuur 17
576 lines
625 lines
40 ms
956 pixels
768 pixels
CCIR video norm en het effectieve gescande gebied.
In Figuur 17 staat de CCIR standaard. De synchronisatie van het beeld (terugbeweging) zorgt ervoor dat effectief slechts 74% van het totale gebied gebruikt wordt. Het aantal discretisatie niveaus voor video systemen is beperkter dan voor de klassieke ADC’s maar dat neemt niet weg dat zij een (griezelige) hoeveelheid informatie kunnen genereren. Als men uitgaat van een 8 bits camera met een effectief oppervlak van 576 bij 768 die gedurende een seconde, dat wil zeggen 25 maal, beelden opneemt, dan levert dit een totale hoeveelheid informatie op van Informatie = 25 * 576 * 768 = 10 Mbyte/s Het zal duidelijk zijn dat speciale maatregelen genomen moeten worden om deze stroom beheersbaar te houden en te kunnen verwerken. Het real-time verwerken van videobeelden is dan ook een uitdaging op zich. De sample frequentie wordt door twee randvoorwaarden bepaald: enerzijds de verwerkingssnelheid van het meetsysteem en de computer die eraan verbonden is anderzijds de vraag hoeveel informatie er nodig is om te kunnen komen tot een unieke reconstructie van het signaal. Het antwoord op de laatste vraag ligt in toepassing van de Fourier transformatie en vereist een redelijke kennis van de systeem en signaal theorie. Wij zullen daarom alleen proberen het resultaat aannemelijk te maken. Grafisch is een en ander weergegeven in Figuur 18. Het samplen van het continue signaal, waarvan de temporele en frequentierepresentatie gegeven zijn in de eerste rij, betekent de vermenigvuldiging met een serie δ-functies in het tijdsdomein. In het frequentiedomein correspondeert dit met een convolutie van het oorspronkelijke spectrum met een reeks δfuncties met periode ωs. De sample frequentie is gegeven in de tweede rij, ook in twee representaties. De reconstructie is tenslotte gegeven in de derde rij. Hier wordt duidelijk dat er de kans bestaat op aliasing van het signaal als de sample frequentie te laag is ten opzichte van de maximale frequentie ωm van het signaal. Het Nyquist theorema luidt volledig Een signaal dat in frequentie beperkt is tot ωm, kan volledig gereconstrueerd worden uit een serie van uniforme verdeelde samples met een frequentie ω s ≥ 2ω m . De minimale frequentie wordt Nyquist frequentie genoemd. Let op dat het hier gaat om een volledige reconstructie van het originele signaal. Daarmee wordt een antwoord gegeven op de vraag hoevaak er minstens gesampled moet worden. De samplefrequentie bepaalt de maximale tijd die gebruikt kan worden om het signaal te meten, maar het
Inleiding Natuurkundige Informatica
26
1997
Enige basisbegrippen
Discrete wiskunde: Boolse Algebra
X(ω)
x(t) t
−ωm 0 ωm T δT(t)
ωsδω (ω)
ωs=2π/T
s
t
t
Figuur 18
−ωs
0
ωs
−ωs
−ωm 0 ωm
ωs
Xs(ω)
xs(t)
Fourier transformatie voor ideale sampling.
is een bovengrens. Bij figuur 15 op pagina 24 wordt gesuggereerd dat het bemonsteren van het sinusvormige signaal kort is ten opzichte van de tijd tussen twee samples dus dat de waarde gezien kan worden als een instantane waarde in plaats van een gemiddelde waarde. Voor een aantal typische multi-media signalen staan de belangrijkste signaal parameters in tabel 9. Gezien het Nyquist theorema is het dus nodig om muType Signaal Telefoon
Sample rate (KHz) 8.
Bits/Sample
Mono/Stereo
Uncompressed Data/Rate
8
Mono
64. Kbit/s
AM Radio
11.025
8
Mono
88.2 Kbit/s
FM Radio
22.050
16
Stereo
705.6 Kbit/s
CD
44.1
16
Stereo
1411.2 Kbit/s
tabel 9
Overzicht van enige signaal karakteristieken van multi-media signalen.
ziek eerst laagdoorlaat te filteren zodat ω m ≤ ω s ⁄ 2 , teneinde aliasing te vermijden. Daarmee sluiten wij deze eerste verkenning van de discretisatie van meetsystemen af.
2.3
Discrete wiskunde: Boolse Algebra
Bij het verwerken van de gediscretiseerde signalen die zo verkregen zijn betreden wij het domein van de discrete wiskunde. De ondergrens van de discretisatie die verkregen kan worden is een tweewaardig systeem. Zou men een dergelijke sampling toepassen op Figuur 15 dan zou dat figuur reduceren tot een blok puls. Teneinde deze twee toestanden mathematisch te representeren zijn twee symbolen nodig. George Boole heeft in 1854 een notatie met ’0’ en ’1’ ingevoerd die sedertdien bekend geworden is als de Boolse Algebra. Deze algebra is een formele taal die opgevat kan worden als een verzameling van operaties op de basis elementen van de taal die men wel het alfabet noemt. In het Boolse alfabet zijn er twee symbolen, het Nederlandse alfabet kent er 271. Op deze symbolen kunnen functies toegepast worden. Wij onderscheiden twee
Inleiding Natuurkundige Informatica
27
1997
Enige basisbegrippen
Discrete wiskunde: Boolse Algebra
soorten functies: monadische, die één operand hebben, en dyadische die twee operanden hebben. Gezien beperktheid van het binaire alfabet is het goed mogelijk om alle mogelijke combinaties van inputwaarden en outputwaarden voor deze functies te tabelleren. In de Boolse algebra worden er drie basisoperaties gedefinieerd not, and en or. Om het spreken over deze functies te vereenvoudigen interpreteert men ’0’ ook wel als ’false’ (niet waar) en ’1’ als ’true’ (waar). Naast deze formele omschrijving (zie Figuur 19) kan men ook
x
not(x)
x
y
and(x, y)
x
y
or(x, y)
0
1
0
0
0
0
0
0
1
0
0
1
0
0
1
1
1
0
0
1
0
1
1
1
1
1
1
1
Figuur 19
Overzicht van de waarheidstabellen van de drie basis Boolse operaties not, and en or.
een omschrijving in woorden geven: not(x)
is waar dan en slechts dan als x niet-waar is,
and(x, y)
is waar dan en slechts dan als zowel x als y waar zijn,
or(x, y)
is waar dan en slechts dan als x en/of y waar is.
Aangezien binnen de Boolse algebra de standaard associatieve en distributieve wetten gelden kunnen triadische en hogere functies (recursief) gereduceerd worden tot deze dyadische en monadische. De prioriteitsvolgorde voor de binding van de operaties is not, and en or. Echter de functionele notatie waarvan hierboven gebruik gemaakt wordt voorkomt op voorhand al problemen rond de binding van operatoren. Binnen de Boolse algebra geldt een krachtige stelling die wij hier slechts zullen poneren, nl. dat elke Boolse functie geschreven kan worden als combinatie van de basisoperaties not, and en or. Daarbij moet aangetekend worden dat in de strikte zin des woords alleen één van de beide dyadische operatoren noodzakelijk is. Het is eenvoudig om in te zien dan geldt and(x, y) = not( not(x) or not(y)) Het onderzoeken van de omgekeerde relatie (or als functie van and en not) wordt aan de lezer als oefening overgelaten. Hoewel de bouwstenen eenvoudig zijn groeien de Boolse operaties al snel uit tot lange formules. Teneinde die overzichtelijk te houden zijn er een hoop additionele functies gedefinieerd. Een vaak gebruikte operatie is de zogenaamde exclusive or die als definitie de volgende omschrijving heeft: xor(x, y)
is waar dan en slechts dan als of x of y waar is.
In de Boolse basisfuncties krijgt men xor(x, y) = or( and( not(x), y), and(x, not(y))). Het dient benadrukt te worden dat het tot nu toe gaat om een formele beschrijving van elementen en wij nog niet de verbinding gemaakt hebben met de wereld van de computers. Binnen het kader van dit college zullen wij dat ook maar marginaal doen, dit behoort meer tot het veld van de discrete wiskunde en de digitale electronica. Die overgang kan gemaakt worden met behulp van de boven gedefinieerde functies. Uitgaande van de zojuist gedefinieerde xor is het mogelijk om het eenvoudige geval van de optelling van twee binaire grootheden x en y uit te voeren. Bedenk daarbij dat in de binaire notatie de volgende regels geleden: 1.
Op zijn minst zou men eigenlijk ook de spatie toe moeten voegen aan het alfabet en dus spreken van 27 elementen.
Inleiding Natuurkundige Informatica
28
1997
Enige basisbegrippen
Discrete wiskunde: Boolse Algebra
x
y
xor(x, y)
0
0
0
0
1
1
1
0
1
1
1
0
Figuur 20
Waarheidstabel van de Boolse operatie xor.
02 + 02 = 02 02 + 12 = 12 12 + 02 = 12 12 + 12 = 02 , with carry 12 Het is eenvoudig in te zien dat de som gegeven wordt door de xor terwijl de overflow (ook wel de carry genoemd) door de and gegeven wordt. Een generalisatie is snel gemaakt. Traditioneel heeft men gekozen als fysische representatie van deze ’0’ en ’1’ voor een spanningsrepresentatie. Tegenwoordige is de positieve logica, waarbij de ’1’ correspondeert met een spanning tussen 2 en 5 volt en de ’0’ met een spanning tussen 0 en 1 volt, gemeen goed. Voor alle practische toepassingen beperkt men een logisch signaal in de tijd en wordt er naast de hoogte ook een tijdsduur vastgesteld dat het signaal minimaal moet duren om als geldig gezien te worden. Dat valt overigens buiten het bestek van de Boolse Algebra. De tot nu toe behandelde operaties hebben één ding gemeen n.l. dat ze toestandsloos zijn, er is geen besef van of invloed van een vorige toestand, i.e. de geschiedenis. Men duidt dit vaak aan als ’state less’. De tegenhanger wordt aangeduid met ’state full’. Een geheugen is hier het meest eenvoudige voorbeeld van: de toestand moet hierin bewaard blijven. Een voorbeeld hiervan is gegeven in Figuur 21. Dit schema is bekend G1 S
R
or
or G2
Figuur 21
G3 not
not
Q
G4 Een Boolse operatie met terugkoppeling: een zogenaamde SR latch
als een SET/RESET latch. Het opvallende hieraan is dat er sprake is van een terugkoppeling tussen de output en de input. Dat heeft een aantal gevolgen. Ten eerste is deze functie niet direct meer in de Boolse algebra te beschrijven vandaar dat we gekozen hebben voor een grafische weergave. Maar ook het neerschrijven van een waarheidstabel is vanwege de terugkoppeling niet eenvoudig. Laten wij daarom aannemen dat beide inputs S en R ’0’ zijn en ook de output ’0’ is. Dan is de output van G1 ook ’0’ en dus de output van G3 ’1’. Met deze inputs (’0’ en ’1’) is de output van G2 ’1’ zodat de output van G4 ’0’ is, conform hetgeen wij ondersteld hadden. Kortom de situatie (0, 0, 0) voor S, R, en Q is een mogelijke. Laten we nu onderstellen dat Q de waarde ’1’ heeft, en zien wat er dan gebeurt. G1 wordt dan ’1’ zodat de output van G3 ’0’ wordt, dat maakt G2 ’0’ en dus de output van G4 ’1’, kortom ook de situatie (0,0, 1) is een stabiele. Wij kunnen dus niet de output Q eenvoudig via een waarheidstabel bepalen omdat wij iets over het verleden moeten weten. Laten we daarom aannemen dat we de toestand (0, 0, 0) hebben en dat gedurende een bepaalde tijd S van ’0’ naar ’1’ verandert. Daardoor zal G1 van ’0’ naar ’1’ veranderen waardoor de output van G3 naar ’0’ gaat en dus de output van G4 naar ’1’ gaat, d.w.z. van toestand verandert. Het is eenvoudig om na te gaan dat een overgang van S van ’1’ naar ’0’ terug de output waarde van Q onveranderd laat. Dus
Inleiding Natuurkundige Informatica
29
1997
Enige basisbegrippen
Logicals in programmeertalen
als de waarde van de output Q laag is, en als er tijdelijk een verandering van S van laag naar hoog plaatsvindt, dan wordt er een permanente verandering van Q bewerkstelligd. R heeft de omgekeerde werking. Brengen wij deze van laag naar hoog dan wordt de output van Q teruggezet naar ’0’. Daarmee wordt de naam ’set’ ’reset’ intuïtief gerechtvaardigd. De totale realisatie van een 1-bits G1
S
Data
or
G3 not
Control or not Figuur 22
R
G2
not
Q
G4
Een 1-bit geheugen dat gebaseerd is op een clocked D-latch.
geheugen wordt weergegeven in Figuur 22. Als de Control hoog gebracht wordt dan wordt de waarde die aangeboden wordt op de ’Data’ lijn opgeslagen. Uiteindelijk moet dit nog verder beperkt worden door het clocken van het signaal maar dat zijn kleinigheden. Het is voldoende om hier te constateren dat wij uitgaande van de binaire logica hebben laten zien dat het mogelijk is om binaire waarden op te tellen en om een waarde van 1-bit op te slaan. Een generalisatie naar grotere, en bruikbaardere structuren ligt voor de hand.
2.4
Logicals in programmeertalen
Het idee van logische variabelen (die slechts twee waarden kunnen aannemen) is van groot belang voor de een aantal programmeeromgevingen. In deze paragraaf zullen wij de implementatie in een paar belangrijke talen toelichten. Vrijwel unaniem worden logicals gebruikt om de flow van een programma, d.w.z. de manier waarop de verwerking plaatsvindt, te beïnvloeden door aan te geven of iets waar of niet waar is (’gedaan’ of ’niet gedaan’ moet worden). In Fortran (77 en 90) bestaat er een apart type logical. De reden hiervoor is dat in de ontwerptijd van Fortran logical function
xor(a, b)
c logicala, b logicalxor c xor = ( .not. a .and. b) .or. (a .and .not. b)) return Figuur 23
Implementatie van de xor functie in Fortran equivalenten.
geheugen duur was en een definitie van een apart type de mogelijkheid gaf om hiervoor, efficiënt, 1 bit te reserveren. Om de waarden van logicals te kunnen onderscheiden van ’gewone’ waarden worden zij op een speciale manier genoteerd als .true. en .false. Evenzo worden de logische operatoren genoteerd als .and. .or. en .not. In Fortran zou de function xor geïmplementeerd worden als aangegeven in Figuur 23. In principe zijn er vanwege de prioriteitsdefinitie geen haken nodig maar voor de leesbaarheid zijn ze toegevoegd. In zogenaamde conditionele expressies (if constructies) mogen alleen maar logicals of logische operatoren gebruikt worden. Voorbeelden van logische operatoren zijn de vergelijkingsoperatoren. Voor de niet-ingewijden in Fortran zij opgemerkt dat die op een soortgelijke manier genoteerd worden, namelijk als .lt. .le. .eq. .ne. .gt. .ge. Een expressie als x .gt. 4 wordt dus als geëvalueerd als .true. als x groter dan 4 is en anders als .false. Expliciete operaties op bits worden in standaard Fortran 77 niet ondersteund. Het is in principe niet eens mo-
Inleiding Natuurkundige Informatica
30
1997
Enige basisbegrippen
Logicals in programmeertalen
gelijk om een bit direct te adressseren. Wel moet opgemerkt worden dat in veel zogenaamde extensies van de Fortran standaard bit operaties toegevoegd worden. Opvallend is dat bij de revisie van de taal Fortran 77 in haar opvolger, Fortran 90, bit functies expliciet toegevoegd zijn. Het begrip ’state less’ en ’state full’ is ook van toepassing op de taal Fortran in de zin van de zogenaamde scope van de variabelen. Beschouw het voorbeeld gegeven in Figuur 23. De functie xor die hierin gedefinieerd wordt heeft ’geen geheugen’ en kan zelf niet vaststellen hoe vaak hij aangeroepen is. Voor een aantal toepassingen is dat lastig. Daarom kent Fortran een speciaal statement dat aangeeft dat de state van een (aantal) variabelen bewaard moet blijven. In Figuur 24 wordt een state full implementatie van de functie xor gelogical function
xor(a, b)
c logical logical data save
a, b, first xor first/.true./ first
c xor = ( .not. a .and. b) .or (a .and. b)) if(first) first = .false. return Figuur 24
Implementatie van een state full functie in Fortran 77
geven. De waarde van de (logical) variabele first wordt tussen opeenvolgende aanroepen van de functie xor bewaard. Om het ’kip en het ei’ probleem van de initialisatie op te lossen kent Fortran het zogenaamde ’data’ statement dat een initiële waarde bepaalt. Initieel zal first op .true. gezet worden en in de eerste aanroep van .true. naar .false. gezet worden. Op die manier kan de situatie herkend worden. Ten overvloede wijzen wij erop dat het ’save’ statement niet beperkt is tot logicals. In C(++) bestaat er geen apart type voor logicals. De standaard definieert hier dat variabelen van het type int als logische waarde geinterpreteerd kunnen worden met de volgende conventie false true
waarde gelijk aan 0 waarde ongelijk aan 0
De interpretatie van het statement if(a) statement_1 else statement_2 is dan ook dat statement_1 uitgevoerd wordt als a ongelijk 0 is, en anders statement_2. De drie basisoperaties and, or en not zijn op twee verschillende niveaus geïmplementeerd in de taal. Dit wordt toegelicht in && wordwise || wordwise ! wordwise Figuur 25
’and’ ’or’ ’not’
& bitwise | bitwise ~ bitwise
’and’ ’or’ ’not’
Overzicht van de logische operatoren die in C(++) gedefinieerd zijn.
Figuur 25. Zowel op het niveau van woord als van bit kunnen de operaties uitgevoerd worden. Een illustratie hiervan wordt gegeven in Figuur 26. Drie integer variabelen, die als logicals gebruikt kunnen worden, worden in de eerste regel gedeclareerd. Vervolgens worden er de hexadecimale waarden 1, 2 en 3 in gezet. Dit betekent dat van a maar één bit op 1 staat, van b ook terwijl bij c twee bits op 1 staan. Aangezien de waarden van de variabelen allen ongelijk aan 0 zijn, zullen alle drie de variabelen als woord opgevat worden als waar.
Inleiding Natuurkundige Informatica
31
1997
Enige basisbegrippen int
Logicals in programmeertalen a, b, c;
a = 0x01; b = 0x02; c = 0x03: Figuur 26
if(a && b) printf("wordwise true\n"); else printf("wordwise false\n"); if(a & b) printf("bitwise true\n"); else printf("bitwise false\n");
Voorbeeld van het gebruik van logicals in de taal C(++)
A en b hebben echter op bit niveau geen corresponderende bits geset, en dus zal de and operator false retourneren, het tegenovergestelde geldt voor het toepassen van de and operator op a en c. De bitwise operator zijn dus een één-op-één implementatie van de Boolse operaties zoals wij die in de vorige paragraaf besproken hebben. Met behulp van de woord operaties kunnen er feitelijk evenveel bit operaties parallel uitgevoerd worden als bits in een woord zitten, op courante architecturen dus 32. Het is dus in principe mogelijk om 32 binaire grootheden in één woord bij te houden en onafhankelijk van elkaar te manipuleren. Dat levert een redelijke besparing van geheugengebruik op maar de beschikbaarheid van geheugen is dermate groot dat dat tegenwoordig amper nog als een voordeel gezien hoeft te worden. Implementeert men de logische functie xor uit figuur 23 op pagina 30 in C dan krijgt men de functie uit Figuur 27. De functie verandert dramaint xor(inta, int b) { return ((!a) && b) || (a && (!b)); }
Figuur 27
C code voor de xor functies
tisch van werking als men in plaats van de ’woord’ operatoren de ’bit’ varianten gebruikt. Het zal duidelijk zijn dat één van de grootste zondes die men kan bedrijven in C(++) het omdraaien van de definitie van true en false is. Voor hen die gehecht zijn aan het onderscheid tussen het begrip logical en int biedt de preprocessor uitkomst met (bijvoorbeeld) de volgende definities. Past men deze definities toe #define #define
FALSE TRUE
0 1
#typedef
LOGICAL
int
dan wordt de C code vrijwel identiek aan de Fortran code. Net als Fortran heeft ook in C een functie in principe geen state als alle variabelen locaal zijn. De tegenhanger hier van het statement ’save’ is het voorvoegsel static. Dit geeft aan dat de waarde tussen twee opeenvolgende aanroepen behouden moet worden. Merk op dat in dit geval de initialisatie en declaratie als static gecombineerd kunnen worden. Alleen de eerint {
xor(int a, int b) static int
Figuur 28
first = 0;
if(!first) first = 1; return ((!a) && b) || (a && (!b));
Tegenhanger in C(++) van de Fortran functie uit figuur 24 op pagina 31.
}
Inleiding Natuurkundige Informatica
32
1997
Enige basisbegrippen
Informatie, Codes en Compressie
ste keer dat de functie aangeroepen wordt zal first de waarde 0 (false) hebben. Mathematica volgt in zeker zin de manier waarop in C de logicals geïmplementeerd zijn. De vergelijkings operatoren zijn op vergelijkbare wijze geïmplementeerd en ook hier laat een degelijke logische operatie een logical waarde achter, die geprint wordt als ’true’ of ’fasle’. Vooruitlopend op een verdere behandeling van Mathematica zij hier vast opgemerkt dat een kenmerkend verschil wel is dat Mathematica in staat is om logische expressies formeel uit te werken.
2.5
Informatie, Codes en Compressie
Het is verleidelijk om te denken dat bits en informatie hetzelfde zijn maar dat is niet zo. Om dit in te zien is het voldoende om te bedenken dat bits, zoals nu ten tonele gevoerd, slechts een abstracte representatie zijn en willekeurig toegekend kunnen worden. Informatie is een moeilijk te definiëren begrip. Intuïtief zal het duidelijk zijn dat informatie en verrassing veel met elkaar te maken hebben: een perfect voorspelbare gebeurtenis heeft geen informatie. Meer mathematisch geformuleerd kan men zeggen dat informatie en kans met elkaar gerelateerd zijn. Om dit verder te kunnen onderzoeken zullen we uitgaan van een alfabet A met M verschillende symbolen aj, j=1, .., M die elk een waarschijnlijkheid hebben om gegenereerd te worden van P(aj ). Het zal duidelijk zijn dat de volgende relaties gelden P(a j) ≥ 0
j = 1, 2, …, M en
∑ j = 1 P(a j) M
= 1
(11)
Het begrip entropie komt voor in twee gescheiden gebieden: enerzijds de thermodynamica anderzijds de informatietheorie. In de inleiding hebben wij al beargumenteerd dat ze nauw gerelateerd zijn via onder andere de tweede wet van de thermodynamica. In de thermodynamica wordt de entropie gedefinieerd als evenredig met de logaritme van het aantal realisatiemogelijkheden van een systeem. Beschouw een systeem dat bestaat uit twee vaten met moleculen, 10 van soort A en 10 van soort B. Er is maar één manier waarop het systeem gerealiseerd kan worden zodanig dat alle moleculen van soort A in het ene vat en die van soort B in het andere vat zitten. Anderzijds zijn er veel mogelijkheden om ze gelijkelijk te verdelen over beide vaten. Derhalve zegt men dat de entropie van het tweede systeem groter is dan van het eerste. In de informatietheorie kan men de entropie op een zelfde manier definiëren als de logaritme van het aantal mogelijkheden van realisatie oftewel de logaritme van de inverse van de kans op een specifieke realisatie, oftewel 1 (12) I ( a j ) = log -------------- P ( a j ) I geeft aan wat de informatie is van het voorkomen van element aj uit ons alfabet. Dat dit een correcte definitie is kan eenvoudig ingezien worden. Beschouw een string van n bits. Met deze n bits kan men ten hoogste 2n verschillende boodschappen coderen. Voor een uniforme distributie van boodschappen geldt dat de kans op een specifieke boodschap gelijk is 1/2n . Het is voorts eenvoudig in te zien dat de entropie van twee strings ter lengte n en m precies gelijk is aan de som van de entropieën van de individuele strings. De eenheid van informatie hangt af van het grondgetal van de logaritme. Standaard is het geworden om een als grondtal 2 te nemen in welk geval men spreekt over binaire informatie eenheden of bits. Voor veel toepassingen is de gemiddelde informatie een belangrijker grootheid. Onder voorwaarde dat het voorkomen van alle M symbolen onafhankelijk is, is dan eenvoudig in te zien dat voor een serie van N symbolen, de M totale informatie gegeven wordt door – j = 1 NP ( a j ) log P ( a j ) Men definieert de entropie H(A) als de statistisch gemiddelde informatie per symbool, dus
∑
Inleiding Natuurkundige Informatica
33
1997
Enige basisbegrippen
Informatie, Codes en Compressie M
H ( A) = –
∑ P ( a j ) log P ( a j ) bits/symbool
(13)
j=1
Net als in de statistische mechanica is H(A) een maat voor de wanorde of onzekerheid van het systeem en is de waarde laag voor een geordend systeem. In het limiet geval levert dit P ( A 1 ) = 1 en P ( a j ) = 0 j=2, 3, ..., M. In dat geval geldt H ( A ) = H ( A ) min = 0 . Een andere limiet wordt gevormd door het geval waarin alle symbolen even waarschijnlijk zijn, en dus geldt dat P ( a j ) = 1 ⁄ M . Het is te bewijzen dat er in dat geval sprake is van maximale informatie per symbool M
H ( A ) max =
∑ ----M- log M 1
= log M
(14)
j=1
zodat de volgende relatie geldt 0 ≤ H ( A ) ≤ log M
(15)
Om het punt van bits en informatie duidelijk te maken kan het volgende voorbeeld dienen. Beschouw een situatie waarin er vier elementen in de verzameling A zitten a1, a2, a3, a4. Laat de kans op voorkomen van de vier elementen gegeven worden door 0.125, 0.5, 0.25, 0.125. Merk op dat de som van deze kansen gelijk is aan 1. Om deze vier symbolen te coderen hebben wij twee bits nodig, wij komen hier nog nader op terug in hoofdstuk 3. Maar de entropie, gemiddelde informatie, is gelijk aan 4
H ( A) = –
∑ P ( a j ) log P ( a j ) j=1
1 1 1 1 = --- log 8 + --- log 2 + --- log 4 + --- log 8 = 1.75 bits/symbool 8 2 4 8
(16)
Voor het binaire geval waarin beide waarde even waarschijnlijk zijn wordt keurig de limiet waarde gehaald. Uit het bovenstaande voorbeeld is duidelijk dat het mogelijk moet zijn om een compressie toe te passen. Compressie technieken zijn in toenemende mate belangrijk aan het worden. Een toepassing is het comprimeren van beelden. Voorlopig zien wij een beeld als een verzameling van N bij M beeld elementen, pixels genaamd. Deze pixels zullen bij een doorsnee foto niet willekeurig fluctueren, maar een zekere correlatie vertonen (aannemende dat de nemer scherp gesteld heeft en niet de Formule 1 fotografeert bij 1/30 sec). Intuïtief is het dus aannemelijk dat hier op een zeker mate van compressie mogelijk moet zijn. Een methode hiervoor is vastgelegd in de JPEG (Joined Photographic Expert Group) standaard. Vooruitlopend op een behandeling geven wij vast de (verbazingwekkende) resultaten voor een kleurenbeeld dat niet al te complex is in termen van bits die er per pixel nodig zijn - 0.25 - 0.5 bits/pixel: matig tot goede kwaliteit, toereikend voor een aantal applicaties, - 0.5-0.75 bits/pixel: goede tot zeer goede kwaliteit, toereikend voor veel applicaties - 0.75 - 1.5 bits/pixel: uitstekende kwaliteit, toereikend voor vrijwel alle applicaties - 1.5 - 2.0 bits/pixel: meestal ononderscheidbaar van het origineel, toereikend voor alle applicaties. Als men de gegevens van tabel 9 op pagina 27 bekijkt dan ziet men dat voor een CD speler al een fors hoge data rate gehaald moet worden. Gaat men van het audio naar het video domein dan neemt de data rate kwadratisch toe. Als men dan bedenkt dat het streven is om ’video on demand’ mogelijk te maken over een standaard telefoonlijn moet het duidelijk zijn dat de voornaamste inspanning daarvoor in de hoek van de codering zit. Een overzicht van state-of-the-art technieken wordt gegeven in tabel 10. MPEG staat voor ’Moving Pictures Expert Group’ en betreft een standaard die met name ontwikkeld is voor het comprimeren van video beeld. De MPEG1 standaard comprimeert 320 x 240 full motion video beelden tot een data stroom van ongeveer 1.5 Mbit/s. De nieuwe MPEG2 standaard zal 720 x 480 full motion video beelden comprimeren tot stromen in de groot orde van 10 Mbit/s en als resultaat VCR kwaliteit afleveren. Voor het comprimeren van de nieuwe standaarden als HDTV is MPEG3 bedoeld. De H.261 standaard bewandelt min of meer de omgekeerde weg: hier wordt de kwaliteit geofferd aan de mogelijkheid om over een standaard telefoonlijn
Inleiding Natuurkundige Informatica
34
1997
Enige basisbegrippen Standaard
Informatie, Codes en Compressie Qualiteit
Compressie
MPEG 1
Med-High
35:1
1.2 Mbit/s
MPEG2
High
40:1
1.65 - 60 Mbit/s
MJPEG
High
20:1
3.0 Mbit/s
H.261
Poor
200:1
tabel 10
Compressed Data Rate
50. Kbit/s
Overzicht van enige video compressie ratio’s.
de informatie te verzenden. In de rest van deze paragraaf zullen wij een paar coderingsschema’s de revue laten passeren. Het gaat hier slechts om een eenvoudige introductie. Voor een fundamentele behandeling wordt verwezen naar de definitie van de betreffende standaards. Voor een aantal toepassingen is het van belang om te weten wat de implicaties zijn van de compressie in de zin van verlies van informatie. Daarom onderscheidt men ’lossy’ en ’lossless’ technieken. In het eerste geval gaat er onherstelbaar informatie verloren bij de compressie, in het tweede geval is de volledige informatie terug te brengen. Voor zaken als gecomprimeerde filesystemen is de tweede karakteristiek een aangename eigenschap maar ook in de medische wetenschap (röntgen diagnostiek bijvoorbeeld) is men geporteerd van een dergelijke eigenschap. Voor veel films anderzijds zal het niet uitmaken als zij bij transmissie over een beperkte lijn wat aan beeldkwaliteit inboeten. Voor veel compressie technieken geldt dat zij haalbaar geworden zijn door de snelle toename van de processor technologie waardoor rekenintensieve technieken gebruikt kunnen worden. Hufman code Eén van de schema’s die in dit verband vaak toegepast wordt is de zogenaamde Hufman (1952) code. De stappen om tot een Hufman code te komen zijn de volgende. aj
P(aj)
code
a1
0.5
0
a2
0.25
10
a3
0.125
110
a4
0.125
111
tabel 11
Hufman code voor een niet uniform alfabet van 4 symbolen
(1) (2)
List alle voorkomende symbolen in volgorde van afnemende prioriteit, Voeg de twee kleinste waarschijnlijkheden samen, en sorteer opnieuw op waarschijnlijkheid, herhaal dit procédé net zolang tot er nog maar twee mogelijkheden over zijn. (3) Start voor het coderen met het eindpunt van (2). Geef het symbool met de hoogste waarschijnlijkheid ’0’ (arbitraire keuze) en de rest van de mogelijkheden ’1’ (4) Herhaal stap (3) recursief. Het is illustratief om dit proces grafisch voor te stellen. Wij zullen later op deze structuur terugkomen bij de behandeling van binaire bomen. Vooruitlopend daarop is het nu voldoende om te zegen dat de boom gelezen kan worden door een ’0’ als een hoek van +90 graden te interpreteren en een ’1’ als een hoek van -90. Het resultaat wordt gegeven in Figuur 29. De winst die we nu boeken ten opzichte van de vaste 2-bits codering is gauw bepaald. Deze is L = 0.5 × 1 + 0.25 × 2 + 0.125 × 3 + 0.125 × 3 = 1.75 bits/symbool Kortom: op deze manier zijn wij gemiddeld optimaal efficiënt. De reden voor de efficiency verhoging van
Inleiding Natuurkundige Informatica
35
1997
Enige basisbegrippen a4
Informatie, Codes en Compressie
a3 0 1
Figuur 29
a2 1
0
a1
Grafische representatie van het tot stand komen van een Hufman code. De waarschijnlijkheidsvolgorde van de symbolen is essentieel.
0
1
Start
de code is duidelijk: het meest frequente symbool wordt aangegeven met de kortste codering en omgekeerd. Voor de decodering kan men de boomstructuur van Figuur 29 in omgekeerde volgorde eenvoudig gebruiken. Op die manier is het eenvoudig in te zien dat {1110101100} correspondeert met boodschap a4, a1, a2, a3, a1 . Problematisch wordt de situatie overigens als er een fout sluipt in de transmissie. Hufman codering is op zich alleen maar practisch toepasbaar als de frequentie verdeling van de te coderen informatie van te voren bekend is. Dat en de constatering dat de frequentie verdeling van de letters over een langer stuk tekst allesbehalve uniform is heeft er toe bijgedragen dat Hufman codering vaak is toegepast op taal. Comma Codering Een kleine variant op de Hufman codering is de zogenaamde comma codering waarbij gebruik gemaakt wordt van een (soort) comma als scheiding tussen de opeenvolgende codes. Toegepast op het geval van tabel 11 levert dit de volgende code op van tabel 12. De ’0’ speelt hier de rol van de comma, de scheider van de aj
P(aj)
code
a1
0.5
0
a2
0.25
10
a3
0.125
110
a4
0.125
1110
tabel 12
Hufman/Comma code voor een niet uniform alfabet van 4 symbolen
verschillende elementen van het alfabet. Door de comma is de code extreem eenvoudig te implementeren en in zekere mate bestand tegen fouten in de transmissie omdat na de eerste volgende comma weer correct gecodeerd/decodeerd kan worden. De tol die hiervoor betaald wordt is een (kleine) verhoging van het aantal bits dat nodig is voor codering. Uit de gegevens van tabel 12 is het eenvoudig te berekenen dat de gemiddelde lengte omhoog van de 1.75 (de optimale waarde) naar de 1.875 bits/symbool. JPEG codering Als slot van de coderingen zullen wij wat opmerken over de JPEG codering. Een principe schema hiervan staan in Figuur 30. De JPEG standaard omvat een drietal stappen. Eerst wordt het input beeld verdeeld in een aantal 8 bij 8 blokken. Van deze blokken wordt de Discrete Fourier Transformatie (DFT) bepaald. Deze Fourier transformatie vormt de overgang naar het frequentie domein. Deze overgang kan nog verliesvrij uitgevoerd worden! De coefficiënten van de Fourier Transform worden vervolgens gekwantificeerd, waarbij verlies optreedt. De mate waarin hangt af van de kwantisatie. Bij de decompressie wordt het proces in omgekeerde volgorde afgehandeld. De winst bij deze compressie techniek zit met name in de vertaling van het spatiële naar het frequentie domein. Bij scènes die niet al te scherpe overgangen vertonen zullen niet te hoge frequentie componenten behoren. Dat betekent dat veel van de hogere Fouriercoefficiënten gelijk aan nul zijn wat weer goed te coderen
Inleiding Natuurkundige Informatica
36
1997
Enige basisbegrippen
Informatie, Codes en Compressie
DFT
Quantisatie
8x8 blocks
Figuur 30
Schematische representatie van de JPEG codering.
Entropie Coder
is met de Entropie encoder. Laat f(x, y) een element van het input beeld aangeven dan wordt de Discrete Fourier Transform gegeven door: 1 F ( u, v ) = --- C(u)C(v) 4
7
7
∑ ∑ x = 0y = 0
( 2y + 1 ) ( 2x + 1 ) f (x, y) cos -------------------- uπ cos -------------------- vπ 16 16
(17)
waarbij C(u), C(v) = 1 ⁄ ( 2 ) als u, v =0 en 1 anders. Hierbij moet bedacht worden dat als basis voor deze transformatie de stelling geldt, die wij hier niet bewijzen, dat elke functie, die gedefinieerd is op N opeenvolgende gehele getallen, exact geschreven kan worden als N–1
x[n] =
∑ X [ k ]ϕk [ n ]
(18)
k=0
Hoewel deze stelling veel lijkt op de Fourier transformatie in het continue geval is er één belangrijke verschil: in het discrete geval is hij exact in een eindig aantal termen! Vandaar dat de eerste compressie stap verlies vrij is. Dat is ook in te zien door te bedenken dat wij gaan van een 8 bij 8 beeld in het spatiële domein naar een 8 bij 8 representatie in het frequentie domein, waarbij de volgende basisfuncties gebruikt worden. ( 2x + 1 ) ( 2x + 1 ) (19) B ( u, v ) = cos -------------------- uπ cos -------------------- vπ 16 16 Een grijswaarde representatie van een aantal basisfuncties is gegeven in Figuur 31. Om duidelijke redenen noemt men de bijdrage van de (0, 0) functie de DC component en die van de andere de AC componenten. (0, 0)
(1, 0)
(4, 0)
(7, 0)
(0, 1)
(7, 1)
(0, 7) Figuur 31
(1, 7)
(4, 7)
(7, 7)
Grijswaarden representatie van enige basisfuncties van de Discrete Fourier Transformatie. Op de 8 bij 8 images is voor de display smoothing toegepast.
Inleiding Natuurkundige Informatica
37
1997
Hoofdstuk 3: Enige Problem Solving Environments
Enige Problem Solving Environments
3.1
Inleiding
Inleiding
In dit hoofdstuk willen wij kijken naar de ontwikkelingen die plaatsgevonden hebben in de mogelijkheden om computers te programmeren. Voor 1954 werd bijna alle programmering gedaan in zogenaamde machinetaal. Het oplossen van een probleem op een computer vereiste gedetailleerde codering van lange reeksen instructies in binaire of octale getallen. Op zich betekende dat overigens al een vooruitgang omdat nu het programma niet meer onlosmakelijk met de rekenautomaat gegeven was maar, zij het met veel moeite, door de gebruiker veranderd kon worden. Dit concept van ‘stored program computer’ (tabel 6 op pagina 12) is vermoedelijk met de Manchester University Mark I computer in Engeland voor het eerst gerealiseerd. Voor deze machine moesten instructies binair neergeschreven worden, in groepen van 5 bits gegroepeerd, en daarna vertaald naar teleprinter code opdat ze de machine ingebracht konden worden. Een stukje programma had ongeveer de gedaante als aangegeven in Figuur 32. Ondanks alle problemen was het mogelijk om zinvolle programE@HO ma’s te schrijven. In het algemeen duidt men deze (machine)talen XE/: als eerste generatie computertalen aan. Het overzetten van een prove/: gramma van het ene type computer naar het andere type was niet @@HB mogelijk omdat de machinetaal computer specifiek was. I@T/ Om de leesbaarheid van de programma’s te verhogen werden afkorI@/C tingen (mnemonics) geïntroduceerd, bijvoorbeeld ADD in plaats @@/N van de octale code 021. Het effect dat dit had op de leesbaarheid TCTA Figuur 32 Voorbeeld van machinetaal werd verder nog versterkt door het gebruik van symbolische namen in plaats van (absolute) numerieke adressen. Programma’s die op deze manier geschreven waren, konden niet meer direct door de machine uitgevoerd worden en moesten eerst geconverteerd worden in machinetaal door een zogenaamde assembler. De verzameling van alle mogelijke instructies en de manier waarop zij gebruikt konden en mochten worden (hun syntaxis) noemt men een assembler taal. De (relatieve) vooruitgang van deze manier van programmeren loadr1, #0 waarnaar men vaak refereert als de tweede generatie programmeerload r2, #60 talen wordt in Figuur 33 geïllustreerd. Enige vorm van leesbaarheid loop cmp r1, r2 is aanwezig. Bij het werken met assemblers sprak men vaak over bne ready coderen in plaats van programmeren. inc r1 De grote tekortkomingen van de eerste twee generaties zijn duidebr loop lijk: ready hlt • boven alles staat de machine afhankelijkheid van de talen. Een Figuur 33 snel vervangingstempo van de hardware, zoals nu het geval is, zou niet mogelijk zijn indien alle code steeds weer opnieuw geschreven zou moeten worden.
Voorbeeld van assembler
• het grote gebrek aan (data)structuren binnen de taal die af te beelden waren op het toepassingsgebied. Als voorbeeld kan men denken aan het veld van de lineaire algebra waarin een stelsel vergelijkingen meestal genoteerd worden als Ax=b en de oplossingen van dit stelsel formeel wordt weergegeven door x = A-1b . Het voorbeeld onderstelt dat er een notatie is voor (i) getallen, (ii) vectoren, (iii) matrices (en hun inverse). • het grote gebrek aan mogelijkheden om operaties op een voor de gebruiker zinvolle manier te noteren en eventueel aan te passen. In het bovengenoemde voorbeeld zijn dat (i) de inversie van een matrix, en (ii) matrix/vector vermenigvuldiging. In de assembler moeten deze volledig uitgecodeerd worden. Een eerste manier waarop getracht is hiervoor een oplossing te vinden is was in de vorm van zogenaamde automatic programming systems. De gebruiker denkt in termen van een abstractiemachine met andere mogelijkheden dan de werkelijke fysieke machine. Dergelijke abstractiemachines hadden met name ondersteu-
Inleiding Natuurkundige Informatica
39
1997
Enige Problem Solving Environments
Inleiding
ning voor floating-point grootheden, registers en het vereenvoudigde de input en output, zaken die op de werkelijke hardware niet of nauwelijks ondersteund werden. De abstractie machine werd gesimuleerd op de eigenlijke computer. Het schrijven van deze simulatieprogramma’s was moeilijk, het programmeren van de abstracte systemen een stuk eenvoudiger. Op het begrip abstract systeem zullen wij nog terugkomen in de context van Java (zie sectie 3.8). Een doorbraak werd bereikt door de groep onder leiding van John Backus die in 1954 begon met de ontwikkelingen van een geautomatiseerde vertaler (compiler) voor formules, die zij Fortran (Formula Translator) noemden. Omdat computers extreem duur waren, was de voornaamste zorg van de groep de efficiency van de translator, of zoals Backus [1964] het formuleerde: [Our development group] had one primary fear. After working long and hard to produce a good translator program, an important application might promptly turn up which would confirm the views of the sceptics: .... its [translated] program would run at half the speed of the hand-coded version. It was felt that such an occurence, or several of them, would almost completely block acceptance of the system. Het project van Backus bleek erg succesvol. De mythe van de superioriteit van de menselijke codeur/programmeur ten opzichte van de compiler is overigens erg hardnekkig gebleken en nog steeds zijn er die menen dat zij sneller en beter assembler (of machinetaal) kunnen programmeren dan een compiler het kan. Fortran (I) markeerde de eerste stap bij het ontwikkelen van de derde generatie talen die als belangrijke karakteristiek hadden dat zij machine onafhankelijk waren. Bij de eerste implementatie van Fortran op de IBM 704 slaagde men er ’niet helemaal’ in machine onafhankelijk te worden. De nummers die gebruikt worden bij zogenaamde output kanalen werden bepaald door de nummering van de tape drives van de 7041. Vaak wordt ook de benaming hogere programmeertalen gebruikt in deze context. Een derde generatie taal wordt gekarakteriseerd door enerzijds de definitie van de taal, en haar syntactische regels en anderzijds het bijbehorende vertaalprogramma, de compiler. Deze compiler moet voor elke nieuwe hardware (opnieuw) gemaakt worden. Bij het ontwerpen van de derde generatie talen is een hoop ervaring opgedaan met het identificeren van belangrijke en minder belangrijke, realiseerbare en onpractische uitgangspunten. Zo heeft men een tijd gewerkt aan de universele programmeertaal. Het concept is eenvoudig en er is maar één compiler nodig - voor alle verschillende soorten hardware wel te verstaan - maar geen van de voorgestelde talen heeft een brede acceptatie en/of gebruikerskring gekregen. De veelheid van talen die in Figuur 34 aangegeven is suggereert eerder het tegenovergestelde: een grote hoeveelheid verschillende talen is beschikbaar gekomen. De ontwikkelingen zijn niet gestopt bij de derde generatie. Een toenemende erkenning is er gekomen voor het belang van de data. Deze data zijn abstracte representaties van werkelijke verschijnselen of (abstracte) modellen. Geassocieerd met objecten van een bepaald type is een reeks van bewerkingen die het object transformeren in een (ander) object. Denken we weer terug aan het voorbeeld vanuit de lineaire algebra. Een matrix inversie is hier een operatie die een object van het type matrix transformeert tot weer een object van het type matrix, met de eigenschap dat het de inverse is van de originele matrix. Een ander voorbeeld is de matrix/vector vermenigvuldiging die als resultaat een (nieuwe) vector oplevert. Hiermee is een dergelijke vermenigvuldiging overigens niet meteen zinvol toepasbaar op twee matrices, daarvoor dient de vermenigvuldigingsoperator apart gedefinieerd te worden voor twee matrix objecten. Dit besef dat object en operatie bij elkaar horen en niet door elkaar gehaald moeten worden heeft geleid tot de ontwikkeling van de zogenaamde object georiënteerde talen waarvan C++, F90 en Java typische voor1.Voor de niet-Fortran(ners) onder ons: write(6, *) ’Hello world’ refereert dus nog naar tape unit 6, maar print
gelukkig op de standaard output.
Inleiding Natuurkundige Informatica
40
1997
Enige Problem Solving Environments
Inleiding
APL Fortran I
Fortran II
Fortran IV
Fortran 77
Fortran 90
C
C++
Simula 67 ADA
PL 1
Pascal Algol
Algol 60
Flowmatic
Cobol
IPL 5
Lisp
1950 Figuur 34
Algol 68
1960
1970
1980
Overzicht van de ontwikkeling van enige derde generatie programmeertalen.
beelden zijn. Om enig gevoel te krijgen voor het begrip object georiënteerd is in Figuur 35 de opzet van een // the definition of the class #ifndef vec_h #define vec_h class Vector { public: Vector(int n, float value = 0); Size() { return nel; } private: float *f; int nel; };
#include <stream.h> #include "vec.h" void {
main() Vector A(10), B(20, 2.);
printf("Size of the vector is: %d\n", A.Size()); }
// the code for the vector creation Vector::Vector(int n, float value) { if(n < 1) { n=1 cerr << "Error in size of vector, reset to 1\n"; } f = new float[nel = n]; for(int i=0; i
Figuur 35
Beperkt voorbeeld van een object (vector) in C++ waarvan de constructor gedefinieerd is.
klasse voor een (floating point) vector gedefinieerd. Rechts ziet men de aanroep. Het cruciale bij de constructie van de vector, die gedeclareerd is in de procedure Vector::Vector, is dat de allocatie van geheugen voor de vector (call naar new) niet te scheiden is van de assignatie van het aantal elementen (nel). In ’ge-
Inleiding Natuurkundige Informatica
41
1997
Enige Problem Solving Environments
Inleiding
woon’ C is het niet mogelijk om op een dergelijke manier de beide zaken te koppelen. Voorts is het opvragen van de grootte van de vector (Size) deze functie als het ware gekoppeld aan de vector A. Ook dat is in C niet op deze manier voor elkaar te krijgen. Een tweede tendens, naast de object oriëntatie, is de erkenning dat een programma opgesplitst moet kunnen worden in delen die op verschillende computers, gekoppeld door een netwerk, uitgevoerd worden. Dit heeft geleid tot de zogenaamde client/server technologie. De derde generatie talen waren redelijk ‘kaal’. De gebruiker moest een grote hoeveelheid functies zelf programmeren of toevoegen wilde hij een bepaald probleem (kunnen) oplossen. De beperkende werking hiervan heeft geleid tot nieuwere talen, die completer zijn en die wij in de rest van dit dictaat ook wel aan zullen duiden met de benaming ‘Problem Solving Environments’. Als werkdefinitie zullen wij hanteren: Een Problem Solving Environment (PSE) is een applicatiedomein specifieke omgeving waarin (oplossingen voor) problemen eenvoudig genoteerd en efficiënt bepaald kunnen worden. De factoren die hierbij een rol spelen - maar die ook van toepassing zijn op de derde generatie talen - worden symbolisch aangegeven in Figuur 36. Dit schema zal ook dienen als richtsnoer voor de selectie van onderProbleem
Data
Hardware/
Algoritme
Technologie
Software
Objecten programmering
afbeelding PSE
User
(G)UI Figuur 36
Samenhang van de componenten die van belang zijn bij het gebruik van een programmeeromgeving (of PSE).
werpen die in dit dictaat verder aan de orde gaan komen. De beperking die wij ons opleggen bij de natuurkundige informatica zit in de selectie van probleemvelden die van belang zijn voor de fysica. Het is ondoenlijk om een overzicht te geven van omgevingen dat ook maar enigszins pretendeert volledig te zijn. In tabel 13 worden een aantal courante omgevingen genoemd met hun belangrijkste toepassingsgebieden. In de rest van dit hoofdstuk zullen wij een paar voorbeelden van dergelijke omgevingen bespreken een aangeven wat de relatie is tussen probleemstelling en structuur van de PSE. Hoewel omgevingen zich zeer divers kunnen voordoen aan de gebruiker is er onder de oppervlakte vaak sprake van een grote hoeveelheid overeenkomsten. Aan het begin van deze discussie zullen we een aantal van dit soort principes de revue laten passeren.
Inleiding Natuurkundige Informatica
42
1997
Enige Problem Solving Environments
Applicatie Gebied
Voorbeeld
Internet
Netscape, Info Explorer
Administratief Animatie
MS-Office, Lotus Notes, ... 3D Studio, Data Explorer, Data Visualizer
Mogelijkheden HTML viewing E-mail, ftp Spreadsheet, Database, Tekstverwerking, Presentatie, Netwerking Rendering, Modellering, Animatie,
Wiskunde
Mathematica, Axiom, Maple
Numerieke berekeningen, Formele Manipulatie, Graphics
Interfacing/ Process Controle
LabView, HP-VEE, Chorus,
Intrument controle, Data acquisitie, Graphics
Wetenschappelijk rekenen
Fortran 77, Fortran 90, C, C++, Java
Numeriek rekenwerk, enige debug faciliteiten.
Dataverwerking tabel 13
3.2
Enige basisbegrippen van talen
Splus, Matlab, SPSS
statistische analyse, signaalverwerking
Voorbeelden van enige applicatie gebieden en Problem Solving Environments.
Enige basisbegrippen van talen
Het is onvermijdelijk om bij het bespreken van talen, omgevingen en systemen een aantal definities te poneren die vaak gebruikt worden ter karakterisatie. Als een start van de bespreking van omgevingen en talen zullen wij hiermee beginnen. • Wetenschappelijke talen/systemen Hieronder verstaan wij talen die met name ontworpen zijn voor het manipuleren van numerieke gegevens. De Fortran dialecten zijn de meest bekende voorbeelden. Karakteristiek is dat zij beschikken over datatypes als complexe getallen, en trigoniometrische functies e.d. aan de gebruiker ter beschikking stellen. Daarnaast is uiteindelijke snelheid van de uiteindelijke code van vitaal belang. • Systeem programmeertalen Hieronder vallen talen die met name gebruikt worden voor het schrijven van een operatingsysteem. Dergelijke talen worden meestal gekenmerkt door een zekere mate van machine afhankelijkheid. Een overbekend voorbeeld is C. • Commando taal In de enge zin bedoelt men hiermee de interface tussen een computergebruiker en het operatingsysteem. In die zin vormt de commando taal de verzameling van alle mogelijke opdrachten die een gebruiker aan het computer systeem kan geven. In meer globale zin kan men met de structuur van elk programma een commando taal associëren. Binnen Unix zijn de diverse ’shells’, zoals de Bourne Shell, de C Shell, de Korn Shell etc., voorbeelden van een commando taal. Voor IBM systemen is JCL (Job Control Language) het notoire voorbeeld. • Procedurele taal Taal die de gebruiker toestaat om de volgorde waarin statements uitgevoerd worden te specificeren. De meeste derde generatie talen zijn hiervan voorbeelden. Het samenvoegen van stukken programma in procedures noemt men ook wel procedurele abstractie. • Niet-procedurele taal Bij deze talen ligt het accent niet op de specifieke manier waarop iets moet gebeuren om een oplossing te bereiken, de nadruk ligt veeleer op wat er gedaan moet worden. Dat wordt geïmpliceerd door de data. Het meest bekende voorbeeld van een dergelijke taal is een rapport generator zoals men die in een data-
Inleiding Natuurkundige Informatica
43
1997
Enige Problem Solving Environments
Enige basisbegrippen van talen
base aantreft. Gegeven de structuur van de data wordt het rapport automatisch gegenereerd. • Dataflow taal Een dataflow taal staat toe om de uitwisseling (flow) van data tussen procedures te specificeren om zo op een inherente manier mogelijke parallellismen te kunnen noteren. Een dataflow sluit nauw aan bij het werken met (visuele) beslissingsschema’s en wordt daarom ook, in toenemende mate, gebruikt bij de visuele front ends van omgevingen. Ter illustratie staat in Figuur 37 een dataflow representatie van xi yi Sx
N
N Sxx -
Sxx
SySxx - SxSxy
SxSx
/
Figuur 37
Sy
NSxy - SxSy
/
a
Sxy
b
Berekening van een kleinste kwadraten y = a + bx aanpassing volgens een data flow methode
een kleinste kwadraten aanpassing. Uitgaande van een set gegevens, worden eerst het aantal elementen N, alsook de sommen van de elementen bepaald. Deze berekeningen, kunnen, zoals blijkt uit de flow allemaal onafhankelijk gedaan worden. Nadat deze bepaald zijn kunnen de elementen voor fit constantes a en b uitgerekend worden, die in de laatste slag bepaald worden. De logica van dit schema is dat de volgende stap pas genomen kan worden als alle input gegevens beschikbaar zijn. Een dataflow executie model is dan ook eenvoudig te implementeren: het is voldoende om een lijst bij te houden van alle operaties en het aantal input parameters dat bekend is. • Object georiënteerde taal Voorlopig zullen wij die talen object georiënteerd noemen waarin de data objecten actief zijn en functionele eigenschappen hebben die normaal met programma’s (of delen daarvan) geassocieerd worden. Actieve data objecten kunnen reageren op boodschappen van buiten, en daardoor getriggered operaties uitvoeren. Smalltalk is een van de meest bekende voorbeelden van een object georiënteerde taal hoewel men ook geneigd is om C++, F90 en Java er toe te rekenen. Met Figuur 35 hebben wij het object reeds geïntroduceerd in een engere context. • Real-time taal Taal die het programmeren van procedures die gelijktijdig uitgevoerd kunnen worden toestaat. Deze procedures worden binnen een gegarandeerde tijdsrespons na een extern signaal geactiveerd. Concurrent Pascal en Ada zijn voorbeelden van real-time programmeertalen. Meer globaal verstaat men onder real-time het geven van een respons binnen een vantevoren vastgestelde tijd. Deze omschrijvingen moeten niet verward worden met definities. Ze zijn ook niet uitsluitend: een actuele taal kan in meerdere categorieen vallen. Het uitvoeren van een programma geschreven in een taal duidt men meestal aan als de executie van dat programma. Hiervoor bestaan twee mogelijkheden: compilatie en interpretatie. Compilatie betekent dat het programma geschreven in een programmeertaal vertaald wordt naar een func-
Inleiding Natuurkundige Informatica
44
1997
Enige Problem Solving Environments
Enige basisbegrippen van talen
tioneel equivalent programma, het object programma, in de machine taal van de machine waarop het uitgevoerd moet worden, de target machine. Dit proces is geïllustreerd in Figuur 38. Hier wordt formeel een onHost Machine
Target Machine
Compiler Object Programma
Source Programma Programma Input
Programma resultaten Diagnose, Listing Figuur 38
Symbolische representatie van compilatie.
derscheid gemaakt tussen de host en de target machine. Meestal zullen die identiek zijn maar dat hoeft niet zo te zijn. Als voorbeeld kan men denken aan een geld automaat (ATM). De kleine processor die hierin zit is niet de ideale ontwikkelomgeving van de benodigde software. Verschillen host- en target machine dan spreekt men over een cross-compiler. Bij interpretatie wordt het input programma niet vertaald maar direct uitgevoerd door een interpreter, het programma wordt dus gezien als input van deze interpreter. De interpreter zelf draait op de target machine en voert statement voor statement het input programma uit. Een interpreter biedt een veel grotere flexibiliteit Host Machine Source Programma
Interpreter Programma resultaten
Programma Input Figuur 39
Representatie van het interpretatie proces.
dan een compiler, de prijs die echter betaald moet worden is snelheid. Een interpreter is altijd trager dan een compiler. Teneinde dat in te zien is het goed om te bedenken dat een compiler de code kan analyseren en, bij de uiteindelijke object code, kan anticiperen op ’het volgende statement’. Dat is voor een interpreter niet mogelijk. Wil men dus snelheid halen dan is compilatie onvermijdelijk, maar er moet wel eerlijkheidshalve bij aangetekend worden dat de compilatie zelf ook tijd kost. Een genormeerde vergelijking van de executiesnelheid van een test programma gecodeerd in een aantal verschillende talen wordt gegeven in tabel 14 waarin 2 compilers en 3 interpreters met elkaar vergeleken worden. Er is gebruik gemaakt van één probleem dat in de vijf omgevingen geïmplementeerd is en in alle gevallen functioneel hetzelfde antwoord opleverde, echter niet in dezelfde tijd. Het maximale tijdsverschil is een factor 300! Om dit in perspectief te zetten moet men bedenken dat een factor 2 in prestatie gewonnen wordt per 18 maanden. Het verschil tussen C en Mathematica correspondeert hier dus met de opbrengst van een tijdsperiode van 18 jaar. Executie snelheid is dus een belangrijke overweging bij de selectie van een omgeving of programmeertaal. Een tweede karakteristiek, naast het executie model, is de hoeveelheid voorgedefinieerde datastructuren en operaties, immers dit bepaalt ook de hoeveelheid tijd die nodig om de oplossing te realiseren. In C zijn, in
Inleiding Natuurkundige Informatica
45
1997
Enige Problem Solving Environments
User interface
Omgeving
Type
Relatieve Snelheid
C
Compiler
1.
C++
Compiler
0.5
Lisp
Interpreter
0.1
Maple
Interpreter
0.001
Mathematica
Interpreter
0.0003
tabel 14
Vergelijking van efficiency van diverse omgevingen voor implementatie van Buchberger basis
tegenstelling tot Fortran, niet intrinsiek complexe getallen gedefinieerd. Het is eenvoudig om een datastructuur te definiëren in C die dit manco opheft maar om ook functies met een complex getal als argument toe te voegen is een veel meer omvattende taak. Dit wordt onderstreept door de code die in Figuur 40 gegeven typedef struct float float } CMPLX; void
complex { re; im;
program cmplx c declaratie COMPLEX A, B, C; c assignatie A = (3, 1) B = (4, -2) c addition and print C=A+B print *, C stop end
Add(CMPLX *A, CMPLX *B, CMPLX *C);
{ C->re = A->re + B->re; C->im = A->im + B->im; } void {
main() CMPLX A, B, C; A.re = 3;A.im = 1; B.re = 4; B.im = -2; Add(&A, &B, &C); printf("(%f,%f)\n", C.re, C.im);
Figuur 40
}
Equivalente code in C (links) en Fortran77 (rechts) voor het optellen van twee complexe getallen.
wordt voor het optellen van twee complexe getallen in C (waar de gebruiker alles zelf moet doen) en in Fortran (waar het type bekend is).
3.3
User interface
Een laatste punt dat enige toelichting behoeft alvorens wij een paar omgevingen nader gaan bekijken is het begrip user interface. De ontwikkeling van de user interface is niet los te denken van de snelle ontwikkeling van de grafische mogelijkheden van het computerscherm. De eerste computers waren voorzien van zogenaamde ascii buizen waarop alleen een voorgedefinieerde characterset (met vastgestelde grootte) afgebeeld kon worden. Zo er al sprake was van grafische mogelijkheden, werden deze gerealiseerd door speciale symbolen die op de terminal gedefinieerd werden. Ook hier gold dat de leeftijd van de terminal dermate lang was dat het loonde om code specifiek voor een bepaalde terminal te gaan ontwikkelen. Een erg bekend voor-
Inleiding Natuurkundige Informatica
46
1997
Enige Problem Solving Environments
User interface
beeld in deze is de zogenaamde VT100 terminal. Met name Tektronix heeft baanbrekend werd gedaan op het gebied van ontwikkeling van buizen die (ook) grafische informatie konden weergeven. De methode waarop dat gedaan werd was zonder uitzondering analoog en met buizen. De echte versnelling in de user interface ontwikkeling is gekomen met de zogenaamde all points addressable schermen waarbij het scherm opgebouwd werd uit een (groot) aantal punten (pixels). Tegenwoordig is een scherm van 1280 bij 1024 min of meer standaard geworden. In vrijwel alle gevallen betreft het kleurenschermen. De snelle ontwikkeling bij de computers heeft als neveneffect gezorgd voor een spectaculaire prijsdaling van de overeenkomstige TV buizen. Afbeeldingen worden op het scherm gezet door groepen van pixels te schakelen. Het is overigens wel goed om te onderstrepen dat het afbeelden op het computerscherm een discretisatie betekent. Wij komen hierop in het volgende hoofdstuk nog terug. Xerox, Apollo (later opgegaan in HP) en Apple (op aan het gaan in MicroSoft ?) zijn de drijvende krachten achter de grafische user interface geweest. De eerste twee bestaan niet meer als zelfstandig bedrijf de derde maakt de laatste jaren zware crises door. Blijkbaar loont een goede ontwikkeling niet altijd (de wet op de remmende voorsprong). Het grafische scherm vormt het ene ingrediënt. Een manier om met dit scherm interactie te hebben is het andere benodigde, dat is de muis geworden. Het doel van de muis is om een (twee dimensionale) beweging te detecteren en af te beelden op het scherm. Hiertoe wordt gebruik gemaakt van het feit dat elke beweging in een twee dimensionaal vlak opgedeeld kan worden in twee onafhankelijke bewegingen in orthogonale richtingen. Dit is schematisch aangegeven in Figuur 41. Naast een mechanische Figuur 41
y
Veel voorkomende realisatie van een muis. De translatie van een rubberen bal (b) wordt omgezet in de onafhankelijke beweging van twee assen waarvan optisch de rotatie gedetecteerd wordt.
b x
constructie is er ook een optische mogelijk, en een magnetische. De laatste, die als voordeel heeft dat er een absolute positie bepaling gedaan kan worden wordt meestal gebruikt bij (dure) CAD/CAM (Computer Aided Design/Computer Aided Manufacturing) systemen. Door de muis worden meestal dus relatieve positie veranderingen doorgegeven. Deze worden door het operating systeem geïnterpreteerd en afgebeeld op het systeem. Het hele concept van user interfaces is nu gebaseerd op een interpretatie van gebeurtenissen die de muis registreert, men spreekt in dit soort gevallen vrijwel altijd van events. De muis zelf kan een bepaald gebied binnen komen of verlaten. Hoewel men hiermee al een behoorlijk complexe userinterface zou kunnen bouwen, zou het toch weinig intuïtief zijn. Vandaar dat op de muis één (Apple), twee (MicroSoft), drie (SUN) of vier (IBM CAD/CAM) knoppen toegevoegd zijn. Met de positie wordt de toestand van de knoppen geregistreerd (down/up, click, or double click). Om tenslotte de events te ordenen wordt er ook nog een zogenaamde timestamp aan toegevoegd, een tijdsvolgorde aan toegekend. De Grafische User Interface (GUI) krijgt de events van het operating systeem en moet zelf zorgen voor een zinvolle afhandeling van alle gebeurtenissen. De afhandeling moet in elk geval in real-time plaatsvinden (vandaar de zandloper om de gebruiker tot rust te manen) en vindt plaats op een in principe onvoorspelbare, zogenaamde interrupt basis. Teneinde de interactie te stroomlijnen zijn de meeste programma’s tegenwoordig voorzien van een zogenaamd ’point-and-click’ interface, d.w.z. een bepaald gebied wordt gemarkeerd (b.v. File) en het aanclicken leidt tot een actie. Tot de standaard bouwstenen van een GUI behoren allerlei vormen van menu’s (pull down, pop up) al dan niet voorzien van scroll bars. Het voordeel van de menu’s is dat zij de keuze inperken. Als uit een menu lijst een file gekozen kan worden kan de gebruiker niet een invalide keuze maken als er
Inleiding Natuurkundige Informatica
47
1997
Enige Problem Solving Environments
C/Fortran
alleen maar toegestane filenamen in de lijst opgenomen zijn. Vermoedelijk belangrijker dan het point en click systeem op text items is het gebruiken van zogenaamde pictogrammen of iconen, d.w.z. grafische realisatie van een actie. Een prullenbak lijkt een universele interpretatie te hebben van een plaats waarin dingen horen die weg mogen. In de XWindows standaard van het MIT wordt een groot aantal van de vrhijheidsgraden van de GUI vastgelegd en een programmeermodel voor schermen gedefinieerd dat als de facto standaard is gaan functioneren. Ook voor het ingeven van waarden kan men eenvoudig grafische representaties maken. Een grafische tegenhanger van een schuif (slider) kan dienen om een getal een waarde te geven tussen twee uitersten. Met de ontwikkeling van de grafische schermen is ook hard gewerkt aan de software voor de grafische userinterfaces. Dit heeft geresulteerd in zogenaamde GUI (spreek uit goe-ie) builders. De GUI builder kan gezien worden als een niet-procedurele taal waarin aan elk attribuut van de userinterface een aantal acties vastgeknoopt worden. Een hypothetisch voorbeeld is gegeven in Figuur 42. De knop (quit) is door de gebruiker Event Event Event Event Event
quit
Figuur 42
Mouse _Left_Click Mouse_Right_Click Mouse_Enter Mouse_Leave Mouse_Left_Double_Click
call do_quit(Event); call do_help(Event); call do_shade(Event, quit_button); call do_unshade(Event, quit_button); call ignore(Event);
Voorbeeld van een hypothetische code voor een GUI-builder met de knop (quit_button) worden een aantal acties geassocieerd die afhangen van events die door de muis afgeleverd worden.
gedefinieerd en een relatie is gelegd tussen events van de mouse en gebruiker gespecificeerde functies die aangeroepen moeten worden. Het hoofdprogramma loopt voortdurend in een loop zoals aangegeven in Figuur 43. Met enige retorica is te snappen dat een forse hoeveelheid rekenkracht nodig is om een dergelijke user interface vlot af te handelen. for_ever_do { get_next_mouse_event(event); process_event(event); }
Figuur 43
Loop die door de GUI doorlopen wordt.
Nog steeds heeft iedereen zijn eigen opvatting over de ideale user interface maar er lijkt zich enige standaardisatie af te tekenen. Overigens begint de user interface de grenzen van het grafische te overschrijden. De grote beperking van grafische systemen is het begrip dat men aanduidt met screen cluttering of te wel het (te) vol worden van het scherm door een overvloedig aantal iconen. Het werken in een window omgeving met een buis kleiner dan 17 inch is eigenlijk niet mogelijk. Ook andere vormen van interfacing zijn in opmars: spraakherkenning is duidelijk uit de laboratorium fase en op weg om geïntegreerd te worden met systemen, vormen van tactiele terugkoppeling worden bestudeerd enz. enz. In plaats van een rigide theoretische behandeling van aspecten die wij tot nu toe naar voren gebracht hebben willen wij ervoor kiezen om enigszins terloops een aantal omgevingen de revue te laten passeren en via een paar kleine voorbeelden proberen gevoel te krijgen voor de voor en nadelen van die omgeving.
3.4
C/Fortran
3.4.1
Case study
Bij de interactie tussen deeltjes is de potentiaal de bepalende grootheid. De fenomenologische Lennard-Jones potentiaal bijvoorbeeld wordt binnen moleculaire dynamica toepassingen zeer veel gebruikt. Deze po-
Inleiding Natuurkundige Informatica
48
1997
Enige Problem Solving Environments
C/Fortran
tentiaal brengt een afstoting op korte dracht en een aantrekking op langere dracht in rekening en wordt gegeven door:
σ 12 σ 6 V ( r ) = 4ε --- – --- r r
(20)
Waarden voor genormeerde sterkte van de potentiaal ε en de eenheidsafstand σ zijn voor een aantal systemen bekend2. Bij practische berekeningen moet men oppassen om de potentiaal niet ’te dicht’ te naderen, omdat dan de waarde van de potentiaal snel de neiging heeft om erg groot te worden (duidend op een grote afstotende kracht). Wil men uitrekenen hoe een systeem van deeltjes onder invloed van een dergelijke potentiaal gaat bewegen dan moeten de bewegingsvergelijkingen opgelost worden. Essentiële stap hiervoor is dat de kracht, de afgeleide van de potentiaal, berekend wordt, immers er geldt:
E (r ) = –
∂ V (r)eˆ r ∂r
(21)
Voor systemen waarin meer dan één deeltje aanwezig is moet gesommeerd worden over de individuele bijdagen van alle deeltjes. Met name recentelijk is er grote belangstelling voor dit soort berekeningen binnen de fysica omdat zij een rol spelen bij de zogenaamde Atomic Force Microscopy (AFM) waarbij variaties in de kracht tussen een tip een een oppervlak worden geïnterpreteerd in hoogte profielen. Een voorbeeld van een geanalyseerde meting
Figuur 44
STM Image of oxygen atom lattice on Rhodium single crystal. Courtesy of Perdue University, 4 nm scan.
binnen AFM ziet men in Figuur 44. Hoewel het rekenwerk op zich eenvoudig is moet voor het construeren van een dergelijke plaatje op een groot aantal data punten de potentiaal geëvalueerd worden. Voor serieuze toepassingen is het werk dermate groot dat er met parallelle systemen gewerkt moet worden. Rekentijden zijn hier dus van groot belang. In de case study zullen we proberen wat eigenschappen van de potentiaal te verkennen.
3.4.2
Achtergrondinformatie
De computertaal C is ontwikkeld door B.W. Kernighan en D.M. Ritchie in 1972 in Bell Laboratories, Murray Hill, New Jersey. De eerste machine waarop het draaide was een PDP-7. De taal was met name bedoeld voor het schrijven en ontwikkelen van het operating systeem Unix en is hiermee onlosmakelijk verbonden gebleven. Het is goed om te bedenken dat C niet in eerste instantie ontwikkeld is voor wetenschappelijk rekenwerk! Een formalisatie van de taal heeft plaats gevonden in 1989 door middel van de vaststelling van de zogenaamde ANSI standaard3. De verschillen tussen ANSI-C en het zogenaamde K&R C zijn niet vreselijk 2.Voor
vloeibaar Argon zijn zij: σ = 0.34 × 10 – 9 m en ε ⁄ k B
Inleiding Natuurkundige Informatica
49
=
120K
. 1997
Enige Problem Solving Environments
C/Fortran
groot, maar maken ANSI wel eenvoudiger te hanteren. In 1986 heeft de opvolger van C zich aangediend in de vorm van het object georiënteerde C++, waarvan C een subset is. Eenzelfde relatie bestaat tussen Fortran77 en Fortran90. De programmeertaal C wordt gerekend tot de hogere programmeertalen dat wil zeggen de architectuur van de machine waarop men draait is (haast) niet zichtbaar in de taal zelf. Derhalve vertonen programma’s die geschreven worden in een dergelijke taal een grote vorm van ’portabiliteit’ dat wil zeggen overdraagbaarheid van machine naar machine. In tabel 15 wordt een overzicht gegeven van de datatypes die er in C onGroup
Sub-types bit
and/or/not
short/int/long
Numeriek
float/double Text
Misc
tabel 15
Operaties
+,-,/,*
Character
assignatie, test
String
geen
Pointer
assignatie
Structure
assignatie
Vector
assignatie
Overzicht van de meest belangrijke types die in C gedefinieerd zijn.
dersteund worden. Het feit dat C ontworpen is met operating systeem toepassingen in het achterhoofd komt naar voren in het feit dat bits makkelijk gemanipuleerd kunnen worden. C wordt meestal als gecompileerde taal gebruikt hoewel er ook C-interpreters in omloop zijn. Op vrijwel alle Unix systemen is de compiler met cc aanroepbaar. Een commando als cc myfile.c zal de compiler aanroepen en aannemen dat in myfile.c een C programma staat dat vertaald moet worden naar machinetaal. Standaard zal dat resultaat programma a.out genoemd worden. Als programmeertaal voor andere zaken dan operating systeem programmering is C ’kaal’. Zo zijn de standaard mathematische functies als machtsverheffen, en de trigoniometrische functies niet in de taal zelf gedefinieerd! Zij worden toegevoegd aan de taal door middel van zogenaamde bibliotheken d.w.z. verzamelingen van functies. Voor ons probleem hebben wij machtsverheffen nodig. Deze functie is, tesamen met een groot aantal andere, in de mathematische bibiotheek m gecodeerd en kan meegegeven worden aan de compilatie op de volgende manier cc myfile.c -lm Deze toevoeging is wel nodig maar niet voldoende omdat ook nog een type declaratie van de gedefinieerde functies gegeven moet worden. Daarnaast is het handig om ook een aantal vaak voorkomende constantes te definiëren (zoals pi, e, etc.). Deze type declaraties en constantes worden gedefinieerd in het bijbehorende header file math.h dat toegevoegd moet worden aan de source van het programma.
3.American
National Standard X3.159 - 1989
Inleiding Natuurkundige Informatica
50
1997
Enige Problem Solving Environments
3.4.3
C/Fortran
Uitwerking Case study
Het is gebruikelijk geworden om een probleem op te delen in een (groot) aantal (kleine) functies die elk een wel gedefinieerde taak uitvoeren. Men noemt dit formeel wel procedurele abstractie. Een mogelijke implementatie van de code om de Lennard Jones potentiaal uit te rekenen en het nulpunt van de potentiaal te vinden wordt gegeven in Figuur 45. De parameters van de potentiaal worden als constanten gedefinieerd. Voor #include
<math.h>
#define EPSILON #define SIGMA #define TOL
main() { double pos = .2, neg = 1.5, new, fnew, LJ(), deriv();
1. 1. .001
#define FABS(X) (((X) > 0) ? (X) : -(X)) printf("Argument: Function value\n"); while(FABS(pos - neg) > TOL) { new = (pos + neg)/2.; fnew = LJ(new); printf("%.3f: %.3f\n", new, fnew); if(fnew > 0) pos = new; else neg = new; }
double LJ(double arg) { double sr12, sr6; sr6 = pow(SIGMA/arg, (double) 6.); sr12 = pow(SIGMA/arg, (double)12.); return 4. * EPSILON * (sr12 - sr6); } double deriv(double f(), double arg) { double h = 1.001 * arg; return (f(arg+h) - f(arg))/h; } Figuur 45
}
C code voor het uitrekenen van de Lennard Jones potentiaal en het bepalen, volgens een bisectie methode van het nulpunt van de potentiaal.
de lezer blijft er niet veel over van de oorspronkelijke notatie van de potentiaal uit vergelijking (20). Met name de functie ’pow’ maakt één en ander niet direct leesbaar. Voor het differentiëren van de functie moet een beroep gedaan worden op een numerieke methode, die geArgument: Function value 0.850: 17.515 1.175: -0.942 1.012: -0.267 0.931: 3.270 0.972: 0.886 0.992: 0.202 1.002: -0.055 0.997: 0.067 1.000: 0.005 1.001: -0.026 1.000: -0.011 Figuur 46
Output van het C programma dat in Figuur 45 gegeven is.
bruik maakt van de benadering door een differentiequotiënt. Ook de procedure voor het vinden van een nulpunt van een functie is numeriek gecodeerd. Er wordt hier gebruik gemaakt van een zogenaamde bisectie algoritme. Wij komen hierop terug in hoofdstuk 4. Het vinden van een relatie tussen r en σ wordt op die manier moeilijk. Het grote voordeel van C in dit geval moet gevormd worden door de snelheid waarmee het
Inleiding Natuurkundige Informatica
51
1997
Enige Problem Solving Environments
Mathematica
programma uitgevoerd moet worden. Voor de presentatie van de gegevens op een andere manier dan een strikt numerieke is men (alweer) aangewezen op additionele bibliotheken en/of andere applicaties. Zeker het produceren van een grafische representatie als in figuur 44 op pagina 49 vereist veel meer inspanning. Het verschil met een codering van het probleem in Fortran 77 is gering. Het meest opvallend is dat in de definitie van Fortran alle functies die bij C in ’math.h’ zitten inbegrepen zijn. Met spreekt in dat geval van intrinsieke functies. Voor het overige is de opbouw van het programma vrijwel identiek. Het uitrekenen van het krachtoppervlak is een relatief eenvoudige zaak die ongeveer op de volgende manier geprogrammeerd zou worden for(alle x punten do) for(alle y punten do) for(alle atomen do) sommeer potentiaal done done done
Figuur 47
Pseudocode voor het bepalen van de potentiaal van een verzameling van deeltjes.
3.5
Mathematica
3.5.1
Case Study
Als case study bestuderen wij de gedempte harmonische oscillator. Wij zullen proberen te laten zien hoe die bestudeerd kan worden met behulp van Mathematica en waar die manier verschilt van een derde generatie programmeertaal. We volgen Fowles & Cassiday paragraaf 3.4 op blz. 86. Een massa m hangt aan een veer met veerconstante k. We nemen aan dat de demping lineair is met de snelheid. De uitwijking van de massa wordt dus tegengegaan door twee krachten: de veerkracht -kx en de demping -c x˙ . Het invullen hiervan in de tweede wet van Newton, F=ma, levert de bewegingsvergelijking. mx˙˙ + cx˙ + kx = 0 (22) Door invullen van x = eλt en gebruik van de volgende substituties,
c γ = ------- ; 2m
ω0 =
k ---- ; m
ωd =
2
2
γ – ω0
(23)
vindt men na enig rekenwerk de volgende oplossing voor x:
x = A1 e
( – γ + ω d )t
+ A2 e
( – γ – ω d )t
(24)
Vergelijk voor het vinden van deze oplossing de methode van Fowles & Cassiday met de methode uit het Calculus dictaat, paragraaf 7.3.2, blz. 181. (Let op: in Fowles & Cassiday heet ωd aanvankelijk q). A1 en A2 zijn constanten die bepaald kunnen worden door het stellen van twee randvoorwaarden, bijvoorbeeld door het vastleggen van beginpositie en beginsnelheid. Dit zou een algemene formule geven die de oscillator onder alle omstandigheden beschrijft. In Fowles & Cassiday wordt dit niet gedaan, maar wordt gekozen voor een andere aanpak. Afhankelijk van de grootte van ω0 ten opzichte van γ kan ωd reëel of imaginair zijn. Dit correspondeert met de drie karakteristieke toestanden van de oscillator:
Inleiding Natuurkundige Informatica
52
1997
Enige Problem Solving Environments
Mathematica
• overdemping (ω is reëel en > 0) • kritische demping (ω = 0) • onderdemping (ω is imaginair) In Fowles & Cassiday wordt voor alle drie deze gevallen apart de oplossing herschreven. In de eerste twee gevallen naar reële exponenten, in het laatste geval naar sinus en cosinus termen. Hiervan worden kwalitatieve plaatjes geschetst die de verschillende bewegingen karakteriseren.
3.5.2
Achtergrond informatie
Mathematica versie 1 is in 1988 geannonceerd. Het pakket is ontwikkeld door Stephen Wolfram. Doel van het pakket is om symbolische en numerieke berekeningen op wiskundig gebied te kunnen uitvoeren. Mathematica niet uniek; er zijn meer pakketten verkrijgbaar die functioneel min of meer identiek zijn, voor de volledigheid noemen wij er een aantal: Axiom (IBM), MatLab en Maple. Mathematica is op een groot aantal platformen verkrijgbaar (MacIntosh, MS-DOS/Windows, UNIX) hoewel het zo ’resource intensief’ is dat men wel een krachtig systeem nodig heeft om interactief met het pakket te kunnen werken. De huidige versie is 3.01. Mathematica is een interpreter, dat wil zeggen in principe krijgt de gebruiker direct antwoord. In tegenstelling tot C en Fortran probeert Mathematica zo lang mogelijk met formele variabelen te werken en pas op het laatste ogenblik over te schakelen naar een numerieke evaluatie. Als zodanig sluit het veel dichter aan bij ons wiskundig denken. Een typisch voorbeeld van een eenvoudige Mathematica sessie wordt gegeven in Figuur 48. De vierkantsvergelijking wordt met behulp van Mathematica analytisch opgelost en met de worIn[1] := Solve[ ax^2
Out[1] =
Figuur 48
+ bx + c== 0 , x]
2 2 – b – b – 4ac – b + b – 4ac -------------------------------------------------------------------------- → → x x , 2a 2a î î î
Voorbeeld van het symbolisch oplossen van een vierkantsvergelijking in Mathematica
tels kan verder gewerkt worden, standaard worden eerst de negatieve en dan de positieve wortels gegeven. Mathematica is gebaseerd op het idee van ’lijsten’ (zie bij sektie 4.3.3) waarin willekeurige elementen opgenomen kunnen worden. Voor een dergelijke lijst, en dus voor het werken met Mathematica maakt het niets uit of men een formule hanteert of een (serie) getal(len). De structuur van Mathematica is complexer dan die van C. Het eigenlijke werk wordt gedaan door de zogenaamde kernel van Mathematica die tekstgeoriënteerd is. Deze wordt opgestart als men het commando math geeft. De hierboven gegeven voorbeelden zijn communicatie met de kernel. Boven op de kernel draait een geavanceerdere user interface die het notebook heet. Deze wordt gestart met het intikken van mathematica. Notebooks zou men kunnen zien als een combinatie van een tekstverwerker en Mathematica: niet alleen kunnen wiskundige expressies ’getypeset’ worden, maar ook kunnen ze uitgerekend worden in dezelfde omgeving! Een voorbeeld van de interface wordt gegeven in figuur 50 op pagina 54. Teneinde de interface te kunnen begrijpen moet het volgende opgemerkt worden. De interface is een combinatie van tekst en opdrachten aan Mathematica (bijv. Table[Random[], {100}, {100}], die een random array genereert van 100 bij 100). Het resultaat van een bewerking van een dergelijke iets (bijvoorbeeld de ListPlot van de eigenwaarden) kan weer in de interface geïmporteerd worden. Hierdoor lijkt het misschien een platte tekst, maar als de dimensie van bijvoorbeeld de tabel veranderd wordt, dan zal het plaatje zich ook aanpassen. De definitie van Mathematica is niet vastgelegd in een of andere standaard, de maker bepaalt wat erin komt
Inleiding Natuurkundige Informatica
53
1997
Enige Problem Solving Environments
Mathematica
User Graphics Point/Click/Evaluate
(mathematica)
Notebook Interface
(math)
Mathematica Kernel
Tekst
User
Graphics
Host Systems
Figuur 49
Figuur 50
Schematische representatie van de structuur van Mathematica. Merk op dat er op twee verschillende niveau’s gewerkt kan worden met het pakket.
Notebook interface van Mathematica
en wat niet, eenzelfde overweging geldt voor de andere pakketten. Het staat dus in principe aan de ontwerper vrij om te doen wat hij wil.
Inleiding Natuurkundige Informatica
54
1997
Enige Problem Solving Environments
Mathematica
Binnen mathematica wordt de conventie gevolgd dat functies beginnen met een hoofdletter en dat vierkante haken de argumentlijst omgeven. Dus Sin[x] duidt op de (formele) functie sinus. Koppeling met de buitenwereld is mogelijk door middel van een interface die ’MathLink’ heet. Hierdoor kan (bestaande) C en Fortran code gekoppeld worden aan Mathematica. Dat soort koppelingen wordt gerealiseerd als men ervoor wil zorgen dat een hoeveelheid code snel uitgevoerd wordt. Tot slot zij opgemerkt dat Mathematica ook in staat is om resultaten in C en Fortran Code om te zetten (of om een antwoord in een tekstverwerking neer te zetten. Mathematica heeft uitgebreide grafische mogelijkheden die de gebruiker eenvoudig in staat stellen om (complexe) grafieken te maken. Wij zullen hiervan nog voorbeelden laten zien.
3.5.3
Een eerste Tour van Mathematica
Als de interpreter van Mathematica gestart is (math of mathematica) kunnen er opdrachten aan de interpreter gegeven worden. In tegenstelling tot derde generatie talen als C en Fortran hoeven hier geen variabelen gedeclareerd te worden. Het type van een variabele wordt duidelijk uit de assignatie. Dus opdrachten als In[1] := 5 + 3 Out[1] = 8
en In[2] := a = 5 + 3 Out[2] = 8
zullen het te verwachten resultaat opleveren. Het verschil tussen de eerste en de twee opdracht is dat in het tweede geval het antwoord van de operatie in ’a’ bewaard wordt. Zolang de sessie van mathematica niet afgebroken wordt zal in principe de variabele zijn waarde behouden. Met variabelen werken gaat op exact dezelfde manier In[3]:= (c x + d) ^ 2 Out[3]= (d + c x) ^ 2 In[5]:= Expand[%3] Out[5]= d^2 + 2 c d x + c^2 x^2
Functies worden in Mathematica met een hoofdletter geschreven. Een onwaarschijnlijke hoeveelheid functies is voorgedefinieerd. Hieronder een paar voorbeelden: Formele differentiatie van eenvoudige expressies In[6]:= D[Sin[x], x] Out[6]= Cos[x]
gaat even makkelijk als van meer complexe functies In[10] := D[BesselJ[n, x], x] Out[10]= (BesselJ[-1 + n, x] - BesselJ[1 + n, x])/2
Wat numerieke types betreft sluit Mathematica nauw aan bij het wiskundig denken.
3.5.4
Uitwerking van de case studies
Het programma om de bewegingsvergelijking van de gedempte harmonische oscillator in mathematica op te lossen, demp1.m, ziet er als volgt uit: Clear["Global`*"]; eq = x''[t] + 2 gam x'[t] + w0 ^ 2 x[t] == 0; sol = DSolve[ { eq, x[0]==x0, x'[0]==v0 } , x[t], t ] [[1]]; subst = { (gam^2 - w0^2)^(1/2) -> (wd) , 1/(gam^2 - w0^2)^(1/2) -> 1/(wd) }; sol = sol //Simplify; sol = sol //.subst;
Inleiding Natuurkundige Informatica
55
1997
Enige Problem Solving Environments
Mathematica
De eerste regel wist alle definities die op dat moment bestaan. Mathematica is immers een interpreter, alle resultaten worden bewaard. Bij een nieuw programma is het verstandig alles te wissen om te voorkomen dat oude definities en instellingen plotseling een rol gaan spelen. In de tweede regel wordt de differentiaal vergelijking opgegeven. In de derde regel wordt hij opgelost met als randvoorwaarden de beginpositie x(0) = x0 en de beginsnelheid x˙ (0)= v0. De variabele sol bevat nu de oplossing. In de laatste drie regels wordt een substitutie en de algemene functie Simplify uitgevoerd om het antwoord er wat eenvoudiger uit te laten zien. Men kan zich terecht afvragen hoe van te voren een substitutie aan te geven is zonder te weten hoe de berekening en het antwoord er uit gaan zien. Het antwoord daarop is eenvoudig. Net zoals bij het uitvoeren van de berekening met de hand komt er een moment waarop een handige substitutie zich aandient. Bij het uitvoeren van het bovenstaande programma zonder de substitutie verschijnt in de oplossing zes maal de term ( γ 2 – ω 02 ) 1 / 2 . Het substitueren van deze term suggereert zichzelf. Het programma geeft als output In[1]:= << demp1.m t ( – gam+wd ) v0 + ( gam- wd )x0 – --------------------------------------------- + E ( v0 + ( gam+ wd )x0 ) ( gam+ wd ) E Out[1] = x [ t ] → ----------------------------------------------------------------------------------------------------------------------------------------------- 2wd î
Vergelijk deze oplossing met vgl. 3.23 van Fowles & Cassiday (let op, ωd heet hier aanvankelijk q en wordt pas twee bladzijden verder ωd genoemd!). Door het stellen van de randcondities zijn de constanten A1 en A2 hier uitgedrukt in x0, v0, γ en ωd.
3.5.5
Visualiseren van de oplossing
In Mathematica kan deze oplossing eenvoudig gevisualiseerd worden. We kiezen als begintoestand een uitwijking van 1 en geen snelheid, ofwel x0 = 1 en v0 = 0. We willen nu kijken hoe de oscillator zich gedraagt bij verschillende verhoudingen van demping en veerconstante. In eerste instantie zou je er dus waarschijnlijk voor kiezen ω0 (evenredig met de veerconstante) vast te kiezen en γ (evenredig met de demping) te laten varieren. Het blijkt echter, wederom door gewoonweg experimenteren, dat het meest inzichtelijke plaatje ontstaat door γ vast te kiezen en ω0 te laten varieren. We stellen γ = 1 en laten ω0 varieren van 0 tot 5. De code, demp2.m, ziet er als volgt uit values = { x0 -> 1, v0 -> 0, gam -> 1, wd -> Sqrt[ gam^2 - w0^2 ] }; p1 = Plot3D[ x[t] //.sol //.values , {w0, 0, 5} , {t, 0, 7} , PlotPoints -> 25 , ViewPoint -> {2, 1, 1} , AxesLabel -> {“w0”, “t”, “x[t]”} ];
Resultaat is Figuur 51 In dit plaatje zijn alle toestanden van de oscillator terug te vinden. Omdat we over het algemeen minder gewend zijn met 3D plaatjes te werken dan met 2D plaatjes (ze zijn immers een stuk moeilijker zelf te tekenen) is dit misschien niet direct in te zien. Het ‘zien’ van 3D plaatjes is ook iets wat geleerd moet worden! Probeer het plaatje als volgt te beschouwen: Kies een waarde voor ω0. Deze waarde bepaalt de veerconstante (de demping is vast gekozen). We bevinden ons hier op de lijn x[t] = 1, de beginuitwijking van de oscillator! De lijn die vanaf dit punt wegloopt in het x[t], t-vlak geeft het bekende plaatje van de uitwijking van de oscillator tegen de tijd voor deze waarde van ω0. Voor ω0 = 0 (het ‘achtervlak) is de demping zo groot ten op-
Inleiding Natuurkundige Informatica
56
1997
Enige Problem Solving Environments
Mathematica
w0
0
1 2 3 4 5 1 0.5 x@tD
0
-0.50 2 4 t Figuur 51
6
Output van het Plot3D commando.
zichte van de veerconstante dat de oscillator helemaal niet beweegt. Voor ω0 = 0.5 beweegt de oscillator wel naar de evenwichtstand toe maar bereikt die niet. Hij blijft a.h.w. ergens halverwege steken. In beide gevallen is sprake van overdemping. Voor ω0 = 1 zien we de oscillator precies de evenwichtstand bereiken maar er niet doorheen gaan. Dit is kritische demping. Voor ω0 > 1 begint de oscillator langzaam een paar keer te oscilleren alvorens uit te dempen. Voor ω0 = 5 (het ‘voorvlak’) is deze oscillatie het grootst. De oscillator is ondergedempt. Zelfs zonder te weten wat voor toestanden de oscillator aan kan nemen kun je ze in dit plaatje allemaal terugvinden! Het getoonde plaatje heeft natuurlijk wel precies de goede instellingen qua bereik van de assen en viewpoint om zo prachtig alle toestanden te kunnen onderscheiden. Terecht kun je je afvragen hoe je tot zulke instellingen komt als je niet eens precies weet naar wat voor plaatje je zoekt. Ook dit plaatje is gemaakt door gewoonweg wat met de instellingen te experimenteren. Het is uiteraard ook mogelijk 2D plaatjes te maken. Het volgende programma, demp3.m, maakt een plaatje met dezelfde instellingen als hierboven en ω0 = 5 values = { x0 -> 1, v0 -> 0, gam -> 1, wd -> Sqrt[ gam^2 - w0^2 ] }; p2 = Plot[ x[t] //.sol //.values //.{ w0 -> 5 } , {t, 0, 10} , PlotRange -> {-1, 1} , AxesLabel -> {“t”, “x[t]”} ];
3.5.6
Gevaren van het gebruik van Mathematica
We hebben een aantal voordelen van het gebruik van Mathematica gezien. Met weinig inspanning kunnen ingewikkelde berekeningen gedaan worden, symbolisch zowel als numeriek. Dit geldt ook voor berekeningen die, in tegenstelling tot het behandelde voorbeeld, te complex zijn om met de hand uit te werken. Bovendien kunnen resulaten eenvoudig op diverse manieren grafisch weergegeven worden. Naast de getoonde 2D en 3D grafieken zijn diverse technieken beschikbaar, zoals het gebruik van kleur en contourlijnen en zelfs animaties. Het is echter belangrijk de beperkingen van Mathematica goed voor ogen te houden. Zoals ieder computer programma doet het, indien mogelijk, wat je er van vraagt, niet meer en niet minder. Het is daarom heel
Inleiding Natuurkundige Informatica
57
1997
Enige Problem Solving Environments
ScilImage
x[t] 1 0.75 0.5 0.25 2
4
6
8
10
t
-0.25 -0.5 -0.75 -1
belangrijk te weten wat je nou eigenlijk precies laat uitvoeren. Een voorbeeld van een valkuil waar je met het gebruik van Mathematica in terecht kunt komen is het volgende. Het voorbeeld van de gedempte harmonische oscillator komt uit het boek ‘Mathematica for Physics’. In dit boek wordt voor het verkrijgen van de getoonde plaatjes niet ω0 of γ maar ωd gevarieerd. Zoals blijkt uit formule 2c kan ωd, als ωd reëel is, maximaal de grootte γ hebben, namelijk als ω0 gelijk is aan 0. In het boek wordt echter zonder blikken of blozen een plaatje gemaakt waarbij ωd groter is dan γ, waardoor het volgende plaatje ontstaat. Zonder beginsnelheid beweegt de oscillator van de evenwichtstand af in plaats van er naar toe! Een wiskundig correcte maar fysisch onmogelijke oplossing die Mathematica zonder problemen uitvoert. x[t] 2 1.5 1 0.5 2
4
6
8
10
t
-0.5 -1
Het aperte voordeel van Mathematica is de geïntegreerdheid en het hoge niveau van haar basis instructies. De prijs die men er voor moet betalen komt in de vorm van traagheid: een forse computer is nodig om Mathematica enigszins comfortabel te kunnen draaien. Een Mathematica Compiler zou helpen in het verhogen van de prestatie maar het maken daarvan is geen sinecure. Een daaraan gerelateerd probleem is dat Mathematica niet kan aangeven ’hoelang het nog duurt’ voor de oplossing gevonden is. Deze kan ’nooit’, het ’volgende uur’ of ’de volgende microseconde’ gevonden worden.
3.6
ScilImage4
3.6.1
Case Study
Mede door de consumenten electronica heeft de beeldverwerking een grote vlucht genomen in de afgelopen 4.Scil/Image
is een ontwikkeling van de Universiteit van Amsterdam.
Inleiding Natuurkundige Informatica
58
1997
Enige Problem Solving Environments
ScilImage
jaren. De zogenaamde CCD-chip (Charge Coupled Device) die het hart vormt van vrijwel elke video camera is hier van eminent belang voor. Hierdoor is het mogelijk geworden om op afstand te kunnen ’waarnemen’ en ’meten’. De camera’s aan boord van ruimtevaartuigen zijn hiervan een goed voorbeeld. Wil men quantitatieve gegevens kunnen produceren dan dient er gewerkt te worden met één of andere vorm van meetlat. Een veel gebruikte techniek die wij hier vereenvoudigd zullen presenteren is die van het werken met zogenaamd gestructureerd licht. Beschouw als voorbeeld een schaakbordpatroon dat op een object geprojecteerd wordt en vervolgens geregistreerd wordt door een camera. Een voorbeeld is gegeven in Figuur 52. Een prak-
Figuur 52
Voorbeeld van beeldverwerking. Reflectie van een ’meetpatroon’ aan een stalen kogel van 8 mm (model voor het menselijk oog).
tische toepassing van dit principe is het meten van het oppervlak van het menselijk oog. Het zal duidelijk zijn dat hier niet direct op ’gemeten’ kan worden. Een voorbeeld van een partroon dat hiervoor gebruikt wordt is ook gegeven in Figuur 52. Wij zullen in deze case study niet het hele proces van de gegevensverwerking doorlopen maar alleen het herkennen van de hoekpunten behandelen.
3.6.2
Achtergrond ScilImage
Scil/Image is in feite een interpreter. Als basistaal heeft men de programmeer taal ’C’ gekozen. De relatie van de diverse componenten die verder van belang zijn wordt gegeven in Figuur 53. In principe kan er met de interpreter ’direct’ C gepraat worden. Dus een opdracht als printf("hello world\n");
zal het welbekende resultaatopleveren. De kennis over de beeldverwerking wordt aan de interpreter toegevoegd door een aantal bibliotheken. Voor de gebruiker zijn dit ’gewone’ functies. Daarnaast zijn er een aantal datastructuren gedefinieerd om beelden in op te kunnen slaan. Deze verzameling van C interpreter en bibliotheken noemt men de Image kernel. Om de communicatie hiermee te vereenvoudigen is er ook een grafische userinterface toegevoegd aan het systeem. Deze genereert op een ’click’ van de gebruiker de textuele commando’s die hiermee corresponderen en geeft die weer door aan de image kernel. Als een gebruiker nieuwe functies wil ontwikkelen kan hij dat doen door ze eerst in de interpreter te testen. Dat heeft als nadeel dat ze dan regel voor regel geïnterpreteerd worden en dus erg traag lopen. Als de code eenmaal correct is kan hij ook gecompileerd worden en als het ware aan de ’kernel’ geplakt worden, wat aanzienlijk in snelheid scheelt. Mathematica beschikt over een zelfde schema. Voor onze case study hebben wij datastructuren en een paar operaties nodig. In principe is een beeld goed vergelijkbaar met een tweedimensionale matrix. Afhankelijke van de toepassing zijn er kleur, grijs en zwart/ wit beelden. Naar de laatste refereert men ook wel als binaire beelden. Een element van een beeld noemt men een pixel, met Pij duiden wij het element op positie (i, j) aan. De waarde van een pixel is, wij zullen hier nog op terugkomen, meestal beperkt tot het integer interval 0..255. Een kleurenbeeld kan gezien worden als een verzameling van drie grijswaarden beelden: één voor rood, één voor groen en één voor blauw. De
Inleiding Natuurkundige Informatica
59
1997
Enige Problem Solving Environments
ScilImage
Menu interface
Line interface
Image Kernel
User Bibliotheken
C Interpreter Figuur 53
Figuur 54
Image Bibliotheken
Principe schema van de ScilImage omgeving voor het verwerken van beelden.
Grafische user interface van ScilImage
transformatie tussen een grijswaardenbeeld en een kleurenbeeld kan dan geschreven worden als Grijs = .6 * Rood + .3 * Groen + .1 * Blauw In deze notatie duiden Grijs, Rood, Groen en Blauw nu beelden (!) aan en moet de bewerking uitgevoerd worden voor alle pixels binnen het beeld. De coefficiënten .6, .3 en .1 zijn gebaseerd op een empirische relatie. Omzetten van een grijswaarde beeld naar een zwart/wit5 beeld kan nu eenvoudig door het uitvoeren van de bewerking ZW = (value > drempel) ? 1 : 0 die weer voor alle elementen geëvalueerd moet worden. Binaire beelden kunnen handig zijn. Als men in een binair beeld moet vaststellen hoe groot een (samenhangend) object is, is het voldoende om alle pixels ongelijk aan nul te tellen. Voor het herkennen van de positie van de kruispunten waarnaar wij opzoek zijn is gebruik van een zogenaamd ’filter’ nodig. Algemeen wordt een filteroperatie gegeven door de zogenaamde convolutie operatie: n
n
n
n
P kl' = w ij P(k – i, l – j) ⁄ w ij i = –n j = –n i = –n j = –n
∑ ∑
∑ ∑
(25)
Naar w refereert men als het filter. Merk op dat een filter altijd een oneven aantal elementen heeft. Een eenvoudig voorbeeld is: 5.Wat
wij in ons spraakgebruik zwart/wit noemen zijn meestal grijswaarden.
Inleiding Natuurkundige Informatica
60
1997
Enige Problem Solving Environments
ScilImage
11 1 w = 111 11 1
(26)
Effectief zal dit filter aan het centrale pixel het gemiddelde van de 9 pixels toekennen. Voor een beeld betekent dit een ’smoothing’ anders gezegd de scherpe kantje gaan eraf. Dit wordt geïllustreerd in Figuur 55.
Figuur 55
Effect van uniform fliter, zoals aangegeven in vgl. (26) op een beeld. Boven origineel. Beneden van links naar rechts een 7x7, 15x15, 31x31 filter.
Hierin worden drie verschillende groottes van filters toegepast, 7x7, 15x15 en 31x31. Men kan ook de ’waarde’ van het nieuwe pixel zien als de response van het beeld op dat punt op het filter. Merk op dat als het filter even groot is als het beeld zelf er maar één punt overblijft waar de respons uitgerekend kan worden, nl het centrale punt.
3.6.3
Uitwerking case study.
Een deel van een sessie binnen ScilImage wordt in de onderstaande figuur gegeven. De eerste regel leest het readfile /usr/local/scilimage/images/flamingo.tif A 300 300 split_color_im A B C D set_im_type A GREY_2D eval "A= .6 * B + .3 * C + .1 *d" -1
kleuren plaatje in en bindt er de naam A aan. De display van het plaatje krijgt de grootte van 300 bij 300 pixels. In de tweede regel wordt het kleuren plaatje A gesplitst in de drie basis kleuren en de images worden aangeduid met B, C en D. Vervolgens wordt gezegd dat A nu een grijswaarde plaatje gaat worden. En tenslotte wordt A op de eerder beschreven manier ingevuld als grijs waarden. Voor het vervolg is een wat ingewikkelder operatie nodig. Wij zullen hier alleen het resultaat vertellen. Om de overgang te kunnen detecteren moet een matched filter geconstrueerd worden waarin de voorkennis over het te herkennen patroon zit. Wij poneren hier dat de oplossing gegeven wordt door
Inleiding Natuurkundige Informatica
61
1997
Enige Problem Solving Environments
HTML/Netscape
–1 –1 –1 0 1 1 1
–1 –1 –1 0 1 1 1
–1 –1 –1 0 1 1 1
0 0 0 0 0 0 0
1 1 1 0 –1 –1 –1
1 1 1 0 –1 –1 –1
1 1 1 0 –1 –1 –1
(27)
Intuïtief is het in te zien dat het hierboven gegeven filter gebruikt kan worden. Er zijn drie delen te onderscheiden: blokken met de waarde 1, de waarden -1 en twee lijnen met de waarde 0. Het kruispunt wat wij zoeken heeft precies de karakteristiek dat over de diagonaal er dezelfde informatie staat (wit tegenover wit en zwart tegenover zwart). Als we dus dit filter gaan gebruiken en kijken naar een maximale respons moeten de kruisingen gevonden kunnen worden. In Figuur 56 wordt het resultaat weergegeven van de operatie. De plaatsen waar ten onrechte een kruising gevonden wordt noemt men false positives, de plaatsen waar geen respons gevonden is false negatives. Als resultaat van deze operatie kent men nu op (integer) pixel coordinaten de plaats van de kruisingen. (Het werk begint dan overigens pas.) Men kan ook laten zien dat het ook mogelijk is om nauwkeuriger dan het pixel formaat de positie te bepalen. Dat ligt echter buiten het bestek van deze inleiding. Figuur 56
3.7
HTML/Netscape
3.7.1
Case study
Resultaat van de detectie van de kruisingen in Figuur 52 met behulp van het in vergelijking (27) aangegeven matched filter.
Bezie het werk uit de vorige case study over beeldverwerking. Voor het geval dat er op twee (of meer) verschillende plaatsen gewerkt wordt aan dit project is de overdracht van met name de beelden een probleem. Immers bij een fax gaat, vanwege de compressie (!), vrijwel alle detail verloren (men wil de telefoonrekening niet te hoog maken). Een soortgelijke situatie heeft men als het verhaal opgenomen moet worden op een CD6.
3.7.2
Achtergrondinformatie
Het Internet werd aan het einde van de 60er jaren opgezet als een experiment om te komen tot een robuust computer netwerk. Het doel was om een netwerk te ontwerpen dat het verlies van een (groot) aantal systemen zou kunnen lijden en toch de resterende computers nog met elkaar zou verbinden. De opdrachtgever was het amerikaanse ministerie van defensie (DoD) dat geïnteresseerd geraakt was in computernetwerken 6.Het complete verhaal is te vinden onder www.nat.vu.nl/~vosf en is op CD uitgekomen als onderdeel van de proceedings
van een conferentie.
Inleiding Natuurkundige Informatica
62
1997
Enige Problem Solving Environments
HTML/Netscape
die een nucleaire aanval konden overleven (bedenk dat de 60er jaren vallen in de koude oorlog). Het eerste resultaat was het zogenaamde ARPAnet. De openstelling van het netwerk voor het publiek kwam pas in de 90er jaren maar de belangstelling was in het begin niet overweldigend omdat de gebruikersvriendelijkheid van ’het net’ bepaald gering was. Het tweede ingrediënt dat nodig was om te komen tot de ontsluiting van het netwerk werd geleverd door een aantal fysici van het CERN. Geconfronteerd met de steeds groter wordende samenwerkingen rond de versneller van CERN ontstond er de behoefte om te komen tot een eenvoudige manier van uitwisseling van gegevens tussen experimentatoren die over de hele wereld verspreid zaten. Het ging hier niet alleen om textuele informatie maar (vooral) ook om grafieken, tekeningen, en andere multi media vormen. De oplossing die door deze mensen bedacht werd - een taal - kreeg de naam Hypertext Markup Language. Het uitwisselen van resultaten - in welke vorm dan ook - ging nu door het uitwisselen van Hypertext documenten. Het laatste ingrediënt werd geleverd door een aantal studenten van het National Center for Supercomputing Applications (NCSA) van de universiteit van Illinois, Urbana-Champaign, die de eerste web browser, met als voornaamste taak het interpreteren van HTML documenten, in elkaar zetten. Over de spectaculaire groei van het Internet sedertdien hoeft weinig meer verteld te worden. Tot op heden zijn 1.2 miljoen (!) domeinnamen uitgegeven7. User HTML Files Web Browser (HTML Interpreter)
HTTP
~japie/www/welcome.html
daemon
HTTP
andere HTML files
daemon
Internet domain A domain B
Figuur 57
Schematische representatie van de Netscape/HTML omgeving: Een gebruiker in domein A verkent het net met behulp van een Browser. De eigenlijke files komen uit domein B en worden op het systeem van de gebruiker geïnterpreteerd.
De ingrediënten worden samengevat in Figuur 57. Het principe is dat een gebruiker de HTML files die hij/ zij nodig heeft ophaalt bij de eigenaar van de gezochte informatie en daarna de informatie op zijn locale systeem interpreteert. Een gebruiker in domein A (voor de faculteit natuurkunde is dat nat.vu.nl) communiceert met een netwerk browser. Deze heeft als primaire functies dat hij de taal HTML kan interpreteren. De plaats waar de informatie opgehaald wordt, wordt symbolisch aangegeven door een naam. Binnen het Internet wordt elke gebruiker door een zogenaamd IP (Internet Protocol) adres geïdentificeerd. Voor een logische groep van gebruikers (een domein) fungeert één machine als ’vertaler’. Deze machine beeldt symbolische namen af op IP adressen. Als de gebruiker in het voorbeeld de pagina opvraagt die correspondeert met b.v. www.sara.nl wordt het adres doorgegeven door de browser aan een programma dat HTTP heet (Hypertext Transfer Protocol) en dat zorgt voor de overdracht van de benodigde HTML files. Tezamen met de IP laag wordt de bron van de informatie opgezocht. Hiervoor zijn verschillende conventies in gebruik. Binnen de faculteit wordt de conventie gehanteerd dat de startplaats voor HTML files gevormd wordt door de directory www in de 7.Voor
verdere statistieken zie http://www.nw.com
Inleiding Natuurkundige Informatica
63
1997
Enige Problem Solving Environments
Java
home directory van de gebruiker waar het over gaat, of op de directory /var/spool/www op de domain name server. Voor de faculteit is dat de machine ’hardy.nat.vu.nl’. Binnen die directory wordt gezocht naar een file Welcome.html of welcome.html. Wordt dat niet gevonden dan krijgt de zoeker de beroemde code 404: not found. Wordt de informatie wel gevonden, dan wordt zij door de ’daemon’ overgedragen naar de opvrager en door diens browser geïnterpreteerd. Omdat een gebruiker misschien herhaald dezelfde pagina wil opvragen, en dat nodeloos lang zou duren als het steeds volledig uitgevoerd zou worden, houden de meeste netwerk browsers een zogenaamde cache dat wil zeggen houden zij stiekem een kopie van de opgevraagde informatie bij op de disk van de gebruiker. Naast formaten voor het representeren van multi media informatie is het belangrijkste hulp middel van HTML de ’doorverwijzing’ naar andere informatie oftewel de zogenaamde hyperlink. De standaard HTML is nog erg jong en onderhevig aan veel veranderingen. De huidige versie is HTML 3.2, de derde revisie in 5 jaar. Ter vergelijking de vorige vernieuwing van Fortran vond 12 jaar geleden plaats. HTML is geen tekstverwerkingstaal (zoals bijvoorbeeld TeX, of een tekstverwerker als FrameMaker). Het aantal fonts dat beschikbaar is is gelimiteerd en, hinderlijk voor veel wetenschappers, er zijn alleen maar voorziening voor sub- en superscripts. Een beetje een complexe formule moet dus al gauw als ’plaatje’ in de text opgenomen worden. Daar tegenover staan monumentale voordelen. Niet alleen kunnen allerlei soorten multi-media informatie probleemloos gepresenteerd worden maar ook hoeft de ontwerper zich (amper) zorgen te maken over de overdraagbaarheid van zijn informatie. Een kleine restrictie geldt hier. Helaas hebben de grote browsers elk zo hun eigen opvatting over de interpretatie van de standaard HTML en hebben zij eigen features toegevoegd aan de standaard! Meer en meer worden tools gemaakt om automatisch HTML pagina’s te genereren. Het meest in het ooglopende voorbeeld hiervan zijn de grote databases die dienen als ’search engines’ om snel informatie te vinden. In deze databases wordt wereldwijd een lijst van trefwoorden bijgehouden. Alle hits die gevonden worden, worden volgens een criterium van een relevantie index voorzien. Deze informatie wordt in real-time, dat wil zeggen terwijl de gebruiker wacht, omgezet naar HTML formaat, wat vervolgens opgestuurd wordt naar de aanvrager. Tot slot zij opgemerkt dat de browsers natuurlijk ook kunnen werken zonder netwerk. Het gaat hier slechts om HTML files. Vandaar dat in toenemende mate informatie ook op CD rom in HTML formaat uitkomt.
3.7.3
Uitwerking van de case study.
Op www.nat.vu.nl/~vosf kan men een realisatie zien van het complete verhaal over het in de vorige paragraaf aangegeven onderzoek en hoe een en ander omgezet kan worden in HTML. Het voert te ver om dit in detail te beschrijven. In Figuur 58 wordt een enkele pagina getoond. HTML commando’s zijn te herkennen aan het feit dat zij tussen ’<’ en ’>’ staan. De tekst is direct herkenbaar. Let op hoe makkelijk een plaatje in deze omgeving neergezet kan worden en ook hier een zogenaamde ’link’ (href) aangegeven wordt. Op de meeste browsers is het mogelijk om de source van een pagina te zien, hij wordt immers overgestuurd, met het commando ’view source’. Dit kan helpen om wat sneller inzicht te krijgen in hoe pagina’s opgebouwd zijn.
Java8
3.8
Het is passend om dit overzicht over omgevingen te beëindigen met een inleiding op JAVA want in zekere zin vormt JAVA een logisch (voorlopig) eindpunt van de ontwikkelingen die wij tot nu toe besproken hebben en moet Java gezien worden als logisch vervolg van HTML. De ’tekortkoming’ van HTML is dat het een taal is die ’slechts’ geschikt is voor de overdracht van multi media informatie. Het goede is dat zij platform onafhankelijk is en vrijwel onmerkbaar voor de gebruiker de mogelijkheden van het net kan uitbuiten. Het door SUN ontwikkelde Java is opgezet als een volledige programmeertaal die platform onafhankelijk, en 8.De
officiële web site is http://www.javasoft.com
Inleiding Natuurkundige Informatica
64
1997
Enige Problem Solving Environments
Java
Figure: Crossings detected in the image reflected by a steel ball, using the module
Crossing.
See also
the input image the reflection of an eye the crossings found for an eye
Figuur 58
Voorbeeld van een HTML pagina (rechts) en de presentatie in Netscape
over het net, kan worden gebruikt. Het realiseren van platform onafhankelijkheid klinkt niet onbekend. In feite is dat hetzelfde als de automatic programming systems die wij in sektie 3.1genoemd hebben (er is weinig nieuws onder de zon in de informatica). Java is een object georiënteerde taal. Dat betekent dat er een aantal datatypes en daarmee geassocieerde functies zijn, voorgedefinieerd in zogenaamde objecten. Als zodanig is Java goed te vergelijken met C++, wat weer een superset van C is. Een groot verschil tussen C++ en Java is dat in Java het begrip pointer niet meer bestaat. De kracht van Java zit met name in de functionaliteit die de object classes de gebruiker bieden: van het manipuleren van bits tot en met het openen van windows of het genereren van een animatie is allemaal in de classes van Java inbegrepen. In zoverre is een Java programma, dat een suffix .java heeft, conceptueel ononderscheidbaar van een b.v C programma. Om echter op alle platformen te kunnen draaien is in Java de zogenaamde Virtual Machine gedefinieerd. Deze machine is echt omdat hij beschikt over een complete instructieset. Hij heet virtueel omdat hij alleen maar in software gedefinieerd is. Men dient overigens op te merken dat dit concept niet nieuw is. Bij IBM is er ook tijden een software omgeving geweest die aangeduid werd met de naam Virtual Machine (VM). Ook deze deed in software een machine na. Het verschil met het Java concept was dat bij IBM er een compleet mainframe nodig was om de virtuele machine te kunnen draaien. Met behulp van een Java compiler wordt code voor de virtuele machine gegenereerd. Naar deze code refereert men als de bytecode executable class. Het welbekende voorbeeld bij C is hello world. In Java zou dit er uit zien als in Figuur 60. Zelfs voor de C programmeur zitten hier nog een aantal bekende stukken in. De class
HelloWorld { public static void main(String args[]) { System.out.println("Hello, world!"); }
Figuur 60
Implementatie van Hello World in Java.
code die door de java compiler (javac) wordt gegenereerd kan vervolgens gedraaid worden met het commando:
Inleiding Natuurkundige Informatica
65
1997
Enige Problem Solving Environments
Java
Java Runtime System Java Compiler
Java Virtual Machine Bytecode Executable Classes
Java Source Code Files
Native Operating System Figuur 59
Representatie van de rol van de Virtuele Machine (VM) in het concept van JAVA.
java HelloWorld Het verschil met het bekende a.out is duidelijk. De virtuele machine wordt opgestart met het commando java en aan deze machine wordt de byte code meegegeven van HelloWorld. Het resultaat van dit alles zal er niet onbekend uitzien: Hello, world!. Twee problemen zijn er, in elk geval, met Java. Het eerste is de snelheid en het tweede is de veiligheid. Aan het begin van dit hoofdstuk hebben wij erop gewezen dat interpreters altijd trager zijn dan compilers om dat de laatste in staat zijn om de code te optimaliseren voor één specifiek platform. Dit heeft geleid tot de introductie van de zogenaamde JIT (Just in Time Compiler). In Figuur 61 wordt één en ander toegelicht. Op het Java Runtime System Java Compiler
Java Virtual Machine Bytecode Executable Classes
Java Source Code Files
Native Operating System
Just In-Time Compiler Native Code
Figuur 61
Plaats van de de VM en de JIT compiler in de Java omgeving.
moment dat de code gedraaid moet gaan worden, wordt er snel een compiler aangeroepen om de code naar ’echte’ code om te zetten, die vervolgens door de VM gedraaid wordt. Dit hele proces is transparant voor de gebruiker. De tijd die nodig is om de compiler aan te roepen en het programma om te zetten moet wel gecompenseerd worden door de snelheidswinst van de resulterende code. Op dit ogenblik is het laatste woord nog niet gezegd over dit efficiency vraagstuk. De veiligheid is een tweede aspect. Om dat op waarde te kunnen schatten moet men bedenken dat Java juist met het oog op netwerking ontworpen is. In doorsnee zal dus een programma niet draaien op de hardware van de programmeur maar op een (heel) andere machine. In tegenstelling tot het klassieke ’hacken’ hoeft men dus in dit geval geen moeite te doen om een vreemde machine binnen te komen: het is per definitie mogelijk. In dat soort omstandigheden kan een set statements als unsigned char*p; while(p) *p++ = ’\0’;
Inleiding Natuurkundige Informatica
66
1997
Enige Problem Solving Environments
Samenvatting
al leiden tot een redelijke chaos. De onveiligheid zit met name in de vele mogelijkheden die de pointer aan de geoefende gebruiker biedt. Vandaar dat in Java de pointer in het geheel niet opgenomen is. Desalniettemin zijn er toch al een aantal ernstige en minder ernstige onvolkomenheden in de beveiliging van de Java virtuele machine ontdekt.
Een speciale klasse van programma’s verdient tot slot aandacht: de zogenaamde applets. Applets zijn Java programma’s die louter en alleen ontwikkeld zijn om te kunnen werken binnen een Web browser. Het zou te ver voeren om diep op dit verschil in te gaan we kunnen het alleen proberen aannemelijk te maken: bij een Web browser zijn er een aantal zaken (noodzakelijkerwijs) aanwezig: een muis, een scherm binnen de browser, mogelijke multi media input/output devices. Over al deze zaken moet de programmeur de beschikking hebben. De manier om dat te doen binnen het object georiënteerde paradigma is door al deze parameters in klassen onder te brengen en de programmeur een ’hendel’ te verschaffen naar deze attributen.
3.9
Samenvatting
Bestaat er de programmeeromgeving die toepasbaar is voor alle problemen? Deze korte tour langs een (zeer beperkt) aantal omgevingen moet het antwoord aanvoelbaar maken: nee! Een trend die wij gezien hebben in de omgevingen die we bestudeerd hebben is dat meer en meer functionaliteit onderdeel gaat uitmaken van de omgeving zelf. Niet meer losse applicaties of bibliotheken voor zaken als e-mail, grafische representatie en het opslaan van data in een database maar alles vanuit taal/omgevingen. Voorts is er een toenemende onderkenning van het belang van een goede user interface. Deze kan verkortend werken op een leercurve en acceptatie van een omgeving fors verhogen. Nu moet opgemerkt worden dat deze zaken vroeger ook op hun waarde geschat werden maar er toen niet de mogelijkheden bestonden om het probleem op een algemene manier op te lossen. Dat dat nu veel meer kan is een gevolg van de ’driving force’ van de technologie: steeds meer prestatie van de chip en geheugen zijn voorhanden. Het nadeel van de goede userinterface is ook apert: zij ’vreet’ geheugen en processing power. Een derde trend die wij gezien hebben is het steeds toenemende belang van netwerking. Moderne applicaties dienen informatie uit te kunnen wisselen met allerlei plaatsen en transparant voor de gebruiker samen te kunnen werken. Wie gewapend met deze kennis de fysische gemeenschap in trekt en tracht in kaart te brengen welke omgevingen men allemaal gebruikt zal een wat vreemde mix aantreffen van allerlei hardware en software. Hierin speelt Fortran77 nog een dominante rol en begint C pas een runner up te worden. Dat lijkt conservatief voor een gebruikerspopulatie waar tenslotte revolutionaire zaken als HTML en Mosaic ontwikkeld zijn.
Inleiding Natuurkundige Informatica
67
1997
Hoofdstuk 4:Datatypes en Datastructuren
Datatypes en Datastructuren
4.1
Inleiding
Inleiding
In dit hoofdstuk zullen wij enige datatypes en datastructuren behandelen. Zoals veel software concepten is het idee van datatypes en datastructuur met name ontwikkeld in de 70er jaren voorbouwend op de ervaringen die opgedaan waren met Fortran en Cobol. Onder een datatype verstaan wij een klasse van data objecten tesamen met een verzameling van operaties om ze te construeren, te manipuleren en te verwijderen. Elke taal of omgeving heeft een verzameling van datatypes die in de taal ingebouwd zijn. Daarenboven is het in sommige omgevingen mogelijk om zelf nieuwe types te definiëren. In figuur 36 op pagina 42 hebben wij de relatie tussen datatypes en omgevingen aangegeven. Een datatype kan op twee verschillende niveau’s bestudeerd worden: specificatie en implementatie. De basis van de specificatie wordt gevormd door: -
de attributen van het type, de waarden die het object kan aannemen, de operaties die gedefinieerd zijn voor de manipulatie van de data.
Als voorbeeld kan men denken aan een array. Tot de attributen zou men het aantal dimensies, de subscript range van elke dimensie, en het type van de componenten rekenen; de waarden spreken voor zich, terwijl tot de operaties het indiceren van de elementen, de operaties op de elementen en de (on)mogelijkheden om attributen te veranderen worden gerekend. Implementatie van een datatype omvat: -
de specifieke representatie die gebruikt wordt in een computer om het desbetreffende datatype tijdens executie van een programma op te slaan, de wijze waarop de operaties die de types manipuleren geïmplementeerd zijn in termen van specifieke algoritmen.
-
Onder een datastructuur verstaan wij een verzameling (of aggregaat) van data objecten. Voor datastructuren zal degene die ze definieert zelf moeten zorg dragen voor de implementationele aspecten. Wat de specificatei betreft zjin met name de operaties van belang, de rest volgt bij logisch uit de definitie van de elementen. Een data object van een bepaald datatype noemen wij een instantatie. Instantaties hebben een naam die het mogelijk maakt om ze te onderscheiden. Naar een instantatie met een naam zullen wij kortheidshalve refereren als naar een variabele. Een variabele heeft een bepaalde ’levensduur’ ook wel aangeduid als scope. De start wordt gekenmerkt door de creatie van de (ruimte voor de) variabele en een eventuele initialisatie, terwijl het einde gemarkeerd wordt door de destructie van de variabele. Beschouw ter illustratie de volgende twee voorbeelden (zie Figuur 62). void {
sub()
int
j;
int j;
void {
main()
.......
......
}
} Figuur 62
Illustratie van het begrip ’scope’ van een variabele (in dit geval j). Links betreft het een locale variabele, rechts een globale.
In het linker geval wordt de variabele j ’gemaakt’ op het moment dat de functie sub aangeroepen wordt en weer opgeruimd op het moment dat de functie beëindigd wordt (wij hebben het daar ook over gehad in sektie 2.4). Het maken en verwijderen van de variabele gebeurt overigens automatisch voor de gebruiker, wij komen hier nog op terug. In het rechter geval is er sprake van een globale variabele: bij de start van het programma wordt deze gemaakt en bij het einde van het programma wordt hij verwijderd.
Inleiding Natuurkundige Informatica
69
1997
Datatypes en Datastructuren
Datatypes
Bij de operaties die op een datatype uitgevoerd kunnen worden verdient het aanbeveling om een onderscheid te maken tussen die operaties die het type behouden en die welke het type veranderen. De laatste categorie wordt meestal aangeduid als een cast. Het casten van een variabele is maar in een zeer beperkt aantal gevallen een goed gedefinieerde en omkeerbare operatie. Als voorbeeld kan met denken aan de omzetting van een niet-geheel getal naar een geheel getal. Voor alle gevallen waarin de fractie niet gelijk aan nul is, is deze operatie onomkeerbaar. Wij zullen in de loop van dit hoofdstuk nog terug komen op deze casts. Een rigide introductie van datatypes en -structuren zou te ver voeren. Hier willen wij ons concentreren op een aantal types en structuren die van belang zijn voor fysische omgevingen. Wij zullen opmerkingen in dit hoofstuk illustreren met voorbeelden uit de eerder aangegeven omgevingen als C(/Fortran) en Mathematica.
4.2
Datatypes
4.2.1
Getallen stelsels
Binnen de informatica bedient men zich niet al te vaak van het decimale stelsel, dit is meestal beperkt tot die gevallen waarin communicatie met de (menselijke) gebruiker noodzakelijk is. Om verschillende redenen zijn binair (2-), octaal (8-) en hexadecimaal (16-tallig) stelsels populairder. In ons (arabisch) stelsel geldt dat ’positie’ ’waarde’ is, derhalve kan men algemeen een getal noteren als (28)
d n – 1 d n – 2 .....d 0 en de numerieke waarde bepalen als n–1
waarde =
∑ dir
i
(29)
i=0
waarbij men r de radix noemt en er geldt dat max(d) + 1 = r
(30)
Binair Voor het binaire (2-tallige) systeem impliceert dit dat d = { 0, 1 } en r = 2. Een binaire eenheid noemt men een bit. Voor een serie van n-bits, zoals boven aangegeven geldt dat zij maximaal 2N verschillende mogelijkheden kunnen representeren. Het bit dat correspondeert met de hoogste tweemacht ( d n – 1 ) noemt men het most-significant bit, omgekeerd noemt men het laagste bit het least-significant bit. Octaal Bij het octale stelsel geldt dat d = {0, .., 7} en r = 8. Het digit bij het octale stelsel heeft geen aparte naam en kan gedacht worden als een triplet van bits. Teneinde octale getallen te kunnen onderscheiden is in C gedefinieerd dat de reeks met een 0 begint. Dus zou in C gelden dat 017 = 15 10 17 = 17 10 Net als bij het binaire systeem kan men het aantal mogelijkheden eenvoudig bepalen, dit is 8n. Hexadecimaal Tot slot wordt er ook nog gerekend in en gebruik gemaakt van het hexadecimale systeem. Hiervoor zijn de numerieke tekens onvoldoende vandaar dat hiervoor geldt d={0, 1,...,9,A,...,F} en r = 16. In vele toepassingen (onder andere C) wordt de conventie gehanteerd dat een hexadecimaal getal begint met 0x, dus
Inleiding Natuurkundige Informatica
70
1997
Datatypes en Datastructuren
Datatypes
0x17 = 23 10 0xF7 = 247 Een kwartet van bits is nodig en voldoende om één hexadecimale eenheid te coderen. Het zal duidelijk zijn dat ons decimale stelsel niet direct in een van deze representaties uit te drukken is, althans niet maximaal efficiënt. Wat implementatie betreft in de programmeertalen zijn er nogal wat verschillen. Binair, octaal en hexadecimaal wordt in C allemaal omgezet naar geheeltallig (integer) en daarna worden er operaties op uitgevoerd. Voor het manipuleren van bits zijn er in C speciale operatoren. Deze zijn voor een deel al behandeld in sektie 2.4. Het gebruik van binaire en geassocieerde representatie hangt voor een groot deel nog samen met de ’oude’ tijd toen geheugen duur en beperkt was. Voor assignatie doeleinden wordt in C de shift operator vaak gebruikt. Er zijn twee soorten shift, naar links en naar rechts, aangegeven met << en >>. Zo betekent 1<<3 dat het bit 1 3 posities naar links geschoven wordt en dus met het vierde bit, nummer 3, correspondeert. Een #define #define #define #define #define
ERROR FRAMING_ERROR PARITY_ERROR CARRIER_LOST CHANNEL_DOWN char
(1 << 0) (1 << 1) (1 << 2) (1 << 3) (1 << 4)
/* trouble */ /* framing error occurred */ /* invalid parity received */ /* not connected anymore */ /* loss of power on communication */
flags = 0;
flags | = (CARRIER_LOST | CHANNEL_DOWN); if(flags & PARITY_ERROR) fprintf(stderr, "out of sync on input); Figuur 63
Gebruik van bit operatoren in C.
voorbeeld hiervan wordt gegeven in Figuur 63. Een vijftal, mogelijke fouten, worden gedefinieerd als vijf opeenvolgende bits waarop individueel getest kan worden. Op die manier kunnen er even veel (onafhankelijke) grootheden gemanipuleerd worden als er bits in het datatype zitten. Mathematica heeft een hoop functionaliteit ondergebracht in de functie BaseForm die het mogelijk maakt om waarden in willekeurige stelsels te representeren. In[2]:= BaseForm[123456, 2] Out[2]//BaseForm= 111100010010000002 In[3]:= BaseForm[123456, 8] Out[3]//BaseForm= 3611008 In[4]:= BaseForm[123456, 16]
Figuur 64
4.2.2
Gebruik van de functie BaseForm in Mathematica.
Enumeraties
Enumeraties kunnen gezien worden als een algemeen geval van het schema dat aangegeven is in Figuur 63. In het geval van enumeraties wordt er een correspondentie gelegd tussen de elementen van een verzameling en getallen. Een sprekend voorbeeld wordt gegeven voor C in het onderstaande Figuur 65. De naamgeving in de taal C is goed gekozen. Het statement ’typedef’ geeft aan dat er een nieuw datatype gedefinieerd gaat worden van een geënumereerd type. De naam die aan dit nieuwe type gegeven gaat worden is WEEK_DAY,
Inleiding Natuurkundige Informatica
71
1997
Datatypes en Datastructuren
typedef enum
Datatypes
{SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } WEEK_DAY;
....... main() { WEEK_DAYwork; for(work=MONDAY; work <= FRIDAY; work++) ............... Figuur 65
Voorbeeld van enumeratie types in C.
terwijl de elementen die tot haar gerekend worden tussen de accolades gezet worden. Daarna kan er gewerkt worden met dit type als ware het een standaard gedefinieerd type. Overigens, zoals al aangegeven in de introductie, er is wel een type gedefinieerd maar nog weinig operaties. De vergelijking en assignatie worden in Figuur 65 geïllustreerd maar (helaas) is het ook mogelijk om te zeggen: work = SATURDAY; work++;
zonder dat de compiler of wat dan ook protesteert.
4.2.2.1
Characters
Characters, in goed nederlands de letters van het alfabet vermeerderd met interpunctietekens, leestekens en cijfers, vormen het voorbeeld van een geënumereerd type. De meest gebruikte conventie van de afbeelding van getallen naar characters is de zogenaamde ASCII code, weergegeven in figuur 66 op pagina 72. Het betreft hier een 7-bits code zodat er 128 mogelijke characters gerepresenteerd kunnen worden. Naast deze 000 010 020 030 040 050 060 070 100 110 120 130 140 150 160 170
NUL001 BS 011 DLE021 CAN031 SP 041 ( 051 0 061 8 071 @ 101 H 111 P 121 X 131 ’ 141 h 151 p 161 x 171
SOH002 HT 012 DC1022 EM 032 ! 042 ) 052 1 062 9 072 A 102 I 112 Q 122 Y 132 a 142 i 152 q 162 y 172
STX003 NL 013 DC2023 SUB033 " 043 * 053 2 063 : 073 B 103 J 113 R 123 Z 133 b 143 j 153 r 163 z 173
ETX004 VT 014 DC3024 ESC034 # 044 + 054 3 064 ; 074 C 104 K 114 S 124 [ 134 c 144 k 154 s 164 { 174
EOT005 NP 015 DC4025 FS 035 $ 045 , 055 4 065 < 075 D 105 L 115 T 125 \ 135 d 145 l 155 t 165 | 175
ENQ006 CR 016 NAK026 GS 036 % 046 - 056 5 066 = 076 E 106 M 116 U 126 ] 136 e 146 m 156 u 166 } 176
ACK007 SO 017 SYN027 RS 037 & 047 . 057 6 067 > 077 F 107 N 117 V 127 ^ 137 f 147 n 157 v 167 ~ 177
BEL SI ETB US ' / 7 ? G O W _ g o w DEL
Figuur 66 ASCII tabel voor 7-bits code. De oneven kolommen geven de (octale) waarde, de even kolommen het ASCII teken.
ASCII code kan men bij IBM systemen ook de EBCDIC code aantreffen. Dit is een 8-bits code die dus 256 mogelijkheden biedt om characters te representeren. Overbrengen van een ASCII file naar een EBCDIC machine, en omgekeerd, zal duidelijk in een catastrofe resulteren.
Inleiding Natuurkundige Informatica
72
1997
Datatypes en Datastructuren
Datatypes
Ook voor het representeren van getallen kunnen de characters gebruikt worden. De voor- en nadelen zijn apert: de informatie is voor iedereen leesbaar en met minimale moeite overdraagbaar naar andere systemen (iets wat niet onderschat moet worden), operaties op de getallen zijn echter niet direct mogelijk omdat er eerst een conversie uitgevoerd moet worden naar een intrinsiek formaat. Voor het representeren van een niet-geheel getal met n significante cijfers zijn 7+n characters nodig, zoals duidelijk is uit Figuur 67. Ge± .123456 E ± 000 Figuur 67
ASCII representatie van getal, let op de overhead van 7 posities.
bruikt men een ASCII representatie om getallen weg te schrijven dan moet men bedenken dat er altijd voldoende decimalen weggeschreven moeten worden om afrondingsfouten te voorkomen. Binnen de PSE’s die ten tonele gevoerd zijn in de vorige paragraaf zijn de characters en hun manipulatie bijzaak, er zijn slechts rudimentaire middelen aanwezig. Voor de taal C (en C++) zijn er in het file ctype.h een aantal macro’s gedefinieerd die het leven makkelijker moeten maken. De structuur van de macro’s en de support van C maken duidelijk dat C niet de meest geschikte omgeving is om tekstmanipulaties uit te voeren. Een constructie als char c; c = c - ’a’ + ’A’;
is in staat om van de lowercase c een uppercase te maken, immers hij telt bij elk character c het (numerieke) verschil tussen de kleine letter ’a’ en de hoofdletter ’A’ op. Als c echter geen lowercase was levert de constructie verrassende resultaten op. Dit is een voorbeeld van de operaties die in sektie 4.1 genoemd zijn. Impliciet is het moeilijk vast te stellen of het toepassen van een operatie op een element weer een geldig element oplevert. Voor allerlei toepassingen zijn wij meer in aggregaten van characters, strings, geïnteresseerd. De ondersteuning in Fortran voor characters en strings is niet veel uitgebreider. Fundamenteel is echter dat in C wel een eind character gedefinieerd is voor een string (de ’\0’) terwijl dit in Fortran niet aanwezig is. Voor de complexere operaties als het zoeken van een string in een substring zijn binnen string.h een aantal mogelijkheden gedefinieerd. Binnen Fortran77 en Fortran90 zijn hiervoor een aantal intrinsic functions gedefinieerd, eenzelfde strategie treft men aan bij Mathematica. De meeste operaties op strings hebben betrekking op het zoeken naar substrings of het sorteren van strings op volgorde. Er moet nogmaals op gewezen worden dat de manier waarop een type gedefinieerd wordt afhankelijk is van de omgeving waarin gewerkt wordt. Voor een tekstverwerker is een character van vitaal belang. In Figuur
Figuur 68
Attributen die behoren bij het datatype character in een tekstverwerkings omgeveing.
68 is een dialoog box weergegeven van de tekstverwerker FrameMaker om alle attributen van een character te bepalen.
Inleiding Natuurkundige Informatica
73
1997
Datatypes en Datastructuren
4.2.2.2
Datatypes
Integers
Heeltallige grootheden duidt men aan met de naam integers. Van oudsher vormden zij bij numerieke toepassingen een speciale klasse omdat rekenen met deze grootheden meestal sneller was dan met niet-heeltallige grootheden. Een ander voordeel van gehele getallen is dat zij exact gerepresenteerd kunnen worden, alleen het aantal elementen is afhankelijk van grootte van de representatie. Mathematisch corresponderen zij met Z. Op machine niveau worden de gehele getallen meestal gerepresenteerd in een 2-complements notatie wat impliceert dat indien er N bits ter beschikking zijn een interval van [ –2
N–1
,2
N–1
– 1]
(31)
bestreken wordt, een niet symmetrisch interval. De gebruiker moet zich helaas bewust zijn van de mogelijke grootte van N, er bestaat een grootste positieve getal1 en een kleinste negatieve getal dat gerepresenteerd kan worden. Aangezien N kan verschillen op verschillende architecturen is het uiterst onverstandig om er vanuit te gaan dat N constant is. Dit temeer daar in de meeste gevallen er een soort cyclische afbeelding gehanteerd wordt, d.w.z. het grootste positieve getal plus één is gelijk aan het kleinst negatieve getal (een waarschuwing hiervan wordt niet gegeven). Dit betekent dat de standaard associatieve en distributieve eigenschappen voor gehele getallen a + (b – c) = (a + b) – c a × (b – c) = a × b – a × c
(32)
niet hoeven te werken. Zij zullen in elk geval niet werken in dit voorbeeld: (grootste_positieve_getal + 1) - 2 = ongedefinieerd
terwijl (grootste_positieve_getal - 2) + 1 = gedefinieerd
Bij C en Fortran77 is de gebruiker het slechtste af: hier moet hij in principe de representatie die gebruikt wordt voor het gehele getal2 omzetten in een bereik. Zulks is in C overigens bijna machine onafhankelijk te automatiseren, een voorbeeld daarvan wordt gegeven in figuur 69 op pagina 74. Bij Fortran90 kan er met #define #define #define #define Figuur 69
BITSPERBYTE BITS(type) HIBITS MAXSHORT
8 (BITSPERBYTE * (int)sizeof(type)) ((short)(1 << BITS(short) - 1)) ((short) ~HIBITS)
Bijna machine onafhankelijke manier om een maximale integer voor een systeem te bepalen.
behulp van een intrinsic functie (select_int_kind) bepaald worden of een bepaald bereik representeerbaar is op de machine waarop men werkt. Voor de gehele getallen kent Mathematica geen methode om te bepalen wat het grootst mogelijke getal is. Het voordeel van de ’oneindige nauwkeurigheid’ waarmee gewerkt wordt binnen de gehele getallen moet niet onderschat worden. Het voornaamste probleem zit met name in het (te) kleine bereik van de getallen op 32-bits systemen.
1.Voor 32 bits systemen is dit 2147483648 en -2147483647 2.C kent maar liefst drie verschillende formaten voor gehele
Inleiding Natuurkundige Informatica
74
getallen.
1997
Datatypes en Datastructuren
4.2.3
Datatypes
Fixed point
Soms is het voordelig om te werken met een tussenvorm tussen integers en floating points die wordt aangegeven met fixed point arithmetic, d.w.z. niet heeltallige representaties waarbij de decimale punt op een vaste plaats staat! Wisselkoersen van buitenlandse valuta zijn hier een bekend voorbeeld van. Fixed points kunnen eenvoudig als geheel getal gerepresenteerd worden door het met een juist gekozen constante te vermenigvuldigen. Arithmetische operaties op dit soort grootheden verdienen enige voorzichtigheid zoals uit het onderstaande blijkt. Conversie: xˆ = ( int ) ( x × SCALE )
(33)
xˆ × yˆ Vermenigvuldigen: ------------------SCALE
(34)
xˆ × SCALE Delen: ---------------------------yˆ
(35)
Complexere operaties worden lastiger te implementeren. Neemt men als voorbeeld SCALE = 10 dan zal de benaderde waarde xˆ de eerste decimaal bevatten van de originele waarde van x. Nemen wij als voorbeeld x = 1.7 en y = 3.1415 en SCALE = 10. In dat geval geldt dat xˆ = 17 en yˆ = 31 . Deelt men dan xˆ en yˆ op de volgende manier --xˆ × SCALE = 17 ------ × 10 = 0 yˆ 31
(36)
terwijl de bewerking op de manier van vergelijking (35) oplevert 17 × 10 ------------------ = 5 → x = 0.5 31
4.2.4
(37)
Floating Point
In ons dagelijks taalgebruik duiden wij alle grootheden waarin een zogenaamde decimale punt voorkomt aan als niet-gehele of floating point getallen, dit laatste ter onderscheid van de hiervoor behandelde fixed point getallen. Wiskundig, en ook natuurkundig, gesproken drukken wij ons daarmee slordig uit. Correcter ware het om te spreken over de rationele getallen, dwz. de getallen die als quotiënt van twee gehele getallen geschreven kunnen worden, en de reële getallen. Beide hebben, voor computers, hun specifieke problemen: de eerst genoemde kunnen uitgedrukt worden in zoveel decimalen als men maar wil, terwijl voor de tweede groep in principe een oneindig aantal decimalen nodig is voor de juiste representatie. Het zal duidelijk zijn dat hier de implementatie en specificatie die in sektie 4.1 genoemd zijn hand in hand gaan met de hoeveelheid ruimte die ter beschikking staat voor de representatie. In de meeste toepassingen wordt één en ander minder dramatisch doordat fysische grootheden meestal met een eindige nauwkeurigheid bekend zijn. Als voorbeeld: als een straal maar met 1% nauwkeurigheid gemeten kan worden, heeft het weinig zin om voor een oppervlakte berekening π in 100 decimalen in te voeren. Voor de rationele getallen geldt dat, eigenlijk, voor het behoud van nauwkeurigheid, alle bewerkingen hier uitgevoerd moeten worden op de quotiënten van de gehele getallen. Bij de meeste derde generatie talen is het zo dat de gebruiker zich vastlegt op een bepaald aantal decimalen bij de keuze van het datatype. Dit aantal decimalen wordt op zijn beurt gedefinieerd door de hardware van het systeem. De overweging hiervoor is tweeledig: enerzijds ruimte, de definitie van deze talen stamt uit de tijd dat geheugen duur was, en anderzijds snelheid: hoe meer decimalen hoe trager de berekening.
Inleiding Natuurkundige Informatica
75
1997
Datatypes en Datastructuren
Datatypes
Mathematica heeft vermoedelijk de meest natuurlijke en volledige implementatie gemaakt van de niet-gehele getallen. Ten eerste is het mogelijk om niet gehele getallen te benaderen als rationeel getal. In het onderstaande wordt dit toegelicht. In[1]:= N[Pi, 10] Out[1] = 3.141592654 In[2] := Rationalize[N[Pi, 10], 0] Out[2] = 245850922/78256779
De functie N vraagt Mathematica om een expressie te evalueren met een bepaalde precisie, in dit geval 10 decimalen. Rationalize benadert dit zo nauwkeurig mogelijk (optie 0). Men kan met de rekenmachine nagaan dat dit een goede benadering is. Meer decimalen waren overigens ook opvraagbaar geweest: In[3]:= N[Pi, 100] Out[3] = 3.141592653589793238462643383279502884197169399375105820974944592307816\ 406286208998628034825342117068 ∞
Een ander voorbeeld is voorbeeld Γ(z) =
∫t
z – 1 –t
e dt
o In[1] := N[Gamma[1/7], 30] 6.5480629402478244377140933494
Maar In[2] := N[Gamma[.142857], 30] 6.548069828798543
zal maar een standaard aantal decimalen geven omdat het argument (.142857) van de gamma functie niet met voldoende nauwkeurigheid gegeven wordt, immers 1/7 is in principe oneindig nauwkeurig uit te rekenen. Anders geformuleerd: Mathematica maakt onderscheid tussen rationele getallen en reële getallen. Bij Fortran90 kan men op een vergelijkbare manier vragen om een representatie met een minimaal aantal significante decimalen (select_real_kind). Fortran77 en C staan geen specificatie toe van het aantal decimalen, het gedeclareerde type dient door de gebruiker zelf vertaald te worden in een nauwkeurigheid, terwijl er maar beperkte mogelijkheden zijn. Teneinde een en ander te begrijpen is het noodzakelijk om iets dieper in te gaan op de representatie van de niet-gehele getallen. In het algemeen kan een floating point getal genoteerd worden als x = mr e waarin m r e
(38)
de mantisse is, de radix van de notatie, de exponent. e
e+1
De in (38) gedefinieerde notatie is niet uniek immers x = mr = ( m ⁄ r )r = x . Uniciteit kan gewaarborgd worden door te eisen dat de floating point representatie genormaliseerd wordt, i.e. 1 --- ≤ m < 1 r
(39)
Deze normalisatie eis is echter niet van invloed op het getal 0, zodat daar een aparte representatie voor gekozen moet worden. Voor dataverwerkende systemen is een radix r van 2 standaard, wij zullen de afleiding echter geven voor het algemene geval van r. Zijn er voor de notatie van de mantisse Nm posities mi gereserveerd dan geldt 0 ≤ mi ≤ r – 1
Inleiding Natuurkundige Informatica
i = 0, .. ,( N m – 1 )
76
(40)
1997
Datatypes en Datastructuren
Datatypes
De waarde m van de mantisse wordt dan gegeven door Nm – 1
m = r –1 +
∑
mi r
–i–2
(41)
i=0
Hierbij wordt uitgegaan van een bitnummering van links naar rechts. In geval van een genormaliseerde floating point gelden voor de mantisse de volgende relaties: Nm – 1
max m =
r –1
N –1
m max ( m i ) – Nm – 1 r–1 --------------------- = r – 1 + ----------+ ( r –1 ) i = 1 – r i+2 r2 r i=0 i=0
∑
∑
min m = r
–1
(42)
(43)
Voorts, als geldt dat emax de grootste toegestane waarde van de exponent is en emin de kleinste toegestane waarde is van de exponent, dan geldt dat max x = r
e max
(1 – r
min x = r
– Nm – 1
)
(44)
( e min – 1 )
(45)
Vrijwel alle systemen conformeren zich aan de zogenaamde IEEE 754 floating point standaard. In Figuur 70 wordt een overzicht gegeven van het bereik van deze standaard ANSI/IEEE Std 754-1985 standaard Formaat Data lengte Single-precision 32-bits Single-precision extended > 42 bits Double-precision 64-bits Double-precision extended > 78 bits Figuur 70
Implementatie verplicht aanbevolen aanbevolen aanbevolen
Definitie van de lengte van representaties volgens de ANSI/IEEE std 7541985 standaard voor floating points.
Een mogelijke machinerepresentatie is gegeven in Figuur 71 . De afbeelding van ’single’ en ’double’ precision ANSI grootheden op de elementen van de taal C is niet vastgelegd maar hangt samen met de woordgrootte van de computer waarop men werkt. De meeste mini systemen, zoals de IBM RS/6000 en de SUN, hebben een 32 bits woord, een supercomputer als de Cray daarentegen heeft een 64-bits woord. Dat betekent dat float f op de Cray een 64-bits eenheid declareert en op de SUN een 32 bits eenheid. Het zal duidelijk zijn dat dit tot verwarring kan leiden en het werken met een gebruiker gespecificeerde nauwkeurigheid veel logischer is. Dit is in talen als C niet mogelijk, Mathematica staat het wel toe. Veel problemen kunnen voorkomen worden door gebruik te maken in C van include files, zoals 3, waarin systeemparameters als nauwkeurigheid en grootte van de representatie gedefinieerd staan. Het eindige aantal bits dat men ter beschikking heeft voor het representeren van niet gehele getallen betekent dat men altijd werkt met een benadering. De eindigheid van de reeks introduceert een benaderingsfout. 3.Dit
file is specifiek voor de SUN implementatie van Unix.
Inleiding Natuurkundige Informatica
77
1997
Datatypes en Datastructuren
Datatypes
Single precision floating-point
s
exp
1
8
fraction 23
Double precision floating-point
Figuur 71
s
exp
1
11
fraction
fraction
20
32
Schematische representaties van single en double precision floating point formaten volgens de ANSI/IEEE Std 754 1985 standaard.
Floating point parameters Single +127 -126 +127 8 32 3.4*1038 1.4*10-45
emax emin exponent bias exponent width in bits format width in bits Maximum positive value Minimum positive value Figuur 72
Double +1023 -1022 +1023 11 64 1.8*10308 4.9*10-324
Floating point format parameters voor ANSI/IEEE Std 754-1985 standaard
Laat xˆ de benadering zijn van de te representeren waarde x en laat voorts de mantisse van de representatie N m bits groot zijn. Dan geldt 1 –N –1 ˆ ≤ --- r m m–m 2
(46)
Derhalve wordt de relatieve fout in de representatie gegeven door ˆ – m 1 –N m xˆ – x -------------- = ----------------- ≤ --- r m m 2 x
(47)
aangezien geldt dat 1 ⁄ r ≤ m < 1 . De grootte van de relatieve fout hangt samen met de (hardware) representatie, d.w.z. het aantal bits dat voor de mantisse gereserveerd is. In dit verband is men meestal geneigd om de grootheid r 1 – N m de machine nauwkeurigheid ε m te noemen. Voor een 32-bits machine met IEEE-754 floating point betekent dit dat in enkelvoudige precisie er (slechts) met zes significante decimalen gewerkt wordt, terwijl in dubbele precisie er 14 decimalen beschikbaar zijn. De onnauwkeurigheid die is geïntroduceerd met de representatie zal zich ook voortplanten tijdens bewerkingen. Hij kan dramatische proporties aannemen bij het aftrekken van twee getallen. Met name als beide getallen ongeveer gelijk aan elkaar zijn. Dit is als volgt in te zien. Zij x 1 en x 2 twee niet gehele getallen, en zij xˆ 1 en xˆ 2 hun benaderde representatie. Dan geldt
Inleiding Natuurkundige Informatica
78
1997
Datatypes en Datastructuren
Datastructuren
xˆ 1 = x 1 ( 1 + ε 1 ) en
xˆ 2 = x 2 ( 1 + ε 2 )
(48) (49)
waarbij geldt dat ε i ≤ ε m . Dan geldt: ∆xˆ = xˆ 1 – xˆ 2 = x 1 ( 1 + ε 1 ) – x 2 ( 1 + ε 2 ) = ( x 1 – x 2 ) ( 1 + η )
(50)
De relatieve fout in ∆xˆ wordt dan gegeven door ε1 x1 – ε2 x2 η = ------------------------------x1 – x2 ε2 ( x1 – x2 ) + x1 ( ε1 – ε2 ) = ---------------------------------------------------------------x1 – x2 x1 ≤ ε m 1 + 2 -------------------- x1 – x2
(51)
(52)
(53)
Uit (53) blijkt dat als x 1 – x 2 klein is ten opzichte van x 1 dat wil zeggen x 1 ≈ x 2 de fout in ∆xˆ niet beperkt is tot de orde ε m . Het dient benadrukt te worden dat dit niet komt door de fout in het aftrekken van xˆ 1 en xˆ 2 maar veeleer door de initiële fouten die geïntroduceerd zijn bij het afronden en manifest worden doordat een aantal significante decimalen wegvallen. Dit wordt cancellation error genoemd.
4.2.5
Rekenen in perspectief
De twee benadering van formele talen (Mathematica) en derde generatie talen (C, Fortran e.d.) zijn wezenlijk verschillend. Bij de eerste wordt het moment van numerieke representatie zo lang mogelijk vermeden en dientengevolge veel exacter gewerkt. Bij de derde generatie gaat het puur om een approximatie van het antwoord. In ’double precision’ komt dat overeen met ongeveer 14 significante decimalen, voldoende voor de meeste, maar niet alle toepassingen. De cancellation error die in de vorige paragraaf aangegeven is kan in principe roet in het eten gooien. Een verhoging van de nauwkeurigheid van representatie is niet logisch noodzakelijk een oplossing. Ten eerste moeten de betrokken parameters in voldoende nauwkeurigheid bekend zijn, ten tweede moet er ondersteuning zijn voor het formaat. In toenemende mate moet er dus bij het maniupuleren van formules nagedacht wordt of het gaat om numerieke, analytische of gemengde problemen.
4.3
Datastructuren
4.3.1
Introductie
Gebruikergedefinieerde groeperingen van datatypes noemt men datastructuren. De reden om te komen tot een dergelijke groepering is dat voor een specifieke toepassing een aantal gegevens logisch samenhangen. Als voorbeeld kan men denken aan een spectrum. De parameters die men hiermee kan associëren zijn aangegeven in Figuur 73. In principe is het goed mogelijk om al deze parameters los te definiëren maar een identificatie van een spectrum heeft weinig zin als hij niet onlosmakelijk verbonden is met de data waar het op slaat. Reden dus om deze data bij elkaar te groeperen. Een tweede (wiskundig) voorbeeld van een datastructuur is overbekend, namelijk dat van de matrices en vectoren. De coordinaten van een punt of de elementen van een matrix horen logisch bij elkaar en dienen
Inleiding Natuurkundige Informatica
79
1997
Datatypes en Datastructuren
Datastructuren
start golflengte, increment golflengte aantal meetpunten meetpunten identificatie. Figuur 73
golflengte,
typedef struct float int float char } SPECTRUM;
einde
spectrum { start, step, stop; npoints; *scan; *identifier;
Voorbeeld van een gebruiker gedefinieerde (C) datastructuur
als zodanig ook gegroepeerd te kunnen worden in een omgeving wil deze bruikbaar zijn om eenvoudig problemen op te kunnen lossen. Dit vereist twee zaken. • Ten eerste dient er een manier te zijn om data te kunnen structureren. Hoewel dit voor de handliggend klinkt is in Fortran77 een dergelijke mogelijkheid bijvoorbeeld afwezig. • Ten tweede dient er een datatype aanwezig te zijn dat kan wijzen naar een dergelijke structuur, oftewel de omgeving dient een pointer te ondersteunen. Dit tweede punt is fundamenteel om goed te begrijpen. Data, van wat voor een vorm dan ook, worden in de computer opgeslagen en corresponderen dus met een bepaalde hoeveelheid geheugen. Wanneer meerdere procedures deze data nodig hebben zijn er twee mogelijkheden: er kunnen evenveel copieën gemaakt worden als er verzoeken zijn om de data te hebben en diegenen die de data nodig hebben kunnen de plaats door krijgen waar de data staan. Ter illustratie is een C vertaling aangegeven in Figuur 74. In het linkergeval void {
foo(float x)
void {
foo(float *x)
x = sqrt(x);
*x = sqrt(*x);
}
}
main() {
main() { float
val = 20.;
float
foo(val); }
val = 20.;
foo(&val); }
Figuur 74
Verschil in C van zogenaamde argument passing. In het linker geval wordt aan ’call by value’ gedaan, in het rechter geval ’call by name’.
wordt een copie meegegeven. Algemeen refereert men naar deze methode als call by value. In het rechter geval wordt de plaats waar de waarde opgeslagen ligt meegegeven dit noemt men call by name. De wijzer naar de plek wordt aangeduid met de naam pointer. Men zou geneigd kunnen zijn om een pointer te identificeren met een fysiek adres van een geheugenplaats maar dat is niet correct. De verschillende omgevingen die wij tot nu toe de revue hebben laten passeren hebben verschillende implementaties van pointers: bij Mathematica zijn ze eigenlijk niet manifest aanwezig, in C is de standaard overdracht bij functies in de vorm van call by value, terwijl in Fortran77 alles volgens call by name overgedragen wordt. Een tweede voordeel van het werken met pointers is de efficiency. In plaats van alle data te moeten copiëren volstaat het nu om één variabele, de pointer, te laten passeren. Voorts karakteriseert men een datastructuur meestal naar de manier waarop de grootte vastgelegd moet worden als: statisch, semi-statisch en dynamisch.. De eerste kwalificatie betekent dat de grootte van de structuur (in compilatie tijd) vastgelegd is, de laatste kwalificatie betekent dat de structuur vastgelegd wordt tijdens het draaien van de applicatie, terwijl semistatisch betekent dat de gebruiker gesuggereerd wordt dat de structuur dynamisch is. Stilzwijgend hebben we hiervan al gebruik gemaakt in het voorbeeld in Figuur 73. De plaats waar de punten ingevuld moeten worden (scan) en de plaats waar de identificatie ingezet moet wor-
Inleiding Natuurkundige Informatica
80
1997
Datatypes en Datastructuren
Datastructuren
den (identifier) zijn alleen als pointers gedefinieerd, dat wil zeggen (kunnen) wijzen naar een plaats waarvan de grootte nog niet vastligt. In Figuur 75 wordt een voorbeeld gegeven van een (geneste) datastructuur die gebruikt zou kunnen worden voor het representeren van een polygon. Merk op dat de drie opeenvolgende delen als het ware op elkaar typedef struct point { float x; float y; } POINT; Figuur 75
typedef struct line { POINT from; POINT to; } LINE;
typedef struct LINE int } POLYGON;
polygon { segment[10]; nseg;
Datastructuur voor de representatie van een polygon.
bouwen. Met elke structuur kan men een (aantal) functie(s) verbinden. In Figuur 76 wordt daar een voorvoid {
void {
DrawLine(POINT *from, POINT *to)
DrawSeg(LINE*p) DrawLine(p->from, p->to);
Move(from->x, from->y); Cont(to->x, to->y);
}
} void {
DrawPolygon(POLYGON*p) int i; for(i=0; inseg; i++) DrawSeg(p->segment[i]);
Figuur 76
C code voor het tekenen van een polygon gebruikmakend van de eerder gedefinieerde structuren.
beeld van gegeven. Met de structuur POINT worden twee functies geassocieerd (move en cont). Met de structuur LINE wordt DrawLine geassocieerd terwijl met POLYGON de functie DrawPolygon verbonden is. Het werken met dergelijke structuren maakt dus ook een heldere structurering van de software mogelijk. Te midden van alle variaties die er bedacht kunnen worden rond structuren zijn er een aantal min of meer standaard geworden. In deze paragraaf zullen wij een aantal van dergelijke structuren nader onderzoeken.
4.3.2
Vector/Array
In numerieke applicaties nemen vectoren en matrices een zeer belangrijke rol in. Vandaar dat de meeste PSE’s hardware- en softwarematig dit datatype op een aparte manier ondersteunen. Dat kan variëren van impliciete datastructuren (zoals in C en Fortran77) tot voorgedefinieerde operaties (Mathematica en Fortran90). Ook is er speciaal software ontwikkeld (Basic Linear Algebra Subroutines) die met name op vectoren en matrices werken en is er speciale hardware ontwikkeld: array- en vectorprocessoren. Karakteristiek voor vectoren, deze naam gebruiken wij voor één-dimensionale structuren, en matrices, meer dimensionale structuren, is dat hun elementen aangeduid wordt door een (base) naam en een (volg) nummer. Het aantal volgnummers hangt af van de dimensionaliteit van het systeem. Karakteristiek voor vectoren en mactrices is dat alle elementen dezelfde grootte hebben en dat de ruimte die zij in beslag nemen in het geheugen samenhangend is (ook wel contigue genoemd). Dat betekent dat een bepaalde plaats direct gevonden kan worden als men weet hoe groot één element is en welk element men nodig heeft. Voor het doorgeven van vectoren en matrices zijn pointers een ideaal gereedschap: het is veel efficiënter om alleen ’de plaats waar’ door te geven dan alle elementen. Overigens is dat niet helemaal probleemloos. In de klas-
Inleiding Natuurkundige Informatica
81
1997
Datatypes en Datastructuren
Datastructuren
sieke derde generatie talen als Fortran77 en C is de dimensie van de matrix niet direct gekoppeld aan de pointer, dat betekent dat hij op verschillende manieren geïnterpreteerd kan worden. Dit is als volgt in te zien. Beschouw als voorbeeld een twee dimensionale structuur van n bij m elementen. De pointer zal dan wijzen naar het totaal, maar als n en m onbekend zijn is elke andere onderverdeling, bijvoorbeeld m bij n ook mogelijk. Hier kunnen dus gauw fouten gemaakt worden. Een tweede vraag is of statische en dynamische structuren mogelijk zijn. Het antwoord is dat dit afhangt van de omgeving waarin met werkt. Fortran77 kent alleen maar statische structuren. De nare consequentie hiervan is dat bij het schrijven van een programma bekend moet zijn hoe groot alle vectoren/matrices worden. Als we denken aan het voorbeeld uit figuur 73 op pagina 80 dan zou in een Fortran77 programma de grootte van scan en identifier van te voren gedefinieerd moeten worden. In Fortran90 zijn overigens deze beperkingen verholpen. C kent zowel statische als dynamische declaraties. De statische zijn vrijwel één-op-één vergelijkbaar met die van Fortran77 en hebben een volgende structuur data_type
naam[dimensie];
Voor dynamische declaraties maakt men gebruik van pointers en een (systeem) functie om ruimte te kunnen alloceren. De allocatie gaat dan in twee stappen data_type_pointernaam; naam = gealloceerde_ruimte(grootte)
In een voorbeeld zou men krijgen float *scan; scan = (float*) calloc(n, sizeof(float));
De eerste regel declareert de variabele scan als een pointer naar een plaats, die vooralsnog leeg is, terwijl in de tweede regel met behulp van de functie calloc ruimte opgevraagd wordt ter groot van n elementen elk sizeof(float) bytes groot. Omdat de gebruiker nu zelf de allocatie (creatie) ter hand genomen heeft moet hij ook zorgen voor het omgekeerde: het opruimen van de data als ze niet meer nodig zijn (zie ook sektie 4.1). De tegenhanger in C van calloc, free, moet dan aangeroepen worden. Vergeet men dit te doen dan zal het programma zogenaamd geheugen lekken en uiteindelijk zorgen voor het vastlopen van het systeem. In Mathematica gelden soortgelijke overwegingen maar met dit verschill dat er even eenvoudig met formele matrices gewerkt kan worden als met numerieke. Het voorbeeld in Figuur 77 laat zien dat zowel het formele als het dynamische door elIn[8]:= MatrixForm[Array[a, {2, 3}]] Out[8]//MatrixForm= a[1, 1] a[1, 2] a[1, 3] a[2, 1] a[2, 2] a[2, 3] In[9]:= MatrixForm[Transpose[Array[a, {2, 3}]]] Out[9]//MatrixForm= a[1, 1] a[2, 1] a[1, 2] a[2, 2] a[1, 3] a[2, 3] Figuur 77
Manipulatie van een formele, dynamische, matrix in Mathematica.
kaar heen gebruikt kunnen worden. Eén van de gevolgen hiervan is dat men ook bewerkingen als inversie van matrices formeel kan uitvoeren. Voor het eenvoudige geval van een 2 bij 2 matrix wordt het voorbeeld gegeven in Figuur 78. In de noemer herkent men snel de determinant van de matrix a. De bruikbaarheid van dit soort operaties neemt even snel af als de rekentijd toeneemt4.
4.
Overigens heeft Mathematica de mogelijkheid om C/Fortran code te genereren op grond van expressies.
Inleiding Natuurkundige Informatica
82
1997
Datatypes en Datastructuren
Datastructuren
De grootte van matrices en vectoren kan sterk uiteenlopen. Matrices met een dimensie van N = 1000000 In[10]:= Inverse[Array[a, {2, 2}]] a[2, 2] Out[10]= {{------------------------------------, -(a[1, 2] a[2, 1]) + a[1, 1] a[2, 2]
>
a[1, 2] -(------------------------------------)}, -(a[1, 2] a[2, 1]) + a[1, 1] a[2, 2]
>
a[2, 1] {-(------------------------------------), -(a[1, 2] a[2, 1]) + a[1, 1] a[2, 2]
>
a[1, 1] ------------------------------------}} -(a[1, 2] a[2, 1]) + a[1, 1] a[2, 2]
Figuur 78
Formele inversie van een matrix in Mathematica.
en derhalve ongeveer 1012 elementen behoren tot de mogelijkheden. Een typisch voorbeeld van zo’n geval is de berekening van electron structuren van H2O. Mathematisch is dit equivalent met het bepalen van de eigenwaarden van de Hamiltoniaan van het systeem. In 1981 konden matrices die tot 256 374 configuraties van de 10 electronen in H2O beschreven verwerkt worden. Die limiet was tegen 1990 gegroeid tot boven de 106. Het zal duidelijk zijn dat in zulke gevallen niet alle elementen op de disk opgeslagen kunnen worden De structuur van matrices en hun opslag is al sedert de 70er jaren, en de ontwikkeling van Fortran, onderwerp van onderzoek. Twee soorten structuur hebben in de loop van de jaren speciale aandacht gekregen: de zogenaamde band- en sparse matrices. Bij de eerste categorie zijn alle niet nul elementen gelokaliseerd binnen een bepaalde band ten opzichte van de hoofddiagonaal, bij de tweede kunnen ze willekeurig verspreid zijn over de hele matrix. Een extreem voorbeeld van een bandmatrix is de eenheidsmatrix. Nemen wij die even als voorbeeld dan is de reden voor het onderzoek eenvoudig in te zien. De verhouding tussen niet nul elementen en het totale aantal elementen, de zogenaamde sparseness, wordt in dit geval gegeven door N/N2. Voor een eenheidsmatrix met een dimensie van 100 is dus 1% van alle getallen nog maar ongelijk aan nul! Niet alleen wordt op die manier een grote hoeveelheid niet gebruikt maar ook wordt een grote hoeveelheid rekenkracht misbruikt. Voor een matrix/vector vermenigvuldiging waarbij men deze eenheidsmatrix gebruikt geldt dat slechts 1% van alle operaties zinvol is, oftewel dat men effectief op één honderdste van de eigenlijke snelheid loopt. Met name binnen mathematische bibliotheken is er grote aandacht voor dit soort problemen. Het is goed om te bedenken dat de inefficiency bij dit soort zaken toeneemt als de grootte van het probleem toeneemt. In dat soort gevallen spreekt men van een slechte schaalbaarheid van het probleem. Onder een sparse, of ijle, matrix verstaat men een matrix waarin er geen structuur is van de niet nul elementen, maar het aantal niet nul elementen kleiner is dan 1% van de totale matrix. In dat soort gevallen loont het om over te gaan op een andere vorm van opslag, namelijk door de indices en de waarde van de niet-nul elementen op te slaan. Overigens is het werken met sparse matrices niet eenvoudig, want het produkt van twee sparse matrices hoeft niet altijd weer sparse te zijn, maar de plaatsen waar de niet-nul elementen zich zullen bevinden is helemaal niet te voorspellen. Algemeen kan men zeggen dat het werken met vectoren en matrices met name efficiënt is als de operaties op alle elementen uitgevoerd moeten worden en het lineaire algebra achtige zaken betreft. Zaken als het invoegen en verwijderen van elementen uit een vector of bijvoorbeeld het verwijderen van een rij en kolom uit een matrix zijn in deze klassieke notatie lastig te realiseren en ook moeilijk te programmeren. Voor dit soort zaken zijn lijsten meer geschikt.
Inleiding Natuurkundige Informatica
83
1997
Datatypes en Datastructuren
4.3.3
Datastructuren
Gelinkte lijst
Het concept van een gelinkte lijst is een natuurlijke data abstractie die gebruikt wordt wanneer een datastructuur een verzameling van elementen omvat waarvan het aantal van te voren niet bekend is. De gelinkte lijst bestaat uit een verzameling van elementen; in elk element zijn twee delen te onderscheiden: een informatie gedeelte en tenminste één wijzer naar een opvolger. Afhankelijk van de structurering van de wijzers
head
link
Figuur 79
link
link
Schematische representatie van een gelinkte lijst.
spreekt men over enkel/dubbel gelinkte lijsten, binaire bomen, AVL bomen etc. Een in het oog lopend verschil met arrays en vectoren is dat een gelinkte lijst zelf de informatie bevat over zijn opvolger. Een gevolg hiervan is dat de elementen van de lijst niet contigue, i.e. samenhangend, in het geheugen gealloceerd hoeven te worden. Daardoor is het niet mogelijk om het ide element in één keer te adresseren. Hiervoor moeten eerst de i-1 voorliggende elementen verwerkt worden. Op dit verschil komen wij nog terug. Essentieel voor een gelinkte lijst is de mogelijkheid om zelf de datastructuur te kunnen definiëren en met dynamische structuren te kunnen werken. In Fortran77 is hier geen mogelijkheid toe, Fortran90 en C(++) ondersteunen dit wel, Mathematica is ondenkbaar zonder het concept van lijsten. In de navolgende behandeling zullen wij ons beperken tot C voorbeelden. Structuren die gebruikt worden in gelinkte lijsten zijn altijd herkenbaar aan het feit dat zij de expliciete administratie van hun opvolger(s/voorgangers) bevatten. Dit wordt geïllustreerd in Figuur 80. Overigens dient type
entry real value integer index type(entry), pointer:: next end type entry Figuur 80
typedef struct entry { float value; integer index; struct entry *next; } ENTRY;
Voorbeeld van datastructuur definitie in Fortran90 en C(++) voor een gelinkte lijst.
bedacht te worden dat de ruimte die nodig is voor het opslaan van de wijzer naar de volgende cel beschouwd moet worden als overhead in de storage. Voor het dynamisch, i.e. in runtime, maken en opruimen van de datastructuren zijn vervolgens nog routines nodig. De paren hebben afhankelijk van de taal en de functionaliteit verschillende namen. In C zijn voorbeelden calloc5, en free. Een typisch voorbeeld van allocatie van een nieuwe entry in C syntax wordt gegeven in Figuur 81 . De functie calloc6 heeft twee argumenten: het aantal elementen dat gealloceerd moet worden en hun grootte. Calloc retourneert een pointer naar het gebied, de cast (ENTRY *) zorgt ervoor dat de return waarde naar het juiste type omgebogen wordt. Het is niet gegarandeerd dat de aanroep van calloc slaagt. Door wat voor een reden dan ook kan het gebied te groot zijn. In dat geval wordt een speciale waarde geretourneerd, in dit geval de NULL pointer die te vergelijken is met NaN (Not a Number) bij de getallen. Een veel voorkomende fout is dat er niet getest wordt of de allocatie geslaagd is. 5.Calloc is vrijwel identiek aan malloc met dien verstande dat 6.Man calloc geeft meer informatie over de precieze aanroep.
Inleiding Natuurkundige Informatica
84
het de dataruimte initialiseert op nul.
1997
Datatypes en Datastructuren
Datastructuren
ENTRY *ep; if((ep = (ENTRY *) calloc(1, sizeof(ENTRY))) == (ENTRY*)NULL) { fprintf(stderr, "can’t allocate new entry\n"); exit(1); } ep->next = NULL; Figuur 81
Voorbeeld van dynamische allocatie in C syntax.
Wij zullen ons hier beperken tot een drietal basis bewerkingen op gelinkte lijsten en hun implementatie in C: element access, invoegen en verwijderen van elementen.
4.3.3.1
Gelinkte lijst: element access
Alle voorbeelden onderstellen de definitie van Figuur 82 . typedef struct float struct } CEL;
cel { val; cel *next;
Figuur 82
Standaard structuur die de volgende paragrafen gebruikt wordt
De sequentiële manier waarop er met de elementen gewerkt moet worden komt tot uitdrukking in de element access. In principe is zij afhankelijk van de structuur van de wijzers binnen de lijst. Binnen een enkelvoudig gelinkte lijst is het niet mogelijk om de voorganger in één keer te benaderen terwijl dat bij een dubbel gelinkte lijst wel mogelijk is. In het algemeen zal de overhead van de extra administratie (wijzers) en de moeite om de wijzers op de juiste manier aan te brengen gerechtvaardigd moeten worden door de snelheidswinst bij het gebruik. Het meest elementaire geval is dat van de enkelvoudig gelinkte lijst. In Figuur 83 wordt een tweetal voorvoid {
fwd_print(CEL *list) CEL
void {
*cp = list;
rev_print(CEL *list) if(list == (CEL*)NULL) return; else { rev_print(list->next); printf("%f\n", list->value); }
while(cp != (CEL*)NULL) { printf("%f\n", cp->value); cp = cp->next; } }
} Figuur 83
Twee voorbeelden van het printen van een enkelvoudig gelinkte lijst. Links niet-recursief voorwaartse print, rechts recursieve inverse print.
beelden gegeven van het doorlopen van een lijst. In de fwd_print wordt de lijst niet recursief voorwaarts doorlopen. Let op dat er steeds getest wordt of het einde van de lijst al bereikt is door de vergelijking met NULL te maken en dat steeds de ophoging van de pointer uitgevoerd wordt (cp = cp->next). In het rev_print voorbeeld wordt de lijst achterstevoren geprint door middel van recursie. Op het begrip recursie komen wij in het volgende hoofdstuk nog uitgebreid terug. Bedenk dat hierbij uiteindelijk evenveel functie calls op de stack komen te staan als er elementen in de lijst zijn! Het enige wat hier van belang is, is de constructie waarbij de wijzer naar het volgende element uit de lijst bepaald wordt. Bij het opzoeken van een element in een enkelvoudig gelinkte lijst komt de structuur meer naar voren. Dit
Inleiding Natuurkundige Informatica
85
1997
Datatypes en Datastructuren
CEL {
Datastructuren
*find_val(CEL*list, float val)
CEL {
a) if(list == (CEL*)NULL) return NULL; else if(list->val == val) return list; else return find_val(list->next, val);
*next_el(CEL *list,CEL *el) b) return
el->next;
}
}
CEL {
*prev_el(CEL *list, CEL *el)
Figuur 84
if(list == el) c) return NULL; else { while(list != (CEL*)NULL) { if(list->next == el) return list; else list = list->next; } return NULL; }
Voorbeelden van (a) het vinden van een element in een lijst, (b) opvolger en (c) voorganger. Let op de verschillen in probleem.
}
wordt geïllustreerd in de programma’s in Figuur 84. Uitgangspunt is hier het vinden van een bepaald getal in een lijst. Daarbij wordt er niets aangenomen over de volgorde waarin de getallen in de lijst opgeslagen staan. Voor het vinden is het dus noodzakelijk om alle elementen van de lijst af te gaan, dat is dan ook de basis van het algoritme, alle elementen worden vergeleken, gemiddeld dus n/2 pogingen. Een recursieve implementatie van dit algoritme staat in figuur a. Terzijde zij opgemerkt dat hier dus gewerkt wordt met een O(n) algoritme waarbij n het aantal elementen van de lijst is. Als returnwaarde wordt een pointer naar de cel gegeven. Het vinden van het volgende element in de lijst is uiterst eenvoudig, de opvolger van de wijzer hoeft slechts doorgegeven te worden. Problematischer is het om de voorganger van een element te vinden. In principe is er geen andere mogelijkheid dan opnieuw de lijst door te lopen en te detecteren van welk element de opvolger gelijk is aan het ’huidige’ element. Een implementatie hiervan staat gegeven in Figuur 84c. Er zijn steeds twee pointers in gebruik de huidige en zijn opvolger. Het is duidelijk te zien dat er een voorkeursrichting is voor het doorlopen van de lijst. Is de kans op het nodig hebben van het volgende en het vorige element van dezelfde grootte orde, dan verdient het aanbeveling om te werken met een dubbel gelinkte lijst, d.w.z. elk element weet zijn voorganger en opvolger.
Figuur 85
Principe schema van een dubbel gelinkte lijst.
Een practische consequentie van het geheel is dat voor een dubbel gelinkte lijst een wijzer naar een willekeurig element voldoende is om de hele lijst te kunnen accessen, terwijl men bij een enkelvoudig gelinkte lijst altijd (!) een separate wijzer naar de kop van de lijst moet bijhouden.
Inleiding Natuurkundige Informatica
86
1997
Datatypes en Datastructuren
4.3.3.2
Datastructuren
Gelinkte lijst: invoegen van elementen
De hoeveelheid werk die men moet verrichten om een element in te voegen in een lijst hangt wel af van de
voor
Figuur 86
na
Schematische representatie van het invoegen van een element in een gelinkte lijst.
structuur van de lijst, maar niet van de plaats waar de invoeging plaats moet vinden. De operatie is schematisch weergegeven in Figuur 86 waarbij opgemerkt kan worden dat het gearceerde gebied zowel één enkel element als ook een lijst kan voorstellen. De procedure valt uiteen in een drietal mogelijkheden: het element dient aan de kop geplaatst te worden, danwel aan de staart, of er midden in. Het criterium om het element ergens te plaatsen is meestal afhankelijk van de interpretatie van het informatie veld. In het geval van numerieke informatie is een eenvoudige vergelijking voldoende. In dit voorbeeld zullen wij het sorteren op grootte als criterium nemen waarbij het grootste element voorop gezet wordt. In Figuur 87 is een voorbeeld van een C implementatie gegeven. Afgehandeld worden de volgende gevallen: -
lege lijst, in dat geval wordt het nieuwe element als top geretourneerd,
lijst met één element, er moet nu bepaald worden of het nieuwe element voor of achter de kop ingevoegd moet worden; de kop moet indien nodig aangepast worden -
in de lijst, maar niet aan het einde
-
aan het einde van de lijst.
Het is bijna onvermijdelijk om met een tweetal pointers in dit geval te werken. In de code is duidelijk te zien dat het weinig uitmaakt of één element of een hele lijst ingevoegd wordt: alleen twee pointers moeten verzet worden. Tevens is duidelijk dat er geen (nieuwe) data kopieën gemaakt worden en/of bewegingen van de data uitgevoerd worden. Zulks zou wel het geval geweest zijn als men met vectoren gewerkt zou hebben in plaats van gelinkte lijsten. Verwijderen van elementen Het verwijderen van elementen lijkt sterk op het invoegen. Ook nu is de ’kunst’ om het element te vinden dat voor het te verwijderen element staat. De situatie is aangegeven in Figuur 88 . Als cp de pointer is die
voor
na
Figuur 88
Symbolische representatie van de eerste fase van het verwijderen van een element uit een lijst.
wijst naar de voorganger van het te verwijderen element dan is de essentiële stap een statement als
Inleiding Natuurkundige Informatica
87
1997
Datatypes en Datastructuren
CEL {
Datastructuren
*CelInsert(float val, CEL *list) CEL CEL
*fp, *np; *cp = (CEL*)calloc(1, sizeof(CEL));
cp->val = val; if((fp = list) == (CEL*)NULL) return cp; else { if((np = fp->next) == (CEL*)NULL) { if(fp->val > val) { fp->next = cp; return fp; } else { cp->next = fp; return cp; } } else { while(np) { if((fp->val > val) && (np->val <= val)) { cp->next = np; fp->next = cp; Figuur 87 return list; } else { fp = np; np = np->next; } } fp->next = cp; return list; } }
Code voor het invoegen van een element in een enkelvoudig gelinkte lijst.
}
cp->next = cp->next->next. Overigens dient de ruimte van het verwijderde element daarna nog wel vrijgegeven te worden om te voorkomen dat het programma langzaam het geheugen vervuilt.
4.3.4
Binaire bomen
Een variatie op een gelinkte lijst wordt gevormd door de zogenaamde ’boom’. Formeel voldoet een boom aan de volgende eisen: er is slechts één node waarnaar geen verbindingen lopen; elke ander node kan bereikt worden door een (eindig) aantal stappen vanuit die ene node. -
elke andere node heeft een verbinding met andere nodes.
Men spreekt in doorsnee over de root als de speciale node (D in Figuur 89.I), terminal branches als eind nodes (A, C, F in Figuur 89.I) en de andere gevallen over branches (B, E in Figuur 89.I). In het navolgende zullen wij ons vrijwel uitsluitend beperken tot zogenaamde binaire bomen waarbij elke branch ten hoogste twee nieuwe branches heeft. Aan de verbinding zijn een aantal beperkingen opgelegd. In Figuur 89.II-IV worden een aantal incorrecte bomen aangegeven. Voor de ordening van de boom moet een bepaald crite-
Inleiding Natuurkundige Informatica
88
1997
Datatypes en Datastructuren
Datastructuren
D
I
II
D
E
B
E
B C
A
F D
III
IV
D
B Figuur 89
E
B
A E
F
Voorbeeld van een correcte binaire boom (I) en drie voorbeelden van incorrecte boom structuren (II, III, IV)
rium gebruikt worden net zoals voor de ordening van een lijst. Ook hier gebruiken we een numeriek criterium. Beschouw een element v van de boom. De volgende drie regels gelden dan: -
voor elk element u van de linker subboom geldt: l(u) < l(v)
-
voor elk element w van de rechter subboom geldt l(v) > l(w)
-
voor elk element a dat tot de boom behoort geldt dat er maar één plaats is waarvoor geldt: l(v) = a.
Een belangrijke karakteristiek van (binaire) bomen is hun diepte. Definieert men als afstand S tussen twee punten van een boom het aantal branches dat men passeert plus één S(a, b) = afstand(a, b) = aantal branches tussen a en b + 1
(54)
dan wordt de diepte van een boom gedefinieerd door Diepte = max a, b S ( a, b )
(55)
Gezien de beperkingen die aan een boom opgelegd zijn betekent dit dat in de praktijk a altijd de root zal zijn terwijl b de verzameling van alle terminal branches is. Men kan ook de diepte op een recursieve manier definiëren. Laat L(T) de linker subboom van T aanduiden en R(T) de rechter subboom dan geldt dat Diepte(T ) = max(Diepte(L), Diepte(R)) + 1
(56)
Het zal gauw duidelijk zijn dat in het algemeen de afstand van de root naar alle terminal branches niet slechts één getal is maar in doorsnee een verzameling van waarden betreft. Als het verschil tussen het grootste en het kleinste getal van deze verzameling ten hoogste één bedraagt spreken we over een gebalanceerde boom. Gezien de ordeningscriteria die gegeven zijn voor bomen zal het duidelijk zijn dat de diepte van een boom afhankelijk is van de waarde op de root. Twee uitersten staan weergegeven in figuur 90 op pagina 90. In
Inleiding Natuurkundige Informatica
89
1997
Datatypes en Datastructuren
Datastructuren
figuur a) is het slechts denkbare geval gerepresenteerd: de binaire boom is verworden tot een enkelvoudig gelinkte lijst. In figuur b) is het andere uiterste aangegeven: nu is de boom volledig gebalanceerd. Wij vol-
1
2
3
4
5
6
7
a)
4 b) 6
2 1 Figuur 90
3 5
7
Twee voorbeelden van een binaire boom. a) volledig ongebalanceerd, b) volledig gebalanceerd.
staan hier met de opmerking dat het mogelijk is om bomen te balanceren. De methode daarvoor valt buiten dit college. Het aflopen van een boom is een recursieve activiteit. Nemen we de structuur van Figuur 90 als voorbeeld dan is het recept voor het printen van de boom redelijk eenvoudig: eerst de linkertak afhandelen, typedef
struct float struct struct
tree { val; tree *left; tree *right;
} TREE; void {
PrintTree(TREE *T)
Figuur 91
Ccode voor het printen van een recursieve boom.
if(T == (TREE*)NULL) return; else { PrintTree(T->left); printf("%f\n", T->val); PrintTree(T->right); } }
dan het element zelf en vervolgens de rechtertak afhandelen. De corresponderende code is gegeven in Figuur 91. Het voordeel van de structurering kan men terugvinden in de reductie van de hoeveelheid werk (en dus de tijd) die nodig is om een element te plaatsen en terug te vinden in de boom. Teneinde dit enigszins af te leiden zullen we een voorbeeld uitwerken. Beschouw een reeks van n willekeurige getallen a 1, ......, a n . Laat vervolgens b 1, ....... , b n deze reeks zijn geordend naar grootte. Laat T(n) vervolgens het aantal vergelijkingen zijn dat nodig is om een boom met n elementen op te bouwen. Als wij aannemen dat b j de root van de boom geworden is zijn er in het totaal j-1+T(j-1) vergelijkingen nodig om de linkerkant op te bouwen. Evenzo geldt voor de rechterkant dat er n-j+T(n-j) vergelijkingen nodig zijn om de rechtertak van de boom te maken. In het totaal betekent dit Operaties(b j) = n – 1 + T ( j – 1) + T (n – j)
Inleiding Natuurkundige Informatica
90
(57)
1997
Datatypes en Datastructuren
Datastructuren
Aangezien we werken met een random reeks zijn alle waarden even waarschijnlijk wat betekent dat er gemiddeld moet worden resulterend in 1 T (n) = --n
n
∑ ( n – 1 + T ( j – 1) + T (n – j) )
(58)
j=1
wat gereduceerd kan worden tot n–1
∑ T ( j)
2 T (n) = n – 1 + --n
(59)
j=0
Het is bewijsbaar dat dit te reduceren is tot T (n) ≤ kn log n
(60)
met k = ln(4) = 1.39 . Dat maakt duidelijk dat voor een gebalanceerd boom geldt (61)
T boom(n) = O(nlog(n))
De vergelijking met een gewone, enkelvoudige, lijst van getallen is snel gemaakt. In dat geval wordt het aantal operaties gegeven door n
T lijst(n) =
∑ --2- ∼ O(n ) j
2
(62)
j=1
De structurele ordening van de getallen manifesteert zich dus onder andere in een andere orde afhankelijkheid voor het zoeken en invoegen van elementen. Het verschil is ook intuïtief in te zien als men kijkt naar de gebalanceerde binaire boom: met elke slag wordt het aantal mogelijkheden gehalveerd. Een variatie op de gebalanceerde boom is de structuur waarbij meer dan twee ’branches’ toegestaan zijn. Voorbeelden hiervan zijn onder andere een hiërarchisch filesysteem en expressie bomen die opgebouwd worden door een compiler tijdens het parsen van een expressie. Een voorbeelden hiervan wordt gegeven in Figuur 92. / bin
dev
home laurel
hardy Figuur 92
4.3.5
tmp
Deel van een UNIX filesysteem als voorbeeld van een boom structuur.
Stack
De stack (stapel) behoort tot een van de klassieke datastructuren van het type LIFO (Last In First Out). Karakteristiek voor de stack is dat er één plaats is waar de elementen toegevoegd en verwijderd worden. Hierdoor ontstaat als het ware een omkering in de tijd: het element dat het laatst is toegevoegd zal het eerst weer weggehaald worden, vandaar de naam.
Inleiding Natuurkundige Informatica
91
1997
Datatypes en Datastructuren
Datastructuren
Beide handelingen kunnen dus niet onafhankelijk van elkaar gedaan worden. De twee basis operaties die op
top
Figuur 93
Representatie van een stack. De top pointer wijst naar het eerste vrije element
bottom
de stack uitgevoerd kunnen worden zijn het opzetten (PUSH) en het afhalen (POP) van een element. Pogingen om meer elementen toe te voegen aan de stack dan mogelijk danwel meer elementen weg te halen dan er zijn resulteren in een stack overflow respectievelijk een stack underflow. De structuur is semi-statisch omdat er van te voren een maximale lengte aan toegekend wordt, maar binnen die ruimte gegroeid kan worden. Van een stack is het voldoende om alleen de wijzer naar de bodem en de eerste vrije plaats bij te houden en stilzwijgend het maximale aantal elementen. Voor een push heeft men dan de volgende, formele, operaties: PUSH test voor ruimte zet element op plaats ’top’ verhoog top met één POP
test of ’top’ gelijk is aan ’bodem’ zo ja foutmelding: stack leeg verminder ’top’met één en haal het desbetreffende element van de stack.
Een klassieke toepassing van een stack is een rekenmachine en wel die welke de zogenaamde ’reverse polish’ notatie gebruikt, dat wil zeggen eerst de operanden en dan de operator geven. Met name bij Hewlett Packard machines wordt dit standaard gebruikt. Het evalueren van de expressie (4 + 8) * 2
(63)
kan voor een dergelijk systeem omgezet worden in de volgende stack akties: Push 4; Push 8; Add; Push 2; Mul
(64)
In Figuur 94 wordt een eenvoudig programma gegeven voor een dergelijke calculatorachtige operatie. De twee basis functies Push en Pop controleren ieder voor de overflow respectievelijk de underflow. Iets meer aandacht vraagt de constructie van Mul en Add als voorbeelden van operaties. De verschillende stadia van de stack staan in Figuur 95.
4
8 4
12
2 12
Figuur 95
24
Verschillende stadia van de stack tijdens de uitvoering van het programma uit Figuur 94.
Breidt men het programma enigszins uit naar twee stacks: één voor de operatoren en één voor de operanden dan is daarmee een flexibele ’rekenmachine’ gerealiseerd. Het verwerken van de prioriteitshaakjes behoeft enige zorg, dit wordt overgelaten aan de lezer. Hoewel niet direct zichtbaar vanuit omgevingen als C en Mathematica speelt een stack ook daar een belang-
Inleiding Natuurkundige Informatica
92
1997
Datatypes en Datastructuren
#include
Datastructuren
<stdio.h>
void {
float *Stack; int StackSize, StackPtr;
}
void CreateStack(), Push(), Mul(), Add(); float Pop();
void {
void CreateStack(n_max) int n_max; { Stack = (float *)calloc(n_max, sizeof(float)); if(!Stack) { fprintf(stderr, "failure to init stack\n"); exit(1); } StackSize = n_max; StackPtr = 0; }
}
Mul() Push(Pop()*Pop());
Add() Push(Pop()+Pop());
main() { CreateStack(10); Push(4.); Push(8.); Add(); Push(2.); Mul(); printf("result is %f\n", Pop()); }
void Push(f) float f; { if(StackPtr == StackSize) { fprintf(stderr, "Stack overflow\n"); exit(1); } else Stack[StackPtr++] = f; } Figuur 94
float Pop() { if(StackPtr == 0) { fprintf(stderr, "Stack underflow\n"); exit(1); } else return Stack[--StackPtr]; }
Eenvoudige implementatie van een floating point stack.
rijke rol omdat zij onlosmakelijk verbonden is met de implementatie van recursie. Als voorbeeld kan de evaluatie van n! op een recursieve manier dienen. De code hiervoor is weergegeven in Figuur 96. Door de recursie moeten effectief n-1 functie evaluaties uitgesteld worden en in omgekeerde volgorde afgewerkt worden, precies het mechanisme van de stack. In deze fungeert de stack als een tijdelijk geheugen waarop alle locale (!) variabelen van de functie opgeslagen worden. Het zal duidelijk zijn dat dit niet direct voordelig hoeft te zijn als er veel variabelen in het spel zijn. In Mathematica is het expliciet mogelijk om de evaluatie stack te manipuleren. Behandeling van de mogelijkheden hiervan valt buiten het kader van dit college.
Inleiding Natuurkundige Informatica
93
1997
Datatypes en Datastructuren
int int {
Voorbeelden
fac(n) n; if(n <= 1 ) else
return 1; return n*fac(n-1);
} Figuur 96
C code voor het op recursieve manier uitrekenen van n!
4.4
Voorbeelden
4.4.1
Bresenham algoritme
In veel toepassingen komt de voortdurende conversie tussen gehele en niet-gehele getallen (herhaald) terug. Graphics is hiervan een duidelijk voorbeeld. Een (computer) scherm bestaat uit een eindig (geheel) aantal beeldpunten, pixels genaamd, evenzo kan een laserprinter alleen maar een discreet aantal pixels printen op het papier. Een gebruiker wil zich van dit probleem liever niet bewust zijn maar ’alleen’ de lijn van ( x 0, y 0 ) naar ( x 1, y 1 ) tekenen waarbij x en y in het algemeen niet heel-tallige getallen zijn. Daarom zal in doorsnee een dergelijke lijn geen enkel punt gemeen hebben met de betrokken pixels op het scherm zoals aangegeven in Figuur 97. De getrokken rechte lijn is het ideale pad dat men graag zou willen volgen terwijl de stippen
Figuur 97
Het benaderen van een lijn op een discreet rooster als voorbeeld van het computerscherm.
het actuele discrete pad aangeven. In Figuur 98 wordt een mogelijke code voor het trekken van de lijn gegeven. Deze code is alles behalve gelukkig want bij het passeren van de argumenten naar de functies WritePixel moet voortdurend afgerond worden. Met name bij zeer lange lijnen, of bij het trekken van lijnen op nauwkeurige rasters kunnen hierdoor (grote) afwijkingen ontstaan. Dit komt doordat de procedure DrawLine niet werkt in het logische (heeltallige) coordinaten systeem. void {
DrawLine(float x0, float y0, float x1, float y1) float int
m = (y1-y0)/(x1-x0); x, y = y0;
for(x = (int)x0; x<(int)x1; x++) WritePixel(x, y); y += m; } Figuur 98
Mogelijke C code voor het trekken van een rechte lijn op een discreet rooster.
Een klassieke oplossing voor dit probleem is gevonden door Bresenham (1965). Om de oplossing te illustreren zullen we eerst inzoomen op Figuur 97. Dit wordt gedaan in Figuur 99. Hier nemen wij aan dat het linker pixel het laatste is dat getekend is. In elk geval moet er voor de volgende stap één pixel naar rechts opgeschoven worden, maar de vraag is of er ook een stap naar boven gemaakt moet worden. Het beslissing
Inleiding Natuurkundige Informatica
94
1997
Datatypes en Datastructuren
Voorbeelden
criterium wordt gevormd door de vraag of de lijn hoger of lager de y as snijdt dan halverwege een hele y stap, aangegeven met M. De algemene vergelijkimg voor een rechte lijn wordt gegeven in parametrische vorm door F(x, y) = ax + by + c = 0. Als men dus wil weten of de lijn onder of boven het midpunt M passeert dan is het voldoende om te bepalen wat het teken is van F(x+1, y+.5). Groter dan, gelijk aan en kleiner dan nul corresponderen met respectievelijk boven, erop, en eronder doorlopen van de lijn. Ligt het punt eronder dan wordt stap L gekozen anders wordt stap H gekozen. Het enige dat nu efficiënt moet gebeuren is het maken van de keuze . Laten we daartoe de testwaarde d noemen. In het punt (x, y) is d gelijk aan d = F(x, y) =
H Figuur 99
M (x, y)
L
Previous Current Pixel Pixel
Pixel grid voor het midline algoritme van Bresenham.
Next Pixel
ax + by + c. Kiezen wij voor de stap via L dan wordt de nieuwe waarde van d’ = F(x+1, y) = d + a, terwijl het eenvoudig is om na te gaan dat als we voor H gekozen hadden de nieuwe waarde van d gegeven wordt door d + a + b. Kortom een aantal eenvoudige optellingen is voldoende om de rechte lijn te trekken. Een void {
MidPointLine(int x0, int x1, int y0, int y1) int dx, dy, incrL, incrH, d, x, y; dx = x1 - x0; dy = y1 - y0; d = 2*dy - dx; incrL = 2*dy; incrH = 2*(dy-dx); x = x0; y = y0; WritePixel(x, y); while(x < x1) { if(d <= 0) { d += incrL; x++; } else { d += incrH; x++; y++; } WritePixel(x, y); }
Figuur 100
Implementatie van het klassieke Bresenham algoritme voor het tekenen van rechte lijnen.
}
implementatie in C van dit klassiek geworden algoritme wordt gegeven in Figuur 100. Merk op dat alle operaties nu uitgevoerd worden met integers, en er dus geen afrondingsfouten meer kunnen ontstaan. Het enige vereiste is nog dat begin en eindpunt geheeltallig zijn. Dat kan men bereiken door de niet-gehele getallen van de gebruikerscoordinaten te schalen met de resolutie van het tekenen, op de manier zoals gedaan wordt met de fixed point grootheden. Een vollediger behandeling wordt gegeven in Foley et al.
Inleiding Natuurkundige Informatica
95
1997
Datatypes en Datastructuren
4.4.2
Voorbeelden
Numerieke bepaling van afgeleiden
Als illustratie van de beperkte nauwkeurigheid waarmee gerekend kan worden op computers kan het probleem van het numeriek bepalen van een afgeleide dienen7. Zij f (x) een functie dan geldt volgens het theorema van Taylor 1 2 1 3 f (x + h) = f (x) + h f '(x) + --- h f ''(x) + --- h f '''(x) + ..... 2 6
(65)
Aannemende dat h klein is levert de bekende benadering voor de afgeleide f (x + h) – f (x) f′(x) = lim ----------------------------------h h→0
(66)
Enige overweging maakt duidelijk dat het niet eenvoudig is om een juiste waarde voor h te kiezen: wordt hij te klein gekozen dan zal door de cancellation error het resultaat onbetrouwbaar worden terwijl een te grote waarde van h de betrouwbaarheid van de eerste orde benadering discutabel maakt. Dit kan men als volgt inzien. De afrondingsfout, geïntroduceerd door de eerste orde benadering, is gelijk aan (67)
e t = h f ''(x) De fout die gemaakt wordt in de juiste representatie van het differentiequotiënt is gelijk aan f ( x) e r = ε f --------h
(68)
waarbij ε f de machine nauwkeurigheid voorstelt. De optimale waarde van h wordt dan eenvoudig bepaald door te stellen dat ∂ ( ε + εr ) = 0 ∂h t
(69)
h ∼ ε f ( f ⁄ f '' )
(70)
wat resulteert in
1⁄2
Het quotiënt ( f ⁄ f '' ) noemt men meestal de ’curvature scale’. Bij gebrek aan meer informatie wordt deze grootheid geëvalueerd op het punt x. Met deze keuze van h wordt de totale minimale relatieve fout in de afgeleide gegeven door 2 1⁄2
ε total ⁄ f ' ∼ ε f ( ( f f '' ) ⁄ f ' )
∼ εf
(71)
Voor een 32-bits machine waarop in enkelvoudige precisie gewerkt wordt, d.w.z. met 6 significante decimalen, betekent dit dat met een optimale keuze van h dit differentiequotiënt een nauwkeurigheid van .1% in de bepaling van de afgeleide toelaat. Indien een hogere nauwkeurigheid vereist is moet men bijvoorbeeld een hogere orde benadering gebruik. Dit is eenvoudig in te zien: 7.
Press, W.H., Teukolsky, S.A. (1991) Computers in Physics 5,68-69.
Inleiding Natuurkundige Informatica
96
1997
Datatypes en Datastructuren
Voorbeelden
1 2 1 3 f (x + h) = f (x) + h f '(x) + --- h f ''(x) + --- h f '''(x) + ..... 2 6
(72)
1 2 1 3 f (x – h) = f (x) – h f '(x) + --- h f ''(x) – --- h f '''(x) + ..... 2 6
(73)
Combinatie van (72) en (73) levert dat de benadering f '(x) ≈ ( f (x + h) – f (x – h) ) ⁄ ( 2h )
(74)
2
een afrondingsfout heeft van ε t ∼ h f ''' . Een soortgelijke afleiding als hierboven gegeven zal dan laten zien dat h ∼ ( ε f ( f ⁄ f ''' ) )
1⁄3
∼ (ε f )
1⁄3
xc
(75)
waardoor de totale fout gegeven wordt door 2⁄3
ε total ⁄ f ' ∼ ε f
(76)
In Figuur 101 is grafisch het resultaat weergegeven voor een berekening van een afgeleide met een 64-bits 10
10
Total error (relative)
10
10
10
-2
-4
-6
-8
- 10 Approx.
10
O(h2 ) O(h)
- 12 10
-10
10
-8
10
-6
10
-4
10
-2
h (relative)
Figuur 101
Afhankelijkheid van de totale fout die in de berekening van een numerieke afgeleide gemaakt wordt als functie van de waarde voor h voor zowel enkelvoudige (cirkels) als dubbele (vierkanten) precisie.
woord corresponderend met ongeveer 14 decimalen. Duidelijk zichtbaar is de optimale waarde van h voor de bepaling van de afgeleide, en het verschuiven van deze waarde als men van een eerste orde naar een twee-
Inleiding Natuurkundige Informatica
97
1997
Datatypes en Datastructuren
Voorbeelden
de orde benadering gaat. Het feit dat de linker flank van de grafiek veroorzaakt wordt door de cancellation van significante decimalen kan bijvoorbeeld met Mathematica goed aangetoond worden. Een kanttekening: bij het bepalen van de afgeleide van een gemeten curve spelen ook de meetfouten een rol. De datapunten worden eerst gefit met een analytische functie (bv. polynoom) waarvan vervolgens de afgeleide wordt uitgerekend.
4.4.3
Command interpreter
Een goed voorbeeld van de logica van datastructuren wordt gegeven door een command interpreter. De drie data die wij hier bij elkaar willen groeperen zijn de gebruikersnaam voor een commando, de plaats waar het commando gevonden kan worden en een uitleg van het commando. Bij het voorbeeld hebben wij gedacht aan een kleinste kwadraten procedure waarbij men punten wil inlezen, manipuleren en het programma wil kunnen beëindigen. In Figuur 102 wordt deze structuur gegeven in CMD. De definitie van de functies boven de structuur is nodig vanwege C, maar zou in andere talen anders eruit kunnen zien. In de structuur My_List wordt de eigenlijke commando tabel gedefinieerd. Er staan 4 commando’s in. Merk op dat dynamisch vastint
readfile(), calculate(), stop(), help();
typedef struct char int char } CMD; CMD
functie pointer definitie
cmd { *name; (*xeq)(); *help;
structure definitie
My_List[] = { "read", readfile, "read data points", "calc", calculate,"perform least squares", "stop", stop, "exit program", "?", help, "give information"
commando definitie
}; #define N_COM
Figuur 102
sizeof(My_List)/sizeof(CMD)
Voorbeeld van datastructuur voor command interpreter
gesteld wordt hoe groot de tabel is (N_COM) door de grootte van de tabel (sizeof(My_List)) te delen door de grootte van één enkele entry (sizeof(CMD)). Het essentiële gedeelte van de command interpreter staat in Figuur 103. Hierbij wordt aangenomen dat de commando’s van de gebruiker op een standaard C manier binnen komen, d.w.z. het commando a.out read data wordt gesplitst in twee delen (read en data), de pointers naar deze strings staan in argv[1] en argv[2] terwijl argc het aantal elementen van het commando bevat (in dit geval 2)8. Essentieel is het vergelijken van het gebruikerscommando met alle commando’s in de lijst (loop over NCOM). Zodra de match gevonden is (strcmp), is de bijbehorende functie ook bekend want de totale structuur van het commando is nu gevonden 8.Voor
een goed begrijpen van het voorbeeld is het nodig om te weten dat strcmp(s1, s2) string s1 met string s2 vergelijkt en 0 retourneert als zij hetzelfde zijn.
Inleiding Natuurkundige Informatica
98
1997
Datatypes en Datastructuren
Voorbeelden
main(int argc, char* argv[]) { ............... for(i=0; i
Command interpreter in werking: de for loop loopt voortdurend de lijst af tot een fit gevonden is.
(xeq). Er wordt aangenomen dat het commando dat uitgevoerd wordt een status retourneert, die ongelijk aan nul is als het succesvol is en gelijk aan nul als het commando faalt. Men kan ook even eenvoudig de manual page printen. Daarnaast is een voordeel van deze aanpak dat hij eenvoudig uitbreidbaar is: vergroting van de structuur is het enige dat nodig is (naast natuurlijk het aanleveren van de benodigde functies). Het hart van de command interpreter van ScilImage is op deze wijze gemaakt. Het is onmogelijk om een dergelijke commando interpreter in elkaar te zetten zonder de gebruikte datastructuur.
4.4.4
Polynoom representatie
Een vaak gebruikt voorbeeld van lijsten is de representatie van polynomen. Dit soort representaties ligt ten grondslag aan b.v. Mathematica. Beschouw als voorbeeld de volgende polynoom 4
2
(77)
3x + 6x + 4x + 5
Dit kan eenvoudig als een lijst gerepresenteerd worden. Een voorbeeld hiervan wordt gegeven in Figuur Figuur 104
3x4 typedef struct float int struct
6x2
4x
5
Voorbeeld van een polynoom als gelinkte lijst en een definitie voor de datastructuur
pol { coef; power; pol *next;
104. Elk element van de lijst bevat twee getallen: de coefficiënt en de macht van de desbetreffende term van de polynoom. Twee voorbeelden van manipulatie zullen wij bespreken van deze lijst: het printen en het optellen van twee polynomen. Het printen is triviaal: alleen de lijst hoeft afgelopen te worden. Voor het optellen van twee polynomen moet meer werk gedaan worden. Het meest eenvoudig is het om dit elementsgewijs af te lopen. Dan zijn er twee mogelijkheden: als de machten hetzelfde zijn hoeven de coefficiënten alleen maar opgeteld te worden en kan een van de twee cellen opgeruimd worden
Inleiding Natuurkundige Informatica
99
1997
Datatypes en Datastructuren
Voorbeelden
als de machten verschillen moeten zij gesorteerd worden op grootte: de grootste macht eerst dan de rest van de lijst (= polynoom) afwerken. POL POL {
void POL {
*PolAdd(p1, p2) *p1, *p2; if(p1 == (POL*)NULL) return p2; if(p2 == (POL*)NULL) return p1; if(p1->power == p2->power) { p1->coef += p2->coef; p1->next = PolAdd(p1->next, p2->next); free(p2); return(p1); } if(p1->power > p2->power) { p1->next = PolAdd(p1->next, p2); return p1; } else { p2->next = PolAdd(p1, p2->next); return p2; }
if(p == (POL*)NULL) return; else { printf("%.2fx^%d", p->coef, p->power); if(p->next != (POL*)NULL) printf(" + "); PolPrint(p->next); } } main() { POL void
*PolAdd(), *p1, *p2; PolPrint();
p1 = PolAdd(Cel(2., 2), Cel(3., 1)); p2 = PolAdd(Cel(1., 3), Cel(2., 2)); PolPrint(PolAdd(p1, p2)); printf("\n"); }
} POL float int {
PolPrint(p) *p;
*Cel(coef, power) coef; power; POL
*pp;
pp = (POL*) calloc(1, sizeof(POL)); pp->coef = coef; pp->power = power; return pp;
Figuur 105
Implementatie van polynoom manipulatie met behulp van gelinkte lijsten.
}
In Figuur 105 wordt dit geïllustreerd in de procedure PolAdd. Het triviale geval dat één van de twee lijsten uitgeput is wordt eerst afgehandeld. Daarna wordt getest of twee polynoomcellen met dezelfde macht onderzocht worden, zo ja dan worden ze gecombineerd in één cel (vergeet de aanroep van de functie free niet) anders wordt er gekeken welk element het grootste is. Teneinde de implementatie voor de programmeur eenvoudig te houden is het geheel recursief geïmplementeerd. Ter completering is de procedure toegevoegd om een enkele cel van een polynoom te maken (Cel). Het voordeel van het gebruik van lijsten en pointers in dit geval is duidelijk: nieuwe cellen worden slechts dan gemaakt als het absoluut nodig is. In alle andere gevallen worden ’alleen maar’ wijzers omgelegd. Het zal duidelijk zijn dat andere bewerkingen als differentiëren en integreren eenvoudig toegevoegd kunnen worden.
Inleiding Natuurkundige Informatica
100
1997
Hoofdstuk 5:Algoritmen
Algoritmen
5.1
Inleiding
Inleiding
Onder een algoritme verstaan we een verzameling van rekenregels die dienen om een probleem op te lossen. De ’bouwstenen’ waarop een algoritme werkt zijn de datastructuren die we in het vorige hoofdstuk behandeld hebben. Er zijn twee redenen om na te denken over een algoritme. De eerste is als oefening om te zien hoe de oplossing geformuleerd moet worden. De tweede reden is om na te denken over de efficiency. Het hoeft niet zo te zijn dat voor elk probleem waarvoor men weet dat er een oplossing bestaat er ook een oplossing geformuleerd kan worden. Als voorbeeld kan men denken aan het schaakspel. Volgens de spel theorie gaat het hier om een zogenaamd ’nul som’ spel met volledige informatie. In dat soort gevallen moet er een optimale strategie (algoritme) zijn om het spel te spelen. Het vinden van dat algoritme is bepaald problematisch. In het algemeen zijn algoritmen nauw gekoppeld aan de gebruikte datastructuren, we zullen hier nog uitgebreid op terugkomen. Het idee van algoritmen is oud. Euclides bijvoorbeeld heeft een efficiënte manier bedacht om de grootste gemene deler te kunnen berekenen terwijl Paus Gregorius een aantal regels gegeven heeft voor het vaststellen van schrikkeljaren. Laten we beginnen met het probleem van het vinden van de ’grootste gemene deler’ van twee getallen u en v. Een voor de hand liggende manier (algoritme) is: 1) 2) 3) 4)
kies het minimum van u en v stel de test voor de ggd gelijk aan dit minimum test op zowel u als v modulo ggd nul is, zo ja stop zo nee verminder de ggd met één en ga door bij 3.
Een implementatie van dit algoritme in C wordt gegeven in Figuur 106 (a). Het is eenvoudig in te zien, #include <stdio.h> #include <math.h> main(argc, argv) int argc; char *argv[]; { int t1 = atoi(argv[1]), t2 = atoi(argv[2]);
a)
#include <stdio.h> #include <math.h>
b)
main(argc, argv) int argc; char *argv[]; { int t1 = atoi(argv[1]), t2 = atoi(argv[2]), i;
printf("%d\n", ggd(t1, t2)); }
printf("%d\n", ggd(t1, t2)); }
int {
ggd(int u,int v) int {
int t;
ggd(int u, int v) return (v == 0) ? u : ggd(v, u%v);
t = (u < v) ? u : v; while((u % t) || (v % t)) t--; return t;
}
} Figuur 106
Niet recursieve en recursieve programmering van het bepalen van de grootste gemene deler in C.
althans in dit geval, dat het algoritme het correcte antwoord op zal leveren. Voor verschillende argumenten u en v zal de tijd die het programma nodig heeft om de ggd te vinden verschillen. Een serie metingen is gegeven in tabel 16 op pagina 103, kolom a. De tijd is substantieel voor (redelijk) grote getallen. Deze tabel
Inleiding Natuurkundige Informatica
102
1997
Algoritmen
Inleiding
heeft een tweeledig doel: (i) zien of de gemeten tijden kloppen met de theoretische verwachting en (ii) dieu
time
v
a
b
1234567890
435268
1.2
2 10-6
123456789
435268
.9
2 10-6
12345678
435268
.7
2 10-6
1234567
435268
.5
2 10-6
123456
435268
.1
2 10-6
tabel 16
Efficiency van de algoritmen gegeven in Figuur 106 voor verschillende waarden.
nen als basis om tijden te voorspellen voor nieuwe input parameters. Het lijkt in het bovenstaande of er een lineair verband is tussen de grootte van u en de tijd die nodig is voor het bepalen van de ggd. Theoretisch is het lastig om te voorspellen wat het verband zal zijn omdat dat afhangt van de gevonden waarde voor de ggd. t ggd ≤ t loop + 2min(u, v) ( t mod + t -- )
(78)
Voor het eerste geval als de ggd 1 is kan men dan een bovengrens uitrekenen. Neem voor het min even een getal in de grootte orde van 1000000, en bedenk dat het nemen van een modulo al gauw een aantal micro seconden kost en het wordt duidelijk dat dit algoritme weliswaar correcte antwoorden oplevert, maar niet bepaald snel is. Gelukkig bestaat er voor dit probleem al zo’n kleine 2000 jaar een efficiënte oplossing in de vorm van het algoritme van Euclides: Gegeven twee gehele getallen u en v, en gegeven dat u groter is dan v, dan is de grootste gemene deler van u en v hetzelfde als de grootste gemene deler van v en (u-v). Ook van dit algoritme is het mogelijk om te laten zien dat het correct is, hoewel dat iets meer moeite kost. Het verschil met de vorige methode zit hem met name in de reductie van de mogelijkheden, bij het eerste gaat die lineair, hier met het verschil van de argumenten. Formeel kan men laten zien dat het aantal stappen 2 voor de Euclides benadering ongeveer gegeven wordt door ( ( 12ln(2) ) ⁄ π )ln(v) Wat implementatie betreft verschillende de beide procedures: de tweede is gebaseerd op de methode van de recursie, de eerste niet. Overigens is de recursie niet noodzakelijk, maar wel eenvoudig te programmeren. Meet men het snelheidsverschil van beide methodes dan vindt men ongeveer een factor 1000000 (zie kolom b van tabel 16) en een andere afhankelijkheid van de input parameters namelijk vrijwel niet! Dus twee functioneel equivalente methodes, immers het antwoord blijft hetzelfde, leveren bij implementatie in dezelfde omgeving (C) een snelheidsverschil van 106. Dat snelheidsverschil moet weer in het licht geplaatst worden van de factor 2 per 18 maanden, en is dus relevant. Beschouw als tweede voorbeeld de volgende differentiaalvergelijking: z
2d
2
y
dz
Inleiding Natuurkundige Informatica
2
+z
2 2 dy + ( z – n )y = 0 dz
103
(79)
1997
Algoritmen
Inleiding
Deze differentiaal vergelijking is karakteristiek voor problemen met een cylinder symmetrie. Oplossingen van de vergelijking zijn de zogenaamde Besselfuncties J n(x) . Formeel wordt de Besselfunctie gegeven door ∞
J n( x) =
∑ k=0
k x n + 2k ( – 1 ) --- 2 ----------------------------------k! ( n + k )!
(80)
De hoeveelheid rekenwerk die men moet verrichten om de functie te evalueren voor een bepaald argument x of orde n lijkt alleen af te hangen van het aantal termen dat men in de reeks meeneemt. Dat is echter niet zo omdat men dan de nauwkeurigheid waarmee de evaluatie plaatsvindt over het hoofd ziet, immers voor grote n zullen opeenvolgende termen minder bijdragen en dus minder termen in de reeks nodig zijn voor dezelfde precisie. Het probleem zit dus in de noodzaak om een adaptieve benadering te maken afhankelijk van het argument. Voor Besselfuncties is ook een alternatief evaluatieschema mogelijk in de vorm van een recurrente betrekking die afgeleid kan worden door de functie te differentiëren. Zonder verdere afleiding geven wij deze betrekking: 2n J n + 1 ( x ) = ------ J n ( x ) – J n – 1 ( x ) x
(81)
welke eenvoudig geverifieerd kan worden. Hiermee is het probleem teruggebracht tot het vinden van de nulde en de eerste orde Besselfunctie. Uit zowel snelheids- als nauwkeurigheidsoverwegingen lijkt deze methode te prefereren, maar niet in alle gevallen is zij stabiel! Dit is eenvoudig in te zien als men bedenkt dat als het rechterlid van de bovenstaande vergelijking ongeveer gelijk aan nul is er een grote cancellation error op kan treden. Numeriek is dit effect weergegeven in tabel 17. Na het nulpunt bij n=8 gaat het verkeerd. Voor de recurrente betrekking kan men n 0 1 2 3 4 5 6 7 8 9 tabel 17
Werkelijke waarde 0.2238907791 0.5767248088 0.35283402860.352834 0.128940.12894 0.33996(-1)0.33996(-1) 0.70396(-2)0.70397(-2) 0.12024(-2)0.12029(-2) 0.17494(-3)0.17756(-3) 0.22180(-4)0.40054(-4) 0.24923(-5)
Single volgens (81) -
0.14287(-3)
Double volgens (81) 0.3528340 0.12894 0.33996(-1) 0.70396(-2) 0.12024(-2) 0.17497(-3) 0.22374(-4) 0.40218(-3)
Evaluatie van Bessel functies J n ( 2 ) met behulp van recurrente betrekking (81) als functie van de orde uitgaande van startwaarde in 10 decimalen nauwkeurig. Werkelijk waarde is gebaseerd op tabel 9.2 Handbook of mathematical functions, Abramowitz and Stegun (pag. 398).
tot twee implementaties komen: een recursieve en een niet-recursieve zoals aangegeven in Figuur 107. Het functionele verschil tussen beide is nihil, zij produceren dezelfde antwoorden en hebben last van dezelfde cancellation errors. Wat snelheid betreft is er echter een groot verschil, de niet-recursieve variant is ongeveer 4 maal zo snel als de recursieve variant. Anderzijds hoeft het weinig nader betoog dat de recursieve variant beter leesbaar is. In dit geval is het dus niet op voorhand duidelijk of het antwoord dat berekend
Inleiding Natuurkundige Informatica
104
1997
Algoritmen
Inleiding
double my_1_jn(int n, double x) double my_2_jn(int n, double x) { { switch(n) { double jn_min_one, jn, jn_plus_one; case 0: return j0(x); if(n == 0) case 1: return j0(x); return j1(x); else if(n == 1) default: return j1(x); if(x != 0) else { return 2*(n-1)/x*my_1_jn(n-1, int n_up = 1; x) my_1_jn(n-2, x); jn_min_one = j0(x); } jn = j1(x); } while(n_up != n) { jn_plus_one = 2*n_up/x*jn jn_min_one; jn_min_one = jn; jn = jn_plus_one; Figuur 107 Recursieve en niet-recursieve implementatie n_up++; van een algoritme om een Besselfunctie van } orde N te bepalen. return jn_plus_one; } }
wordt nauwkeurig is. Laten wij tot slot een derde voorbeeld, in iets minder detail bekijken, het zoeken in een verzameling. Neem als voorbeeld het geval waarbij er N namen in een bestand staan. Als men een bepaalde naam wil zoeken zullen er gemiddeld N/2 operaties nodig zijn om het gezochte element te vinden. Als de namen opgeslagen zijn in de vorm van een binaire boom, en wij aannemen dat de boom goed gebalanceerd is, dan reduceert het aantal operaties tot LogN. Het nadeel is dat er meer tijd besteed moet worden aan het opbouwen van de verzameling. Voor toepassingen van bijvoorbeeld de PTT, waar de veranderingen klein zijn ten opzichte van het aantal malen dat het bestand geraadpleegd zal worden loont het duidelijk om een geavanceerdere datastructuur te hanteren. In dit hoofdstuk zullen wij aan de hand van voorbeelden een aantal vaak voorkomende types algoritmen behandelen. Daarbij zullen wij natuurlijk letten op de gevolgde methode maar ook letten op de zaken die de implementatie van een algoritme bepalen: -
de orde, dat wil zeggen de relatie tussen de executietijd en de input parameter van het algoritme, de betrouwbaarheid, dat wil zeggen de vraag of de code gecontroleerd is op correcte werking, de robuustheid van de code, dat wil zeggen de vraag of de code onder ’alle’ omstandigheden een correct antwoord oplevert, de portabiliteit, dat wil zeggen de vraag hoe systeemspecifiek een bepaald stuk code is de onderhoudbaarheid, dat wil zeggen de mate waarin noodzakelijke veranderingen aangebracht kunnen worden in de code.
In dit bestek zullen wij ons met name op de orde en in mindere mate op de robuustheid concentreren. In globale zin zij overigens opgemerkt dat aan de hier aangegeven criteria meestal door officiële bibliotheken als ESSL, IMSL, NAG e.d. optimaal voldaan is, terwijl dat voor eigen ontwikkelde software in veel mindere mate geldt. De aanbeveling is dan ook om waar mogelijk deze software te gebruiken.
Inleiding Natuurkundige Informatica
105
1997
Algoritmen
5.2
Orde van een algoritme
Orde van een algoritme
De orde, ook wel aangeduid als de complexiteit, van een algoritme definiëren we als de relatie tussen een eigenschap (executietijd, geheugenbeslag) en een karakteristieke parameter van het probleem. Voor de orde heeft men een vaste karakterisatie waarvan wij hier enige elementen zullen toelichten: Constant In principe is dit het meest optimale gedrag dat men kan hebben: de executie tijd is onafhankelijk van de input parameters. Log N
In vrijwel alle praktische toepassingen is dit de beste benadering van optimaal gedrag. Een voorbeeld van een dergelijke orde is het zoeken van een item in een binaire boom. Voorwaarde voor het halen van de orde is dan wel dat de boom gebalanceerd is. Dit voorbeeld illustreert ook dat voor de orde er een afweging plaats kan vinden van geheugen ruimte versus executie snelheid: het opbouwen van een binaire boom kost meer ruimte dan het neerzetten van de elementen in een (lineair) array. Dit verlies wordt goed gemaakt door de verhoogde zoeksnelheid. Log N algoritmen vertonen een grote schaalbaarheid dat wil zeggen de karakteristieke parameter N kan variëren over een groot gebied.
N
Een grote hoeveelheid algoritmen heeft een lineaire karakteristiek. Alle vector operaties, zoals het sommeren van een vector, het uitrekenen van een in-produkt, het bepalen van de mediaan van een verzameling hebben dit lineaire gedrag. Het is het beste wat men kan krijgen indien N inputs verwerkt moeten worden. Binnen de fysica is het uitrekenen van een potentiaal veld een orde N proN q ⁄r bleem, immers V ( r ) = i=1 i i
∑
N log N Deze orde is typerend voor de zogenaamde ’verdeel-en-heers’ benadering, dat wil zeggen het opbreken van een probleem in deelproblemen en die naderhand weer combineren. Het klassieke voorbeeld van een NlogN afhankelijkheid is de zogenaamde Fast Fourier Transformatie (FFT) . Ook de meeste sorteeroperaties zijn van deze orde. N2
Alle problemen die betrekking hebben op paarinteracties vertonen een kwadratische afhankelijkheid. De wisselwerking van een deeltje in een elektrisch veld is hier een duidelijk voorbeeld van: 2 N F i = 1 ⁄ 4πε 0 j = 1 q i q j ⁄ r ij . Verdubbelt men het aantal deeltjes dan verviervoudigt (!) de rekentijd. Ook alle matrix/vector bewerkingen - zoals het vermenigvuldigen van een matrix met een vector - vallen in deze klasse. Met de kwadratische afhankelijkheid eindigt ongeveer de klasse van problemen die in praktische gevallen berekenbaar zijn.
∑
N3
Matrix/matrix operaties vallen onder de derde orde problemen. Daartoe behoren in eerste instantie ook de methoden om een stelsel vergelijking Ax=b op te lossen.
en hoger Hogere orde problemen zijn niet interessant om te overwegen aangezien zij bij de minste variatie uit de grenzen van de berekenbaarheid lopen. Voor het bepalen van de orde van een algoritme bestaan twee methoden: een theoretische en een praktische. De laatstgenoemde berust eenvoudig op het meten van de executietijd als functie van de grootte van het probleem dat men aanpakt. Beschouw als voorbeeld de Unix routine qsort die zogenaamd ’in place’ - dat wil zeggen zonder gebruik te maken van een tussenvector - een serie getallen sorteert1. Het programma dat gebruikt is voor het bepalen van de orde is gegeven in Figuur 109. De essentie van dit programma is dat voor een variërend aantal elementen de tijd, nodig voor het sorteren van de getallen gemeten wordt. De herhalingsloop over het sorteren dient om deze tijd dominant te maken ten opzichte van de overhead van het programma. Het resultaat van de metingen staat uitgezet in Figuur 108. Horizontaal staat de grootte van de te sorteren vector uit, vertikaal een maat voor de tijd nodig voor het sorteren. De orde blijkt lineair te zijn, ge1.
Zie man qsort voor een beschrijving van de aanroep van de routine.
Inleiding Natuurkundige Informatica
106
1997
Algoritmen
Orde van een algoritme
0.3 0.25
tijd (a.u.)
0.2 0.15 0.1 0.05 0.0
0
Figuur 108
5000 10000 Aantal elementen
15000
20000
Tijdsafhankelijkheid van de Unix procedure qsort als functie van het aantal elementen dat aangeboden wordt aan qsort.
#include <stdio.h> main(argc, argv) int argc; char *argv[]; { int *ar, nel; int i, j, ntimes; int cmp(); nel = (argc >= 2) ? atoi(argv[1]) : 100; ntimes = (argc >= 3) ? atoi(argv[2]) : 100; ar = (int*) calloc(nel, sizeof(int)); for(i=0; i
C programma dat gebruikt is voor het benchmarken van de procedure qsort.
middeld is 10 microseconde per element nodig. Als men in de literatuur kijkt naar voorspellingen over dit soort algoritmen dan vindt men dat dit lineaire gedrag klopt. De waarde van deze orde bepaling is dat het nu mogelijk is om te voorspellen hoeveel tijd een bepaald probleem gaat kosten. Voor het geval de source code of een formele representatie van het onderliggende algoritme bekend is kan men door de zogenaamde ’operation count’ de orde bepalen. Als voorbeeld kan men denken aan het produkt van twee matrices:
Inleiding Natuurkundige Informatica
107
1997
Algoritmen
Recursieve algoritmen N
a ij =
∑ bik ckj
(82)
k=1
De zo gedefinieerde produktmatrix zal N vermenigvuldigingen en N optellingen vergen per element, dus overall een N3 afhankelijkheid vertonen. Zonder dus een uitspraak te doen over de absolute tijden kan men nu zeggen dat als het probleem 10 maal zo groot wordt, de rekentijd met een factor 1000 zal toenemen. Meestal is een dergelijke toename van de rekentijd prohibitief voor praktische toepassingen. In de rest van dit hoofdstuk zullen wij kijken naar een aantal karakteristieke technieken en algoritmen en die illustreren aan de hand van een aantal applicaties.
5.3
Recursieve algoritmen
Een essentiële bouwsteen voor het implementeren van algoritmes is recursie. Recursie hangt onlosmakelijk samen met de verdeel-en-heers strategie. Binnen verdeel-en-heers wordt een probleem opgesplitst in twee of meer deel problemen met dezelfde structuur maar van een lagere orde. De oplossingen van de deel problemen worden vervolgens gecombineerd tot een totale oplossing. Recursie kan ook gedefinieerd worden als het uitstellen van werk tot een oplossing mogelijk is. Het klassieke voorbeeld voor het laatste geval is de mathematische definitie van het begrip n! als n! =
1 n ( n – 1 )!
n≤1 n≥2
(83)
De evaluatie van n! wordt net zolang uitgesteld tot er een mogelijkheid voor berekening bestaat. Daartoe worden twee regels gehanteerd: een reductie en een terminatie. Het is niet vanzelfsprekend dat herhaald toepassen van de reductie leidt tot een situatie waarin de terminatie gegarandeerd is. Als men in (83) de tweede regel vervangt door n(n+1)! zal de reductie voortdurend toegepast kunnen worden maar nooit leiden tot terminatie, en dus nooit een antwoord geven. Voor recursief programmeren is n! niet een aansprekend voorbeeld omdat het ook niet recursief opgelost kan worden. Er is echter een klasse van problemen die alleen maar met behulp van recursie tot een goed einde gebracht kunnen worden. De meest notoire vertegenwoordiger hiervan is het spel de torens van Hanoi. Het verhaal gaat dat na de schepping van de wereld een koperen plaat met daarop 3 diamanten naalden aan boeddhistische priesters gegeven werd. Op de eerste naald zaten 64 gouden schijven met een afnemende diameter. De priesters hadden de taak om alle schijven van de eerste naar de derde naald te brengen op voorwaarde dat er maar één schijf tegelijk verplaatst werd en dat er nooit een grotere schijf op een kleinere geplaatst werd. Aan de priesters werd verteld dat ze elke dag 1
2
3 Figuur 110
Torens van Hanoi met 5schijven
zo één schijf mochten verplaatsen en dat als het probleem van de Torens opgelost zou zijn dat het einde zou aankondigen van de wereld. Laten wij voor het oplossen van het probleem eerst een paar eenvoudige gevallen onderzoeken. Het geval van twee schijven is het meest eenvoudige geval:
Inleiding Natuurkundige Informatica
108
1997
Algoritmen
Recursieve algoritmen
• breng de top schijf naar de tijdelijke opslag (2) • breng de onderste schijf naar de uiteindelijke plek (3) • breng de top naar de uiteindelijke plek (3) Voor de generalisatie naar n schijven hoeven we alleen de middelste opdracht te veranderen. In plaats van één gaat het dan om n-1. Hoe de oplossing werkt voor aantallen groter dan 2 is moeilijk voor te stellen maar het moet volgens dit schema gaan. In Figuur 111 wordt de C code gegeven. De elegantie is hopelijk indrukvoid {
MoveDisk(int ndisks, int from, int to, int tmp) if(ndisks > 0) { MoveDisk(ndisks-1, from, tmp, to); printf("Move a disk from %d -> %d\n", from, to); MoveDisk(ndisks-1, tmp, to, from); }
Figuur 111
}
C programma voor het oplossen van het probleem van de torens van Hanoi.
main(int argc, char *argv[]) { MoveDisk(64, 1, 3, 2); }
wekkend. Het is een uitdaging om te proberen om dit probleem niet recursief op te lossen maar de moeite is tevergeefs. Men zou overigens kunnen concluderen dat dus recursie een ideale manier van oplossen is voor elk probleem maar dat is ten onrechte want we hebben nog niet gekeken naar de hoeveelheid werk die verricht moet worden. Dat is bij de torens van Hanoi eenvoudig in te zien. Elke aanroep van MoveDisk genereert in principe weer 2 nieuwe aanroepen. Dus voor een hoogste niveau van 64 wordt het totale aantal aanroepen van MoveDisk gegeven door
63
calls =
∑2
k
= 2
64
– 1 ≈ 1.6 × 10
19
(84)
k=0
Voor een state-of-the-art computer met een klok frequentie van 1 ns, betekent dit dat er grootte orde van 1010 seconden gerekend moet worden wat neerkomt op ongeveer 1000 jaar. De monniken die1 schijf per dag mochten verplaatsen waren dus nog wel even bezig. Het geheim van recursie is het uitstellen van de oplossing maar wil dat werken dan moet er aan twee voorwaarden voldaan zijn. Ten eerste moet elke aanroep van de recursieve functie uniek zijn. Dat geldt triviaal voor de derde generatie talen maar bijvoorbeeld niet voor een aantal Basic interpreters waarin het begrip functie met locale variabelen onbekend is. De tweede voorwaarde is dat er genoeg ruimte moet zijn om alle oplossingen die uitgesteld worden op te slaan. Nemen wij weer het bovenstaande geval van de torens van Hanoi en gaan wij uit van 4 bytes per toestand, dan is er al een astronomische hoeveelheid geheugen nodig om dit probleem op te slaan. Intern zal de computer onveranderlijk recursie afbeelden op een stack. Een tekort aan ruimte voor de recursie zal dan ook de mededeling ’stack overflow’ genereren. Is recursie altijd de oplossing? Nee, in een aantal gevallen is het voordeliger om een niet-recursieve oplossing te gebruiken. Het eerste voorbeeld is de berekening van de faculteit. Het gebruik van de recursie is hier volstrekt overbodig omdat er een lineaire call structuur van de functie is. In zo’n geval geeft recursie alleen meer overhead. Een tweede voorbeeld waarin recursie desastreus is, is het berekenen van een Fibonacci reeks, die gedefini-
Inleiding Natuurkundige Informatica
109
1997
Algoritmen int {
Recursieve algoritmen
fac(int n) int
int {
res = n;
fac(int n) return (n > 1) ? n*fac(n-1) : 1;
while(--n) res *= n; return res;
}
} Figuur 112
Niet-recursieve en recursieve implementatie van de faculteitsberekening in C.
eerd wordt door de recurrente betrekking F0 = a
F1 = b
Fn = Fn – 1 + Fn – 2
(85)
Een recursieve implementatie van de Fibonacci reeks is gauw gegeven. De verspilling zit in het feit dat iedere tak zijn eigen evaluatie gaat doen. Bijvoorbeeld voor F7 is het nodig om F6 en F5 uit te rekenen. F6 gaat de aanroep van F5 en F4 initiëren maar in de andere tak waren die al een keer berekend. Ook hier hebben wij weer een exponentiële afhankelijkheid voor de orde maar dan wel met overbodige berekeningen. Dat scheelt astronomisch in de evaluatie snelheid. Voor n=35 neemt een recursieve implementatie op een IBM RS6000/43P ongeveer 8 seconden in beslag terwijl de niet recursieve implementatie in de grootte orde van microsecondes zit. De algemene overweging is dat als een probleem een eenvoudige structuur heeft en identieke aanroepen van een functie genereert, evaluatie op een stack dus een recursie, niet echt voordelig is. In andere gevallen is recursie te overwegen.
5.3.1
Boom van Pythagoras
Teneinde recursie in Mathematica te illustreren zullen wij een ander voorbeeld nemen dat meestal aangegeven wordt met de naam boom van pythagoras. Het gaat hier om een recursief figuur dat ontstaat door herhaald op een vierkant twee nieuwe vierkanten te zetten en dit te herhalen zolang de grootte van het vierkant groter is dan een bepaalde drempelwaarde. In Figuur 113 staat een voorbeeld dat het begrip recursief figuur direct duidelijk maakt. Terzijde zij opgemerkt dat dit soort figuren toestaat dat er ’oneindig’ veel ingezoomd wordt op plekken omdat er steeds meer detail gegenereerd kan worden. Bij dit figuur is dat overigens niet zo veel interessant nieuw detail. Dergelijke eigenschappen vindt men meer algemeen bij de zogenaamde fractals die, helaas, buiten de orde van dit college vallen. Een dergelijk figuur is eenvoudig te tekenen met behulp van recursie. Het enige ingrediënt dat men nodig heeft is de mogelijk om een vierkant te kunnen tekenen. Daarnaast is er enige basale wiskunde nodig om gegeven de lowerleft coördinaten van een vierkant uit te rekenen waar de coördinaten van de volgende twee vierkanten terecht gaan komen. Bij het algoritme gaan wij van de situatie uit dat de zijde van het vierkant gegeven wordt door een lengte L terwijl het vierkant zelf in het algemeen onder een hoek alpha staat. Het Mathematica programma dat voor het genereren van de boom gebruikt kan worden staat in Figuur 114. Voor een goed begrijpen is het nodig om te weten dat Module in Mathematica de mogelijkheid geeft om een functie, in de zin van een derde generatie taal te schrijven. In de eerste lijst, d.w.z. tussen { en }, worden de namen van de locale variabelen aangegeven. Daarna volgen de aanroepen van de benodigde functies. In het bovenstaande voorbeeld worden twee regels, Boom, toegevoegd. De scheiding tussen die twee wordt uitgevoerd op grond van de actuele grootte van L, die na de ; gespecificeerd wordt. Bij de recursieve aanroep wordt de plot output opgevangen in drie variabelen die uiteindelijk weer samengevoegd worden in een lijst. Het uiteindelijke figuur wordt zichtbaar gemaakt door het Mathematica commando Show[Graphics[Boom[........]]]. In omgevingen als Mathematica is het gebruikelijk om recursie te programmeren als een verzameling van regels waarvan men het aan de omgeving over laat om die toe te passen. Kritisch punt
Inleiding Natuurkundige Informatica
110
1997
Algoritmen
Recursieve algoritmen
Figuur 113
Voorbeeld van een recursief figuur: boom van Pythagoras.
Rect[x_, y_, L_, alpha_] := Polygon[ {{x, y}, {x+L Cos[alpha], y+L Sin[alpha]}, {x+L Cos[alpha]- L Sin[alpha], y+L Sin[alpha] + L Cos[alpha]}, {x- L Sin[alpha], y + L Cos[alpha]}, {x, y}} ] Boom[x_, y_, L_ /; L > 2 , alpha_] := Module[{sa, ca, sa1, ca1, a, b, c}, sa = Sin[alpha]; ca = Cos[alpha]; sa1= Sin[alpha+ Pi/4]; ca1 = Cos[alpha + Pi/4]; a = Boom[x-L*sa, y+L*ca, L/Sqrt[2], alpha + Pi/4]; b = Boom[x-L*sa+L/Sqrt[2]*ca1, y+L*ca+L/Sqrt[2]*sa1, L/Sqrt[2], alpha - Pi/4]; c = Rect[x, y, L, alpha]; {a, b, c} ] Boom[x_, y_, L_ /; L <= 2 , alpha_] := Module[ {}, Rect[x, y, L, alpha]] Figuur 114
Implementatie van de Boom van Pythagoras in Mathematica
is daarbij wel, in het geval dat meerdere regels van toepassing zijn, in welke volgorde gekeken wordt hoe de regels toegepast gaan worden. Meestal neemt men daarvoor de volgorde waarin zij ingevoerd zijn.
5.3.2
Adaptieve quadratuur
Als laatste voorbeeld van recursie willen wij een techniek behandelen die adaptieve quadratuur heet. In essentie gaat het hier om een methode om (numeriek) te integreren. Teneinde de behandeling wat te verlevendigen zullen wij het illustreren aan de hand van een fysisch voorbeeld. Een bekend probleem uit de electrostatica is het bepalen van de potentiaal van een vlakke plaat. In Figuur 115 wordt de situatie aangegeven. Het probleem is dat wij de potentiaal in een punt P boven een cirkelvormige plaat met straal R willen berekenen. Voor het berekenen maken wij gebruik van de infinitesimaal benadering dq dV = -----s
(86)
waarbij dV de bijdrage aan de potentiaal is van de lading dq op afstand s. Met behulp van de parameters uit Figuur 115 kan dit herschreven worden tot
Inleiding Natuurkundige Informatica
111
1997
Algoritmen
Recursieve algoritmen
p Figuur 115
z r
Relevante parameters voor het berekenen van de potentiaal van een vlakke plaat.
dr R dq 2πrσdr dV = -------------------- = -------------------2 2 2 2 r +z r +z
(87)
waarbij de ladingsdichtheid σ gedefinieerd wordt door σ=Q/πR2 en Q de totale lading is. Voor de cirkelvormige plaat wordt de totale potentiaal dan gegeven door R
1 V ( z ) = 2πσ -------------------- dr 2 2 0 r +z
∫
(88)
Voor het oplossen van deze vergelijking staan twee mogelijkheden open. In een beperkt aantal gevallen is het mogelijk om hem analytisch uit te werken. In dat soort gevallen is Mathematica mogelijk een goed hulpmiddel. In andere gevallen zal men numeriek moeten gaan integreren. Voor dat soort gevallen kan een derde generatie programmeertaal goed van pas komen. Mathematica Voor een paar bewerking hebben wij een extra pakket van Mathematica nodig, VectorAnalysis. Dit pakket wordt in de eerste regel geladen. De afleiding van de potentiaal volgt dan bijna direct uit de boven gegeven formules. Bij de functie Integrate moet men bedenken dat mathematica zelf de integratie variabele dr toevoegt aan de formule, vandaar dat hij er hier expliciet uitgedeeld wordt. PowerExpand en Simplify dienen om de oplossing in een hanteerbare gedaante te krijgen. Gegeven de potentiaal kan men ook eenvoudig de veldsterkte uitrekenen door te bedenken dat geldt E = –
∂ V ∂r
(89)
De overgang van een scalar naar een vector wordt automatisch door Mathematica gerealiseerd (er wordt een lijst teruggegeven), terwijl ook de limiet voor R naar oneindig bepaald kan worden. De oplossing die op deze manier verkregen wordt is charmant en analytisch, dus nauwkeurig. In een aantal gevallen zal de integraal niet analytisch uitgerekend kunnen worden en moeten numerieke methoden te hulp geroepen worden. Die benadering is ruimer toepasbaar maar heeft zijn eigen problemen. Wij zullen hier een aanpak laten zien die de naam draagt van adaptieve kwadratuur. Het doel is om zowel numerieke integratie als om recursie te illustreren. Uitgangspunt hierbij is het vinden van een benaderende waarde voor de integraal I =
b
∫a f ( x ) d x
(90)
In een eerste benadering, die bekend staat als de rechthoek benadering, discretiseert men de functie f op N
Inleiding Natuurkundige Informatica
112
1997
Algoritmen
Recursieve algoritmen
In[1]:= Needs["Calculus`VectorAnalysis`"] In[2]:= Clear["Global`*"] In[3]:= dv = dq / Sqrt[z^2 + r^2] dq Out[3]= ------------Sqrt[r2 + z2 ] In[4]:= dq = 2 Pi r dr density In[5]:= dv 2 density dr Pi r Out[5]= ----------------Sqrt[r2 + z2 ] In[8]:= Pot = Integrate[dv/dr, {r, 0, R}] // PowerExpand //Simplify 2 Out[8]= 2 density Pi (-z + Sqrt[R + z ]) In[10]:= Edisk = -Grad[%8, Cartesian[x, y, z]] / /PowerExpand // Simplify z Out[10]= {0,0,-2 density Pi (-1 + -------------)} Sqrt[r2 + z2 ] In[11]:= Eplane = Limit[%10, R -> Infinity]
2 Figuur 116
Berekening van de potentiaal van een plaat met Mathematica.
Out[11]= {0, 0, 2 density Pi}
punten op het interval [a, b] (x 1,x 2, x 3, ....., x N ) en benaderen de integraal waarde I door Iˆ =
xi + xi + 1
) ∑ ( xi + 1 – xi ) f (---------------------2
(91)
i
Figuur 117
Oppervlakte benadering door middel van rechthoeken (zie vergelijking (91))
xi xi+1
Met andere woorden: de integraal wordt benaderd door een serie van rechthoeken die gezet worden op de functie waarde halverwege het interval. Deze situatie is getekend in Figuur 117. Alleen voor maximaal eerste graads krommes is deze benadering exact, in alle andere gevallen zal men een fout introduceren. Men kan laten zien dat voor een dergelijke benadering geldt: b
∫a f (x) dx
3 5 = Iˆ + w e 3 + w e 5 + .....
(92)
waarbij geldt dat w = ( b – a ) ⁄ N , en e n afhankelijk is van de nde afgeleide van f op het interval. Net als
Inleiding Natuurkundige Informatica
113
1997
Algoritmen
Recursieve algoritmen
bij de berekening van een afgeleide hebben wij hier ook te maken met de orde van de benadering. Naast een benadering met rechthoeken is ook een benadering met trapeziums mogelijk, die te schrijven is als Iˆ =
f (x i) + f (x i + 1)
∑ ( xi + 1 – xi ) ------------------------------------2
(93)
i
Voor deze benadering kan men afleiden dat geldt b
∫a f (x) dx
3 5 = Iˆ – 2w e 3 – 4w e 5 + ......
Figuur 118
(94)
Trapezium benadering van een oppervlak.
xi xi+1
Het is nu eenvoudig in te zien dat 2 maal vergelijking (92) + vergelijking (94) de w3 term doet wegvallen en dus een nauwkeuriger resultaat oplevert, dat bekend staat als Simpson’s methode en geschreven kan worden als: I =
xi + 1 – xi
xi + 1 + xi
- f (x i) + 4 f (----------------------) + f (x i + 1) ∑ -------------------- 6 2
(95)
i
Het enige vervelende aan deze benadering is dat er een waarde voor N opgegeven moet worden. Deze waarde bepaalt de nauwkeurigheid van het eindantwoord: hoe groter de waarde van N hoe nauwkeuriger het antwoord. Het erg hoog zetten van de waarde van N zal wel een nauwkeurig antwoord geven, overigens binnen de grenzen van de gebruikte benadering, maar in een aantal gevallen overbodig rekenwerk oproepen, daarom zou men afhankelijk van de behoefte N locaal willen kunnen variëren. Een dergelijke strategie noemt men adaptieve quadratuur. Deze leent zich bij uitstek voor recursieve implementatie. De benadering kan als volgt omschreven worden: -
voor een interval [a, b], kies een waarde voor N en kijk of de oplossing stabiel is. Zo ja stop, zo nee: halveer het interval en pas de originele procedure toe op de twee deel intervallen.
Het charmante van deze benadering is dat de punt dichtheid zich zal verfijnen rond die plekken waar de meeste variatie in de functie is. Aannemende dat vergelijking (95) in C geprogrammeerd is als een functie Simpson(f, a, b, N) zou men de totale Simpson integratie kunnen implementeren op de manier zoals aangegeven in Figuur 119. Er kleven wel enige nadelen aan de gegeven code: de test voor de tolerantie is absoluut en er is geen bewaking of de recursie wel tijdig termineert, daarmee samenhangend is het de vraag of de tolerantie wel op de juiste manier wordt doorgegeven. Het voordeel is overigens ook apert: de methode is extreem eenvoudig te implementeren en goed leesbaar. Het effect van het adaptieve karakter wordt duidelijk uit tabel 18. Bedenk dat we voor deze evaluatie waarden moeten kiezen voor de afstand en voor de ladingsdichtheid. Het voordeel van de adaptieve procedures is dat de gebruiker zich geen zorgen hoeft te maken over de puntenspreiding. Het nadeel is dat er ook moeilijk controle op uit te oefenen is zodat een procedure in een soort oneindige lus kan raken in een poging om de gevraagde tolerantie te halen.
Inleiding Natuurkundige Informatica
114
1997
Algoritmen
Random getallen
double f(x) double x; { ncall++; return 2*M_PI*x/sqrt(x*x + 10.); }
Figuur 119
double Simpson(f, a, b, N) double f(), a, b; int N; { double sum = 0, w = (b - a)/N; int i;
Voorbeeld van de gebruik van recursie om een adaptieve quadratuur te implementeren (Simpson integratie)
for(i=0; i
AdapS(float f(), float a, float b, float tol) float
Int5, Int10;
Int5 = Simpson(f, a, b, 5); Int10 = Simpson(f, a, b, 10); if(fabs(Int5 - Int10) < tol) return Int10; else return AdapS(f, a, (a+b)/2, tol/2) + AdapS(f, (a+b)/2, b, tol/2); }
n
Tolerantie 9
46.13029040
9
.1
46.13029040
45
.01
46.02924233
81
.001
46.02856531
207
.0001
46.02940954
405
.00001
46.02942585
945
.000001
46.02942688
1953
.0000001
46.02942691
tabel 18
5.4
1.
Integraal
Evaluatie van de potentiaal (88) met behulp van adaptieve quadratuur als functie van de tolerantie.
Random getallen
In veel toepassingen is het van belang om te kunnen beschikken over willekeurige getallen. De toepassingen
Inleiding Natuurkundige Informatica
115
1997
Algoritmen
Random getallen
zijn legio lopend van de cryptografie, en simulatieprogramma’s tot en met spelletjes. Een formele definitie is op zijn plaats. Een random generator heet uniform in een bepaald interval als alle waarden in dat interval een zelfde waarschijnlijkheid hebben om voor te komen. Voor de volledigheid: onder een random generator verstaan wij het stuk programma dat de random getallen produceert. Uitgaande van een uniforme random generator kan men ook generatoren met andere verdelingen produceren, bijvoorbeeld Gaussisch door gebruik te maken van de centrale limiet stelling. Met dergelijke procedures kan men willekeurige systemen nabootsen. Beschouw daarom een uniforme generator op het interval [0, 1). Een binaire gebeurtenis kan men dan nadoen door uit die verdeling een getal te trekken en te testen of het resultaat groter of gelijk is aan 0.5. Meer algemeen, wil men een kans p op een bepaalde gebeurtenis hebben dan moet er getest worden of een random trekking tussen, bijvoorbeeld, [0, p] ligt. Toch moet er enig voorbehoud gemaakt worden bij dit begrip random. Informatieverwerkende systemen zijn deterministisch vandaar dat het in principe onmogelijk is om op zulke systemen ’echte’ random getallen te genereren. De beste benadering die men kan krijgen is een serie van getallen die pseudo-random zijn: ze lijken willekeurig, maar kunnen op elk willekeurig ogenblik exact gereproduceerd worden! De meest bekende methode om random getallen te genereren is geïntroduceerd door D. Lehmer en heet de zogenaamde linear congruential method. In meer formele zin kan men deze methode noteren als x N + 1 = ( ax N + c )mod m
(96)
waarin men m de module noemt, a de multiplier, c het increment, en x0 de startwaarde (of seed). Deze methode zal dus getallen in het interval [0, m-1] genereren. Het is eenvoudig te zien dat een dergelijke reeks deterministisch is: neemt men dezelfde waarden dan zal men dezelfde serie random getallen krijgen. Voor een gebruiker zijn meestal alle waarden verborgen behalve de eerste waarde die gegeven moet worden. Deze wordt de seed genoemd van de reeks. De andere constanten, a, c en m, zijn meestal onveranderbaar. In vele handboeken kan men de karakteristieken van bepaalde keuzen van deze getallen vinden. In tabel 19 zijn een aantal waarden opgenomen2. Bij de meeste random generatoren vindt er nog een conversie plaats Constants for Quick and Dirty Random Generators Overflow at
m
a
c
220
6075
106
1283
221
7875
211
1663
222
7875
421
1663
223
6075
1366
1283
6655
936
1399
11979
430
2531
tabel 19Enige startwaarden voor een randomgenerator gedefinieerd in (96)
van een geheel getal naar een niet geheel getal in het interval [0, 1]. De keuze van de parameters a, c en m is niet triviaal. Neemt men voor de range m 10, en alle andere parameters de waarde 7 dan levert dit de volgende reeks op: 2.
De tabel is ontleend aan Press et al.
Inleiding Natuurkundige Informatica
116
1997
Algoritmen
Random getallen
7, 6, 9, 0, 7, 6, 9, 0, .... Van de 10 mogelijke getallen komen er maar 4 verschillende voor en de reeks herhaalt zich na deze 4. Dit noemt men de periode van een random reeks en formeel definieert men deze als het aantal elementen in de pseudo-random reeks voordat er herhaling optreedt. Aan het bovenstaande geval ziet men dat de periode makkelijk kleiner kan zijn dan de maximaal haalbare. Met name in het werk van Knuth3 wordt uitgebreid ingegaan op de voorwaarden voor grote periodes. Een goede random generator heeft een lange periode maar dat is zeker niet de enige voorwaarde. Om dat te bedenken neemt men maar bijvoorbeeld m 2^31 en a en c gelijk aan 1. De random reeks zal dan de maximale periode hebben, maar volstrekt voorspelbaar oplopen. De kwaliteit van een random generator wordt dus bepaald door zijn periode en door de vraag hoe accuraat hij het willekeurig gedrag benadert, de kansverdelingsfunctie. Voor een uniforme random generator op het interval [a, b] verwacht men dat het gemiddelde (a+b)/2 is en dat in een histogram uitgezet, alle bins van het histogram even waarschijnlijk zijn, waarbij de statistisch fout van N aangenomen wordt. Een voor130
120
count
110
100
90
Figuur 120 80
Histogram van een uniforme random generator na 10000 samples.
70 10
20
30
40
50
60
70
80
90
100
bin
beeld van een dergelijke grafiek voor de standaard UNIX random generator drand48 wordt gegeven in Figuur 120: na 10.000 trekkingen wordt het gemiddelde van 100 redelijk goed benaderd binnen het interval van 10. Voldoende is een dergelijke test overigens niet. Een mogelijke andere manier om de generator te controleren is door de getallen als pseudocoördinaten te gebruiken op een twee (of drie) dimensionaal vlak. Een voorbeeld hiervan is gegeven in Figuur 121. Aangezien er zich weinig structuur in het vlak aftekent lijkt het gerechtvaardigd om te stellen dat ook wat dit betreft de random generator voldoet. Merk op dat we hier te maken hebben met een toepassing van visualisatie om snel inzicht te krijgen in de (mogelijke) structuur van data, geprint zou het een ondoenlijke zaak zijn om deze structuur te onderzoeken. Dan nog hoeft men niet verzekerd te zijn het juiste gedrag. Om dat te illustreren is het goed om een random generator die in de 70’er jaren op veel systemen gebruikt werkt nader te bekijken. I n + 1 = 65539I n
31
mod(2 )
(97)
De zo gemaakte generator is weliswaar erg snel maar heeft een duidelijke tekortkoming. Het probleem is 3.
The art of computer programming. Zie ook Taylor, C. (1991) Computers in Physics 5, 16-21.
Inleiding Natuurkundige Informatica
117
1997
Algoritmen
Random getallen
1.0
0.8
0.6
0.4
Figuur 121 0.2
Representatie van de output van een random generator in een tweedimensionaal vlak.
0.0 0.0
0.1
dat 65539 = 2
16
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0
+ 3 zodat geldt dat In + 2 = (2
16
+ 3)(2
16
+ 3 )I n
(98)
wat aanleiding geeft tot de recurrente betrekking I n + 2 = 6I n + 1 – 9I n
(99)
Kortom, als wij de getallen die op deze manier geproduceerd worden in tripletten groeperen dan zullen zij in vlakken liggen. Dat is op zich niet erg want het zal gelden voor elke random generator van dit type. Het probleem ligt meer in het aantal vlakken. Voor de bovenstaande kan men, letterlijk, laten zien dat het zeer beperkt is. Daartoe hebben wij een implementatie van de random generator gemaakt in Mathematica (de
Ran[]:=Module[{},MySeed=Mod[MySeed*65539,2^31]] MySeed=1 ts = Table[ Point[ Table[Ran[], {3}]], {i, 1000}]; Show[Graphics3D[ { PointSize[0.01],ts } ], ViewPoint-> {1, 1, 6}] Show[Graphics3D[ { PointSize[0.01],ts } ], ViewPoint-> {-1, 1, 15}] Figuur 122
Grafische analyse van random getallen die volgens de lineair congruentiële methode (97) gegenereerd zijn. De viewpoints van de twee opnames zijn in de Mathematica code te lezen.
Inleiding Natuurkundige Informatica
118
1997
Algoritmen
Verdeel-en-heers algoritmes
functie Ran in Figuur 122) en bekijken vervolgens de tot tripletten geordende getallen vanuit twee verschillende gezichtspunten. Het resultaat ziet men in Figuur 122. Vanuit de willekeurige hoek (1, 1, 6) ziet alles er in orde uit maar vanuit de speciale hoek (-1, 1, 15) (denk aan relatie (99)) ziet men duidelijk het zeer beperkte aantal vlakken dat er door de random getallen gevormd wordt. Met andere woorden een generator van een beperkte kwaliteit! Het bestuderen van de Brownse beweging is een goed voorbeeld van een toepassing waar een dergelijke deficiëntie de gebruiker parten kan spelen. Modelmatig kan men de brownse beweging beschrijven als een willekeurige beweging in N dimensies. De oplossing van de macroscopische diffusie vergelijking voor het één-dimensionale geval luidt 2
x = 2Dt
(100)
waarin x de verplaatsing van het beginpunt is, D de diffusie constante en t de tijd. Teneinde een dergelijk proces te kunnen simuleren discretiseren we eerst de tijd. Vervolgens nemen wij aan dat in elk tijdsinterval een stap genomen wordt met steeds dezelfde absolute grootte maar variabele richting. Een voorbeeld van
Figuur 123
Tweedimensionaal pad gebaseerd op een willekeurige (Brownse) beweging.
een pad in het twee dimensionale vlak dat op die manier gevormd wordt staat in Figuur 123. Voor een groter aantal deeltjes heeft het zin om te praten over de gemiddelde kwadratische verplaatsing.
5.5
Verdeel-en-heers algoritmes
Een veel gebruikte methode om efficiënte algoritmes te schrijven voor het oplossen van problemen is om het probleem op te breken in subproblemen, die op te lossen en vervolgens de deeloplossingen te combineren tot een totaaloplossing. Hierbij speelt de al eerder genoemde recursie soms een grote rol. Naast het feit dat een dergelijke methode vaak eleganter en goed leesbaar is, geldt ook dat zij meestal efficiënter is. Het ‘verdeel-en-heers’ principe zullen wij voornamelijk illustreren aan de hand van een tweetal voorbeelden. Essentieel voor een ’verdeel-en-heers’ algoritme is dat een probleem opgesplitst wordt in een aantal subproblemen met lagere orde. Binnen het kader van de bespreking over recursie hebben wij dit al eerder besproken. Vandaar dat in veel verdeel-en-heers toepassingen er recursief geprogrammeerd wordt. Een tegenvoorbeeld is het evalueren van een determinant van een matrix volgens minor-ontwikkeling. Ontwikkelt men een matrix van rang N naar een willekeurige rij of kolom dan geldt dat N! = O(N ) = N O(N – 1) = N ( N – 1 )!
(101)
anders geformuleerd deze opdeling in deelproblemen is zinloos. Er zijn echter veel voorbeelden waarbij de ’verdeel-en-heers’ strategie wel werkt. Tot de meest bekende
Inleiding Natuurkundige Informatica
119
1997
Algoritmen
Verdeel-en-heers algoritmes
voorbeelden behoort in elk geval de zogenaamde Fast Fourier Transform. De complexiteit daarvan ligt buiten het kader van dit college. In de navolgende behandeling zullen wij ons meer bemoeien met een geval waaraan wij kunnen laten zien dat door een verdeel-en-heers benadering ook formeel de orde verlaagd wordt. Zij gegeven een verzameling van getallen S i met i=1, ..,n, waarvan wij het maximum en minimum willen bepalen, bijvoorbeeld teneinde een schaling uit te kunnen voeren voor een grafische operatie. Zonder beperking der algemeenheid zullen wij aannemen dat het aantal elementen n een tweemacht is. Een recht toe recht aan oplossing is gauw geformuleerd. Neem het eerste getal als maximum en vergelijk het met de ove#define
MAX(A, B)(((A) > (B)) ? (A) : (B))
max(float *s,int n, float *smax) { *smax = s[0]; for(i=1; i
Directe implementatie van procedure om het maximum van een reeks getallen te bepalen.
rige N-1 getallen van de reeks. Daarna is het eerste extremum gevonden. Voor het bepalen van het andere extremum zijn nog n-2 additionele vergelijkingen nodig, zodat er in het totaal 2n-3 vergelijkingen nodig zijn4. Voor dit probleem is, opmerkelijk genoeg, een verdeel en heers aanpak voordelig. Dit kan men op theoretische gronden laten zien. Zij T(n) het aantal vergelijkingen dat uitgevoerd moet worden om het minimum en maximum te bepalen van de serie. Voor de ’recht-toe-recht-aan’ aanpak geldt dan dat T (n) = 2n – 3
(102)
In het geval van de verdeel en heers strategie verdeelt men de set in twee deelverzamelingen waarvan het minimum en maximum bepaald worden en combineert vervolgens van die twee verzamelingen de resultaten. Het opdelen wordt herhaald tot de deelverzameling ten hoogste nog twee elementen bevat. In dat geval geldt de volgende betrekking 1 T ( n ) = n 2T --2- + 2 î
n = 2 n>2
(103)
Formule (103) kan eenvoudig ingezien worden. De twee subverzamelingen vereisen elk T(n/2) vergelijkingen, terwijl de combinatie van de eindresultaten 2 operaties vergt. Met behulp van volledige inductie kan men laten zien dat de oplossing van (103) gegeven wordt door 3 T ( n ) = --- n – 2 2
(104)
Uit (104) blijkt dat wat het aantal vergelijkingen betreft de recursieve aanpak inderdaad voordeliger is. Men kan dit eenvoudig controleren met behulp van Mathematica. In Figuur 125 wordt hier de code en het grafische resultaat van gegeven. Bedenk dat de functie t2 alleen voor tweemachten ook een antwoord zal geven. 4.
Het is overigens niet triviaal om zo te programmeren dat deze ondergrens echt gehaald wordt.
Inleiding Natuurkundige Informatica
120
1997
Algoritmen
Verdeel-en-heers algoritmes
t1[n_ ] := 2 n - 3 t2[n_ /; n == 2] := 1 t2[n_ /: n > 2] := 2 t2[n/2] + 2
30000 25000 20000
set1 = Table[ {2^i, t1[2^i]}, {i, 1, 10}]; set 2 = Table[ {2^i, t2[2^i]}, {i, 1, 10}];
15000 10000
p1 = ListPlot[set1, PlotJoined -> True]; p2 = ListPlot[set2];
5000 5000
Figuur 125
10000
15000
20000
Mathematica code voor het vergelijken van de betrekkingen (102) en (104).
Met behulp van deze methode is het eenvoudig te verifiëren dat de winst van de verdeel-en-heers in dit geval zit in de ene vergelijking die nodig is om de twee extreme te bepalen in geval van een verzameling van twee elementen. Verandert men die in twee dan is al het voordeel weg. Een theoretisch verschil in orde hoeft echter niet te betekenen dat elke implementatie van het algoritme ook efficiënter zal lopen. Twee factoren spelen hierbij een rol. Ten eerste ontstaat er een zogenaamde overhead door de herhaalde functie call naar de minmax procedure en ten tweede is de datastructuur die men voor S kiest van belang. Indien voor elke deelverzameling van S apart ruimte gealloceerd en gedealloceerd wordt, zal de winst in vergelijkingen spoedig verloren gaan. In het onderstaande C programma wordt een mogelijke implementatie van de algoritme gegeven. #define #define
MIN(A, B) (((A) < (B)) ? (A) : (B)) MAX(A, B) (((A) > (B)) ? (A) : (B))
void {
minmax(float *s, int n,float *min,float *max) float
min1, min2, max1, max2;
if(n == 1) { *min = s[0]; *max = s[0]; } else if(n == 2) { *min = MIN(s[0], s[1]); *max = MAX(s[0], s[1]); } else { minmax(s, n/2, &min1, &max1); minmax(&s[n/2], (n+1)/2, &min2, &max2); *min = MIN(min1, min2); *max = MAX(max1, max2); } } Figuur 126
Bepaling van het minimum en maximum van een verzameling volgens de ‘verdeel-enheers’ strategie.
Het is goed om in dit geval te wijzen op de fraaie manier waarop men gebruik kan maken van een ‘gewoon’ array in C, teneinde de deelverzameling efficiënt naar de volgende procedure call door te geven. Hoewel
Inleiding Natuurkundige Informatica
121
1997
Algoritmen
Dynamisch programmeren
dezelfde mogelijkheden ook in Fortran 77 bestaan, is in dit geval de recursie niet mogelijk. Dit voorbeeld vormt tevens een fraai voorbeeld van het gebruik van pointer voor de data. In geen van de recursieve aanroepen van minmax worden de data gedupliceerd. Onderzoek van het effect hiervan wordt aan de lezer overgelaten. Wat is nu het speciale van het voorbeeld? Als we alleen het minimum of het maximum nodig hebben dan kan men op bovenstaande manier laten zien dat een verdeel-en-heers aanpak niet voordeliger is maar evenveel kost als een recht-toe-recht-aan benadering. Alvorens dus de verdeel-en-heers methode te gaan gebruiken zou men in feite een theoretische analyse moeten loslaten op het voorgestelde algoritme.
5.6
Dynamisch programmeren
Bij ’verdeel-en-heers’ algoritmes worden alle mogelijkheden geëvalueerd, het is de karakteristieke eigenschap van de methode. De methode heeft zeer veel toepassingen, maar neemt het aantal mogelijkheden sterk toe dan is de methode onpraktisch. Een alternatief wordt gevormd door dynamisch programmeren. Het verschil tussen dynamisch programmeren en verdeel-en-heers is dat er bij de eerst genoemde per keer een aantal mogelijke oplossingen geïnvalideerd wordt op grond van een bepaald criterium. Er bestaat geen duidelijke definitie van de klasse van problemen waarbij dynamisch programmeren efficiënt is. Wij zullen hier dan ook volstaan met het geven van twee voorbeelden. Essentieel voor dynamisch programmeren is de zogenaamde ’kostenfunctie’ die gebruikt kan worden als kwantificatie van een vuistregel en waaraan als enige eis gesteld wordt dat hij monotoon niet dalend is. Beschouw als voorbeeld het bekende schuifspelletje dat symbolisch aangegeven is in Figuur 127. Doel is om door te schuiven alle getallen ’met-de-klok-mee’ op volgorde te krijgen. In het totaal zijn er 9! = 362880 mogelijke toestanden, waarvan één de eindtoestand is. In principe zou men van elke toestand (de weg naar) de oplossing kunnen onthouden. Het zal duidelijk zijn dat de hoeveelheid geheugen die hiervoor nodig is en de hoeveelheid rekenwerk die verricht moet worden om alle oplossingen te genereren explodeert als het aantal toestanden toeneemt (zelfs volgens een faculteit!). Het opdelen in subproblemen heeft in dit geval geen zin: een andere methode moet gezocht worden. Een strategie is om een aantal regels op stellen die op elke situatie toegepast kunnen worden en tot de oplossing leiden, in die zin is het vergelijkbaar met recursie. Een elementaire strategie is dan gauw geformuleerd: het aantal fout geplaatste nummers moet niet toenemen. Bedenk overigens dat er een aantal situaties zijn waarin dit criterium niet logisch is! Bijvoorbeeld bij een chemische reactie spreekt men van een potentiaalbarrière om de (uiteindelijke) laagste toestand te bereiken. Binnen het beperkte kader van deze benadering zal men een dergelijke oplossing nooit vinden. Maar zelfs al hanteert men het principe van een niet stijgende kosten functie dan kan men nog (mogelijk) eindeloos heen en weergaan tussen twee toestanden met hetzelfde aantal foute posities. Daarom tellen we ook het (absolute) aantal bewegingen mee. Dit noemt men algemeen de kostenfunctie. Optimalisatie van deze functie is de drijvende kracht bij de oplossing. In dit concrete geval worden twee regels toegepast -
een lijst wordt bijgehouden van de toestanden die reeds onderzocht zijn; zij worden niet nog een keer bezocht als kostenfunctie wordt gebruikt f (n) = W (n) + d(n) waar W(n) het aantal fout geplaatste vakjes is, en d(n) het aantal stappen is vanaf de begintoestand. Die toestand wordt gekozen, die f(n) het minst laat toenemen. Als er meerdere minima zijn worden die allemaal verkend.
De verhouding tussen de ’foutheid van de oplossing W’ en de afstand ’d’ is tot op zekere hoogte arbitrair, in de huidige versie wordt hij even zwaar gewogen als een fout in de oplossing dan nog wordt het juiste antwoord gevonden, maar er zijn toepassingen waarbij het vinden van de juiste weegfactor een onderzoek op zich is. Deze voorbeelden bevinden zich met name in de sfeer van de pad planning, waarbij de ruimte waarin even buiten beschouwing gelaten wordt. De vraag is dan in algemene zin hoe (plastisch geformuleerd) een ’omweg’ moet opwegen tegen het beklimmen van een ’berg’.
Inleiding Natuurkundige Informatica
122
1997
Algoritmen
6
Dynamisch programmeren
283 164 7 5
4
d(n) 0
283 164 75
6
283 1 4 765
4
283 164 75
6
1
283 14 765
5
2 3 184 765
5
283 14 765
6
2
8 3 214 765
283 714 65
7 5
23 184 765 123 84 765
5
Figuur 127
123 8 4 765
7
23 184 765
3
4
5
123 784 65
5 7
Voorbeeld van een heuristische zoek procedure via een kostenfunctie. De waarden van de kostenfunctie worden in italic aangegeven.
Aangezien de kostenfunctie alleen maar kan toenemen geldt dat het optimale pad gevonden kan worden door per stap het optimale pad te vinden. Na evaluatie van 12 van de mogelijke 362880 toestanden is het dan mogelijk om de oplossing te vinden. Een variant op deze procedure kan gebruikt worden om in een grijswaardenbeeld een contour te volgen van een voorwerp dat gekarakteriseerd wordt door een specifieke grijswaarde. In dat geval stelt men W(n) gelijk aan het kwadratische verschil in grijswaarde. Een tweede voorbeeld is terug te voeren op de gewone matrixrekening. Stel dat het produkt van n matrices uitgerekend moet worden: M = M 1 × M 2 × ... × M n
(105)
waarbij de matrix M i r i – 1 rijen en r i kolommen heeft. De volgorde waarin de vermenigvuldiging uiteindelijk uitgevoerd wordt kan grote invloed hebben op het aantal bewerkingen dat plaats moet vinden. Daarbij zullen wij aannemen dat de vermenigvuldiging van een p*q matrix met een q*r matrix pqr operaties vereist, ervan uitgaande dat er een “gewone” algoritme gebruikt wordt. Laten wij als voorbeeld een vermenigvuldiging van 4 matrices beschouwen M = M1 × M2 × M3 × M4
Inleiding Natuurkundige Informatica
123
(106)
1997
Algoritmen
met
Lineaire stelsels
M 1 = 10 × 20
M 2 = 20 × 50
M 3 = 50 × 1
M 4 = 1 × 100
De evaluatie van M = M1 × (M2 × (M3 × M4))
(107)
zal dan 125000 operaties vergen terwijl de evaluatie van M = (M1 × (M2 × M3)) × M4
(108)
slechts 2200 operaties vergt. Niet alleen scheelt een dergelijke reductie in aantal operaties aanzienlijke rekentijd maar potentieel is zij ook nauwkeuriger als men bedenkt dat operaties afrondingsfouten introduceren. De kosten functie is nu eenvoudig gedefinieerd: zij is gelijk aan het aantal vermenigvuldigingen dat nodig is. Zij m ij de kostenfunctie voor het evalueren van het produkt van matrices M i × M i + 1 × ... × M j met 1 ≤ i ≤ j ≤ n . Het zal dan duidelijk zijn dat de volgende relatie geldt m i, j = î MI N i≤k< j
0 als i = j ( m i, k + m k + 1, j + r i – 1 r k r j )
als
j>i
(109)
De eerste term van (109) correspondeert met het minimale aantal operaties dat nodig is om de matrix M' = M i × M i + 1 × ... × M k te evalueren. De tweede term correspondeert met de minimale kosten voor M'' = M k + 1 × M k + 2 × ... × M j , terwijl de derde term correspondeert met het vermenigvuldigen van M' × M'' . Merk op dat M' een r i – 1 × r k matrix is en dat M'' een r k × r j matrix is. Bij dynamisch programmeren wordt de kostenfunctie m ij berekend in oplopend verschil van i en j. Eerst wordt m i, i voor alle i’s geëvalueerd, dan m i, i + 1 , vervolgens alle m i, i + 2 enzovoorts. Op die manier kunnen de termen m i, k en m k + 1, j uit (109) berekend worden. Dit leidt tot de volgende evaluatievolgorde: m 11 = 0 m 22 = 0 m 33 = 0 m 44 = 0 m 12 = 10000
m 23 = 1000
m 1 ( 23 ) = 1200
m 34 = 5000
m ( 23 )4 = 3000
m ( 1 ( 23 ) )4 = 2200 De eis van het monotoon niet-dalend zijn van de kostenfunctie is duidelijk: hierdoor is men ervan verzekerd dat de kostenfunctie bij elke volgende slag in elk geval niet afneemt. Een implementatie van deze algoritme wordt aan de lezer over gelaten. Bedenk dus dat de kracht van het dynamisch programmeren ligt in die combinaties die niet geëvalueerd worden!
5.7
Lineaire stelsels
Een groot aantal probleem in de fysica zijn terug te voeren tot het oplossen van een lineair stelsel, meestal aangeduid als Ax=b. Afhankelijk van de omgeving die men gebruikt is de oplossingsmethode die gebruikt wordt al dan niet zichtbaar. Mathematica verbergt de details van de methode voor de gebruiker, in programmeeromgevingen als Fortran en C moet een expliciete keuze gemaakt worden over de methode. Een groot aantal teksten en codes is voorhanden voor de oplossing van lineaire stelsels. In een aantal gevallen is de structuur van de omringende code zodanig dat de gebruiker slechts de code aanlevert voor het matrix vector produkt y = A ⋅ x . Op die manier kan optimaal gebruik gemaakt worden van band- of sparse eigenschap-
Inleiding Natuurkundige Informatica
124
1997
Algoritmen
Lineaire stelsels
pen van de matrix. De beschikbare methodes kunnen onderverdeeld worden in directe en iteratieve methodes. Directe methodes resulteren in een eindig aantal operaties in een ’exacte’ oplossing van een stelsel. Deze oplosmethode kan het beste toegepast worden op ’full’ of band matrices. In geval van sparse matrices zijn iteratieve methodes te prevaleren, immers zij vereisen geen complete opslag van de matrix. Binnen het kader van dit college dictaat zullen wij ons beperken tot de Gauss eliminatie.
5.7.1
Gauss eliminatie
Beschouw het systeem Ax=b. De meest eenvoudige methode is om de matrix A te reduceren tot een driehoeksmatrix waarna oplossing van het systeem eenvoudig is. De eerste stap wordt meestal aangegeven door ’forward reduction’ de tweede door ’back substitution’. In pseudo-code is de procedure eenvoudig op te schrijven. In n-1 opeenvolgende slagen worden de kop termen van de matrix A gereduceerd, aannemende for k=1, ..., n-1 for i=k+1, ..., n
for k=n, n-1, ..., 1
xk = bk
l ik = a ik ⁄ a kk
for i=k+1, ..., n
x k = x k – a ki x i x k = x k ⁄ a kk
for j = k+1, ..., n
a ij = a ij – l ik a kj b i = b i – l ik b k Forward reduction Figuur 128
Back substitution
Principeschema voor de Gauss eliminatie
dat de diagonaal elementen a kk ongelijk aan nul zijn. Dit leidt tot het stelsel a 11 a 12 ... (1)
a 22 ...
a 1n
x1
a 2n
x2 =
(n – 1) a nn
b1 (1)
b2
(110)
...
... xn
(n – 1)
bn
De bovenindex van de a en b coefficiënten geeft aan na hoeveel ’forward reduction’ slagen zij bereikt zijn. Als het systeem eenmaal in deze vorm gebracht is, is de uiteindelijke oplossing eenvoudig te bereken, zij wordt dan gegeven door (n – 1)
(n – 2)
(n – 2)
bn b n – 1 – a n – 1, n x n b 1 – a 12 x 2 – .... – a 1n x n x n = ----------------- , x n – 1 = ---------------------------------------------- , ..., x 1 = ---------------------------------------------------------(n – 2) (n – 1) a 11 a n – 1, n – 1 a nn
(111)
Uit de pseudocode van Figuur 128 kan men eenvoudig de orde van het algoritme bepalen. In de i en j loop zitten (n-k) optellingen en vermenigvuldigingen, in het totaal dus n–1
∑ (n – k)
n–1 2
k=1
Inleiding Natuurkundige Informatica
=
∑k
2
(112)
k=1
125
1997
Algoritmen
Lineaire stelsels
∑
n
2
3
Gebruik makend van de relatie i = n ( n + 1 ) ( 2n + 1 ) ⁄ 6 betekent dit dat het algoritme O(n ) is i=1 wat dit soort operaties betreft. De delingen zijn van een lagere orde. Het is eenvoudig in te zien dat voor een 2 band matrix met bandbreedte β de orde reduceert tot nβ Het nadeel van de directe methode is dat volledige opslag van de matrix vereist is, sparseness kan dus niet uitgebuit worden. Het voordeel is dat binnen een voorspelbare tijd het antwoord verkregen wordt. Iteratieve methoden Iteratieve methoden zijn interessant om een tweetal overwegingen - de eventuele sparseness van een matrix kan uitgebuit worden, en - vele iteratieve methodes kunnen eenvoudig geparallelliseerd, en dus versneld worden. De term ’iteratief’ refereert naar algoritmes waarin successievelijke benaderingen gebruikt worden om te komen tot een verfijning van een oplossing. Grafisch is dit proces weergegeven in figuur 129 op pagina 126.
x2 start punt
x1 Figuur 129
Illustratie van het iteratieve convergentie proces van een stelsel lineaire vergelijkingen.
Hierbij is uitgegaan van een stelsel van twee vergelijkingen met twee onbekenden. Het snijpunt van de twee lijnen (de vergelijkingen) vormt het oplossingspunt. Vanaf het startpunt wordt er geconvergeerd naar de oplossing. Iteratieve methoden bereiken dus de oplossing altijd binnen een zekere tolerantie. Uit deze grafisch representatie is ook eenvoudig in te zien waarom een afhankelijk stelsel (twee parallelle lijnen) niet convergeert. Iteratieve algoritmen verdeelt men onder in stationaire en niet-stationaire. Stationaire methoden zijn ouder, eenvoudiger te begrijpen en implementeren, maar meestal minder efficiënt. Niet-stationaire methoden zijn relatief nieuw. De achterliggende wiskunde is meestal moeilijker maar vaak convergeren zij sneller. In het kader van deze behandeling zullen wij ons beperken tot een drietal iteratieve methoden: Jacobi, Gauss-Seidel en Successive overrelaxation (SOR). Mathematisch kunnen deze iteratieve methoden uitgedrukt worden in een eenvoudige vorm: x
k+1
k
= Bx + c
(113)
waarbij nog B nog c afhankelijk zijn van k.
5.7.2
Jacobi methode
De Jacobi methode kan eenvoudig afgeleid worden. Beschouw het stelsel Ax=b. De ide vergelijking van het stelsel wordt gegeven door: n
∑ aij x j
= bi
(114)
j=1
Inleiding Natuurkundige Informatica
126
1997
Algoritmen
Lineaire stelsels
Als we een (nieuwe) oplossing bepalen voor xi terwijl we alle andere waarden van x constant houden, dan levert dit xi = bi –
∑ aij x j ⁄ aii
(115)
j≠i
Dit suggereert een iteratieve methode gedefinieerd als (k)
xi
= bi –
( k – 1 )
∑ aij x j
⁄ a ii
j≠i
(116)
die de Jacobi methode genoemd wordt. Merk op dat de volgorde waarin de vergelijkingen afgewerkt worden niet van belang is. Daarom heet de Jacobi methode ook wel de methode van de simultane verplaatsing aangezien alle veranderingen in principe parallel gedaan kunnen worden. In matrix notatie wordt (116) gegeven door x
(k)
–1
= D ( L + U )x
(k – 1)
–1
(117)
+D b
waarin D, L en U respectievelijk de diagonaal, de onder- en boven driehoek van A voorstellen. Een kleine uitbreiding van de Jacobi methode levert de Gauss-Seidel methode: het substitueren van de oplossing zodra deze berekend is (k)
xi
= bi –
(k)
∑ aij x j
j
–
( k – 1 )
∑ aij x j
j>i
⁄ a ii
(118)
Dit moet de convergentie snelheid van de methode ten goede komen. Er wordt echter een prijs voor betaald: de methode is nu sequentieel geworden. Dat betekent dat parallellisatie van het stelsel nu moeilijker uitgevoerd kan worden. Uitzonderlijke omstandigheden, zoals grote sparseheid van de matrix, kunnen de afhankelijkheid weer opheffen. Ook deze methode kan in matrix vorm geschreven worden: x
(k)
–1
= (D – L) (U x
(k – 1)
+ b)
(119)
De ’successive overrelaxation methode’ meestal afgekort tot SOR, is gebaseerd op extrapolatie van de Gauss-Seidel methode. De extrapolatie is een gewogen gemiddelde van de vorige iteratie en de berekende Gauss-Seidel iteratie, d.w.z. (k)
xi
(k)
= ωx i
(k – 1)
+ ( 1 – ω )x i
(120)
waarin ω de extrapolatie factor aangeeft en x de Gauss-Seidel iteratie. Het uitgangspunt is, duidelijk, om de convergentie snelheid te verhogen. De keuze van ω is bepalend voor het succes van de methode. Het is bewijsbaar dat de SOR methode divergeert voor ω buiten het interval (0, 2). Voor de speciale waarde van 1 is de SOR gelijk aan de Gauss-Seidel methode. In het algemeen is het onmogelijk om een optimale waarde van ω van te voren te berekenen. Als A symmetrisch en positief definiet is dan is men verzekerd van convergentie voor elke waarde van ω hoewel zelfs dan de snelheid dramatisch kan verschillen.
5.7.3
Numerieke stabiliteit
Een belangrijk punt bij numerieke processen is het onderzoeken van de stabiliteit. Voor lineaire systemen van het type Ax=b is dat uitputtend gedaan. De oplossing van
Inleiding Natuurkundige Informatica
127
1997
Algoritmen
Lineaire stelsels
Ax = b
(121)
kan formeel geschreven worden als –1
(122)
x = A b
Als men nu een kleine verstoring aan brengt in de b vector ter grootte δb , en aanneemt dat het effect daarvan op de oplossing δx is, dan geldt A ( x + δx ) = b + δb
(123)
zodat geldt –1
x + δx = A ( b + δb )
(124)
Uit vergelijking (124) is duidelijk dat geldt –1
δx = A δb
(125)
De grootte van δx kan afgeschat worden met behulp van matrix en vector normen. In het algemeen wordt de p-norm van een vector gedefinieerd als n
p x p = xi i = 1
∑
1⁄p
(126)
De drie meest gebruikelijke waarden van p zijn p=1, 2 en ∞ . Hierop gebaseerd wordt de matrix norm gedefinieerd als A =
max ( Ax ) x = 1
(127)
Derhalve kan men ook drie matrix normen definiëren m
∑
a ij ) A 1 = max( j i=1 T
A 2 = ( λ max [ A A ] )
1⁄2
(128)
m
∑
a ij ) A ∞ = max( i j=1 In woorden kan men de normen ook omschrijven als -
1-norm is de maximale absolute kolom som, T 2-norm is de wortel van de grootste eigenwaarde van de matrix A A , ∞ -norm is de maximum absolute rij som.
Met behulp van de driehoeksongelijkheid volgt uit (125):
Inleiding Natuurkundige Informatica
128
1997
Algoritmen
Lineaire stelsels
δx ≤ A
–1
δb
(129)
Teneinde de relatieve grootte vast te kunnen stellen merken we op dat geldt b ≤ A x
(130)
Combinatie van vergelijking (129) en vergelijking (130) levert – 1 δb δx ---------- ≤ A A ----------x b
(131)
Als de matrix in vergelijking (121) veranderd wordt ter grootte δA resulteert eenzelfde overweging in ( A + δA ) ( x + δx ) = b . Dit kan herschreven worden als –1
δx = – A δA ( x + δx )
(132)
– 1 δA δx ------------------- ≤ A A ----------A x + δx
(133)
waaruit volgt dat
–1
In vergelijking (131) en vergelijking (130) is de variatie begrensd door de grootheid A A die vaak aangeduid wordt als het conditie getal van A en iets zegt over de gevoeligheid van een oplossing voor kleine veranderingen. De meeste, goede, mathematische bibliotheken zullen het conditie getal van een stelsel vergelijkingen bepalen en waarschuwen voor instabiliteiten. Dat deze redelijk dramatisch kunnen zijn is eenvoudig te illustreren aan een voorbeeld. Beschouw de volgende matrices
b = 0.127 0.112
A = 0.550 0.423 0.484 0.372 De exacte oplossing van het stelsel wordt gegeven door 1 –1
x =
Introduceren we een kleine verstoringsvector δb te grootte van δb = 0.00007 0.00028
zodat
b + δb = 0.12707 0.11228
dan wordt de nieuwe oplossing x + δx =
1.7 – 1.91
met
δx =
0.7 – 0.91
In dit geval is de relatieve verandering van δb ⁄ b = 0.0022 duidelijk veel kleiner van ( δx ) ⁄ x = 0.91 . Het conditie getal van de matrix A moet dus slecht, dat wil zeggen groot, zijn.
Inleiding Natuurkundige Informatica
129
1997
Algoritmen
Lineaire stelsels
De inverse van de matrix A wordt gegeven door A = – 2818 3205 3667 – 4167 Past men hier de ∞ -norm op toe dan vindt men voor A = 0.973 en voor A getal van 7622 (!) oplevert.
–1
= 7834 wat een conditie
Men kan de instabiliteit van dit stelsel ook op een andere manier illustreren. De oplossing van een lineair stelsel kan volgens de regel van Kramer geschreven worden als het quotiënt van de determinanten van twee matrices, waarbij de noemer de determinant van A is. Deze is extreem klein (0.00013) wat er op duidt dat het stelsel bijna singulier is. Dat kunnen we ook anders zien, in dit eenvoudige geval, als we bedenken dat de determinant gegeven wordt als a 11 a 22 – a 12 a 21 . Een kleine waarde van de determinant is in dit geval identiek met een grote cancellation error. In een systeem met maar drie significante decimalen zou het stelsel bij evaluatie als singulier te boek staan. Een laatste interpretatie van dit fenomeen kan op grafische manier gegeven worden. Beperken wij ons tot een stelsel van twee vergelijkingen met twee onbekenden dan kunnen die geïnterpreteerd worden als lijnen in een twee dimensionaal vlak, waarvan het snijpunt de oplossing geeft zoals aangegeven in figuur 129 op pagina 126. De determinant die ongeveer gelijk aan nul is kan dan ook geschreven worden als a 11 ⁄ a 12 ≈ a 12 ⁄ a 22 wat betekent dat de (twee) lijnen bijna parallel lopen. Een geringe verschuiving van de richting impliceert een (forse) verschuiving van het snijpunt en dus van de oplossing.
5.7.4
Toepassing: Weerstandsnetwerk
Als toepassing van een lineair stelsel zullen wij een eenvoudig probleem beschouwen van een weerstandsnetwerk zoals aangegeven in Figuur 130. Aannemende dat alle weerstanden passief en Ohms zijn, en dat er een externe spanning V aangelegd wordt over het netwerk bestaat er een unieke oplossingen van dit probleem in de vorm van de spanning op de nodes A tot en met E en de stromen in de deelkringen. We zullen de conventie gebruiken dat rechtsomdraaiende stromen als positief genomen worden. De nummering van de stromen is aangegeven in Figuur 130. A
j2
j1 V
C
B
j3
D
j4
Figuur 130
Eenvoudig weerstandsnetwerk.
E
Een oplossing kan worden gevonden door herhaalde toepassing van de wetten van Kirchhoff op de verschillende spanningskringen:
Inleiding Natuurkundige Informatica
130
1997
Algoritmen
Lineaire stelsels
ABE ADCB
v AB + v BE + v EA = 0 v AD – v CD – v BC – v AB = 0
BCE
v BC + v CE – v BE = 0
CDE
v CD + v DE – v CE = 0
(134)
Voor de stroomkringen kunnen soortgelijke vergelijkingen opgeschreven worden: Node A
i AB + i AD – i EA = 0
Node B
– i AB + i BC + i BE = 0
Node C
– i BC + i CD + i CE = 0
Node D
– i AD – i CD + i DE = 0
(135)
Met dit stelsel vergelijkingen is de oplossing uniek bepaald. We moeten daarbij bedenken dat de spanning tot op een constante na bepaald kan worden. Bedenken wij dat de relatie geldt V EA = R EA i EA – V . Aannemende dat men geïnteresseerd is in de spanningen op de nodes worden de vergelijkingen van vergelijking (135) gebruikt met twee tussenstappen -
de spanning V PQ wordt geschreven als i PQ R PQ waarbij i de stroom door de weerstand R is. Voorts worden alle stromen in de kring uitgedrukt in de (vier) elementaire kringstromen. Bijvoorbeeld I BE = j 1 – j 3 , I AB = j 1 – j 2 , en i AD = j 2 .
Invullen in vergelijking (134) levert dan het stelsel R AB + R BE + R EA
– R AB
– R BE
0
j1
– R AB
R AB + R AD + R BC + R CD
– R BC
– R CE
j2
– R BE
– R BC
R BC + R BE + R CE
– R CD
0
– R CD
– R CE
R CD + R CE + R DE
V = 0 0 j3 0 j4 (136)
Het is eenvoudig in te zien dat men op een soortgelijke manier uit vergelijking (135) de corresponderende vergelijkingen voor de spanning kan afleiden waarbij wij de spanning op E als nulpunt gekozen hebben. G AB + G AD + G EA
– G AB
0
– G AD
eA
– G AB
G AB + G BC + G BE
– G BC
0
eB
0
– G BC
G BC + G CD + G CE
– G CD
eC
– G AD
0
– G CD
G AD + G CD + G DE e D
G EA V =
0 0 0 (137)
waarbij G ij = 1 ⁄ R ij de geleidbaarheid voorstelt. Het verkrijgen van de oplossing voor het weerstandsnetwerk is daarmee teruggebracht tot het oplossen van een stelsel lineaire vergelijkingen wat in principe kan met de methode uitgelegd in de vorige paragraphen. De structuur van de matrix uit vergelijking (137) is eenvoudig te generaliseren. Het gaat hier om een zoge-
Inleiding Natuurkundige Informatica
131
1997
Algoritmen
Conclusie
naamde connectiviteitsmatrix waarbij het element ij aangeeft of er een directe verbinding (lees weerstand) tussen plek ij is, terwijl de hoofddiagonaal de som bevat van alle elementen die daar uit komen. Voor de evaluatie van deze systemen moet men het volgende bedenken: -
-
De matrix is (per definitie) symmetrisch, want de verbindingen zijn symmetrisch. Het is dus niet nodig om alle elementen op te slaan, met slechts de helft kan volstaan worden. Voor grote stelsels is deze bezuiniging niet voldoende. Er is dan ook meer te halen want voor regelmatige structuren zal de matrix een overeenkomstige structuur gaan vertonen waardoor band of sparse opslag technieken uitkomst kunnen brengen. De numerieke stabiliteit van de oplossing hangt (mede) af van het verschil in grootte orde tussen de weerstanden in het netwerk. Beschouw voor vergelijking (137) het geval dat de weerstand tussen AB erg klein is, dat wil zeggendat GAB erg groot is. In dat geval zal de matrix reduceren tot G AB – G AB 0 0 – G AB G AB 0 0 ... ...
... ...
... ... ... ...
wat zal leiden tot ontaarding van het probleem.
5.8
Conclusie
Algoritmen proberen op een verantwoorde manier de voorkennis van de gebruiker over zijn probleem in rekening te brengen. Hoewel de vooruitgang op het gebied van de hardware spectaculair kan zijn, kan zij gemakkelijk teniet gedaan worden door j een verkeerde algoritmische inzet. De technieken die men kan gebruiken worden overigens in sterke mate bepaald door de omgeving die gehanteerd wordt.
5.9
Appendix: Performance meting
Een belangrijke eigenschap van informatieverwerkende systemen is hun prestatie (performance). Het belang van het meten van de prestatie van een informatieverwerkend systeem is de mogelijkheid om de verwerkingstijd van problemen te kunnen voorspellen, d.w.z. te kunnen aangeven of problemen op een bepaald systeem oplosbaar zijn. De meest eenvoudige oplossing van het ’gewoon proberen’ is vermoedelijk het meest betrouwbaar maar niet altijd eenvoudig realiseerbaar. Er is moeilijk een één-éénduidige maat te definiëren voor de prestatie (P), meestal wordt de tijd nodig voor het verwerken en/of berekenen van een wel gedefinieerde hoeveelheid informatie gebruikt, i.e. [ Operaties ] P = --------------------------tijd
(138)
Om deze maat te kunnen gebruiken is het nodig om zowel de tijd als de te verwerken informatie nader te definiëren.
5.9.1
Tijd
De tijd kan gedefinieerd worden op een tweetal manieren: vanuit de gebruiker gezien en vanuit het proces dat de informatie verwerkt. In het eerste geval spreekt men altijd van ’wall-clock’ tijd in het tweede van ’process’ tijd. De definitie van het eerste is, zoals de naam al suggereert, de tijd die verstrijkt volgens de ’gewone klok’ tussen de start en stop van het proces. Wat er (nog meer) gebeurt tussen de start en stop is niet van belang: of het proces maar een fractie van de tijd actief is en voor de rest wacht op een bepaalde gebeurtenis, of dat
Inleiding Natuurkundige Informatica
132
1997
Algoritmen
Appendix: Performance meting
het proces uit een aantal deelprocessen bestaat die allemaal parallel uitgevoerd worden maakt niets uit. Merkwaardig genoeg is dit de meest hanteerbare definitie van tijd. Bij de ’process-time’ wordt de wall-clock tijd gecorrigeerd voor fractie van de systeem resources, meestal CPU, die het proces toegewezen gekregen heeft. Als voorbeeld kan men denken aan een systeem waarop twee (identieke) processen draaien5. Beide processen zullen in doorsnee de helft van de CPU toegewezen krijgen: de wall-clock tijd zal verdubbelen de user-tijd zal echter constant blijven, i.e. t proces = [ CPU ]t wall
(139)
waarbij [CPU] de fractie toegewezen CPU resource is. Het zal duidelijk zijn dat eq. (139) ook gebruikt kan wordt om de CPU fractie te bepalen. De proces tijd wordt onderverdeeld in de user- en de systemtijd. De eerstgenoemde betreft alle tijd die geconsumeerd wordt door activiteiten die direct door de gebruiker geïnitieerd worden (en geprogrammeerd zijn). Het tegenovergestelde is waar voor de systemtijd. Een belangrijke component bij de systemtijd vormt de I/O tijd. Als voorbeeld kan men denken aan de disk I/O die geïnitieerd wordt door een proces als een groot file weggeschreven wordt. Dus er geldt t proces = t user + t system
(140)
Voor zogenaamde CPU-bound processen is t user dominant ten opzichte van t system . Verricht men eenzelfde hoeveelheid operaties op twee verschillende systemen dan kan de zo gedefinieerde user-tijd gebruikt worden als relatieve maat voor de prestatie van een systeem ongeacht de verdere activiteiten (van andere gebruikers) op het systeem. De meeste PSE’s geven hulpmiddelen om de tijd te meten. In het navolgende zullen wij ons op met name C (Unix) en Mathematica concentreren.
5.9.2
C/Unix
Bij Unix geeft het commando time, met als syntax time [command] de mogelijkheid tot het bepalen van user, systemtijd en wall-clock tijd. Command mag een willekeurig complex(e verzameling van) proces(sen) zijn. Time rapporteert zijn uitvoer op de standaard error! Een typisch voorbeeld is % time wc /usr/share/man/man1/csh.1 1876 11223 65895 /usr/share/man/man1/csh.1 2.7u 0.9s 0:03 91% 3+5k 19+2io 1pf+0w Het commando wc voert de ’word count’ uit van een file (in dit geval csh.1). Het resultaat staat op de volgende regel en is de gewone uitvoer. De regel daarna is de uitvoer van time. De eerste drie getallen vormen de user-, system- en de wall-clocktijd terwijl het vierde getal de CPU fractie aangeeft. De overige informatie laten wij in het kader van dit college buiten beschouwing. Wordt time zonder meer gegeven dan rapporteert hij de statistiek van de shell waarin het command gedraaid wordt. Opgemerkt dient te worden dat officieel de usertijd gemeten wordt in 50-sten van secondes maar de reproduceerbaarheid kan van systeem tot systeem verschillen. Een programmatische tegenhanger van het command time wordt door de timing (C) functie time(3V) gegeven6 die de tijd retourneert, gemeten in seconden, verstreken sinds 1 januari 1970. Bedenk dat het hier dus 5.
Meestal zal er ook een operatingsysteem draaien maar eenvoudigheidshalve laten wij dit hier achterwege.
Inleiding Natuurkundige Informatica
133
1997
Algoritmen
Appendix: Performance meting
gaat om wall-clock tijd. Een voorbeeld van een programma dat gebruik maakt van dit commando wordt gegeven in Figuur 131. Hier wordt in principe een verschil meting gedaan (T_START en T_STOP). Voordeel #include #include #include
<math.h> <sys/types.h> <sys/time.h>
main() { int T_START, T_STOP; T_START = time(NULL); ..... do anything ..... T_STOP = time(NULL); printf("%d\n", T_STOP-T_START); } Figuur 131
C programma dat met behulp van de Unix utility time de tijd meet die het draait. Het gaat hier om elapsed tijd
van deze methode is dat hij ook eenvoudig toegepast kan worden op delen van een programma zodat gedetailleerdere informatie verkregen kan worden. Een duidelijk nadeel is dat de gebruiker zelf alle tijdsinformatie die hij wil meten van te voren in het programma dient vast te leggen. Met behulp van getrusage7 kunnen ook de andere genoemde tijden gemeten worden. De functie getrusage retourneert onder andere de user en system tijd in het timeval formaat dat gedefinieerd is in Figuur 132. Een struct timeval { long long }; Figuur 132
tv_sec; /* seconds */ tv_usec; /* micro seconds */
Structure die in Unix gedefinieerd wordt voor allerlei soorten timingen.
dergelijke structuur is zowel voor de usertijd (ru_utime) als voor de systemtijd (ru_stime) aanwezig in de struct rusage die door getrusage geretourneerd wordt. Een klein voorbeeld programma zou er dan uitzien als in Figuur 133. Het systeem houdt hier zelf de verschilmetingen bij. Merk op dat deze meting ook in principe nauwkeurig is omdat ook een microseconden gedeelte opgegeven wordt. Op deze manier kan in principe per statement gemeten worden, wederom met het bezwaar dat alles door de gebruiker geprogrammeerd moet worden. Een variant op de hierboven beschreven methode wordt gevormd door het zogenaamde profilen waarbij de procedure(functie) als logische eenheid genomen wordt. Bij profiling wordt de code nodig voor het timen door de compiler, transparant voor de gebruiker, toegevoegd. Alleen een aparte compilatie slag is noodzakelijk. Voor de interpretatie van de profile is het nodig om enigszins te weten hoe deze tot stand komt. De code die tijdens compilatie toegevoegd wordt (vlag -pg) zorgt ervoor dat tijdens executie van het programma op equidistante tijden ’gekeken’ wordt waar het programma zich in de executie bevindt. Deze informatie wordt weggeschreven in een file (mon.out). Met behulp van de utility prof kan in dit file gekeken worden. In Figuur 134 staat een voorbeeld van een dergelijke output. Ook hier dient de gebruiker op zijn hoede te 6. 7.
Een exacte beschrijving van de functie is te krijgen met man 3 time. Een exacte beschrijving van de functie is te krijgen met man getrusage (SUN specifiek)
Inleiding Natuurkundige Informatica
134
1997
Algoritmen
Appendix: Performance meting
#include #include #include
<math.h> <sys/time.h> <sys/resource.h>
main() { struct
rusage R_USE;
... do anything ... if(getrusage(RUSAGE_SELF, &R_USE)) fprintf(stderr, "timing failed\n"); else { printf("%ld %ld\n", R_USE.ru_utime.tv_sec, R_USE.ru_utime.tv_usec); } } Figuur 133
C programma dat de usertijd meet via de Unix call getrusage
#include <math.h> main() { float int i;
x, y = 123456;
for(i=0; i<8000000; i++) { x = sqrt((double)y); y = x; } }
granularity: each sample hit covers 2 byte(s) for 0.03% of 31.53 seconds % cumulative self self total time seconds seconds calls ms/call ms/call name 49.0 15.46 15.46 mcount (65) 25.1 23.36 7.90 insqrt [1] 14.7 28.01 4.65 8000000 0.00 0.00 _sqrt [4] 8.2 30.61 2.60 1 2600.09 7260.24 _main [3] 1.1 30.96 0.35 odd [5] Figuur 134
Voorbeeld van een zogenaamde profiling van een programma. Hierbij wordt op functie niveau de prestatie van een programma gemeten.
zijn: het totale aantal samples moet groot zijn om tot betrouwbare tijden te komen en optimalisaties van de compiler maken de profiling soms moeilijk interpreteerbaar. In elk geval zal men altijd de procedure mcount aantreffen die de profiler zelf voorstelt.
Inleiding Natuurkundige Informatica
135
1997
Algoritmen
5.9.3
Appendix: Performance meting
Mathematica
Voor een omgeving als Mathematica gelden alle hierboven gedefinieerde begrippen. Als men een Mathematica programma op file heeft staan kan eenvoudig met het time commando de totale tijd bepaald worden, dus time math [file] zal timen hoelang het executeren van de math sessie duurt. Bedenk dat hier gebruik gemaakt wordt van het feit dat Mathematica onder Unix een ’gewoon’ proces is. Voor de meer gedetailleerde timing kan de Mathematica instrinsic Timing gebruikt wordt. De werking hiervan is vrijwel vergelijkbaar met het Unix time commando. Beschouw als voorbeeld het volgende: In[1] := Timing[1000!;]8 Out[1]= {1.8333 Second, Null} In[2] := Timing[1000!;] Out[2] = {0., Null} De twee metingen van dezelfde berekening leveren niet dezelfde antwoorden op! De verklaring hiervoor is dat bij de eerste berekening een aantal zaken geïnitieerd moeten worden die bij de tweede berekening gebruikt worden. Een zelfde verschijnsel zal men, zij het in veel mindere mate, ook bij C-programma’s kunnen aantreffen.
5.9.4
Operaties
De gegeven voorbeelden maken duidelijk dat een scherpe definitie van het begrip operatie moeilijk is en afhangt van de omgeving die gebruikt wordt. Het meest zinvol is om een totale berekening als eenheid te nemen. Historisch gezien waren numerieke toepassingen van informatie verwerkende systemen erg belangrijk vandaar dat rekenkundige operaties nog alsmaar gebruikt worden. Men onderscheidt floating point (flop) en integer operaties (ip). De hierop gebaseerde maten worden dan ook flop/s en ip/s. Wat men precies een operatie noemt is echter niet scherp te definiëren. Denk aan het volgende voorbeeld: a=b+c Voor de gebruiker staat hier één assignatie en één optelling maar het is de vraag of dat in de uiteindelijke code die uitgevoerd wordt ook zo is. Het volgende voorbeeld is al complexer: a[i] = b[i] + c Nu zijn er nog twee integer operaties nodig om de array-elementen te adresseren. Het zal duidelijk zijn dat voor realistische programma’s van meer dan 5 regels het ondoenlijk is om accuraat het aantal operaties te bepalen. Vandaar dat men voor de flop en ip maten meer theoretisch aan de hand van de architectuur vaststelt: als peak performance definieert men P = # units / cycle tijd
(141)
De cycle tijd is de intrinsieke maat van de processor nodig voor het uitvoeren van één operatie. Hedendaagse processoren, zoals de IBM RIOS, hebben een aantal functionele units die gelijktijdig kunnen werken vandaar dat het produkt genomen wordt9. De naam peak performance wordt gerechtvaardigd doordat de zo berekende performance nooit overschreden (gehaald ?) kan worden. Afgeleid van deze maten zijn twee andere definities van operaties: Drystone en Whetstone. Beiden proberen het ’gemiddelde’ gedrag van programma’s te simuleren. De drystone benchmark is gebaseerd op een ana8. 9.
De ’;’ in de input geeft aan dat de output van de berekening onderdrukt moet worden, vandaar de Null in de output. Voor gepipelinede processoren wordt ook nog het aantal stages van de pipeline meegenomen.
Inleiding Natuurkundige Informatica
136
1997
Algoritmen
Appendix: Performance meting
lyse van C programma’s en bevat 53% assignments, 32% control statements en 15% procedure en function calls. De whetstone benchmark voert een aantal specifieke operaties uit zoals eenvoudige arithmetische operaties op een iets hoger niveau. Dongarra van het Oak Ridge National laboratory heeft een aantal standaard manieren ontwikkeld om de prestatie van systemen te meten en wordt ook gezien als de expert op dit gebied10. Door hem is een standaard ontwikkeld, de Linpack benchmark, die gebaseerd is op het oplossen van een 3 2 linear stelsel Ax = b. Voor een stelsel van n vergelijkingen zijn hiervoor ( 2 ⁄ 3 )n + 2n operaties nodig, zodat de grootte van het stelsel gebruikt kan worden om het aantal operaties te variëren. Wel dient opgemerkt te worden dat de benchmark beperkt is aangezien hij maar twee essentiële routines gebruikt: één om de LU decompositie van de matrix te berekenen, de ander om de oplossing vast te stellen. Dit soort benchmarks is alleen in abstracte zin toepasbaar op PSE’s als Mathematica aangezien de Dongarra benchmark in Fortran staat.
5.9.5
Benchmark maten
Het officieel karakteriseren van de snelheid van systemen is zowel uit marketing als technisch oogpunt onvermijdelijk. De benchmark standaards dienen om ongelijksoortige machines op een zo objectieve manier met elkaar te vergelijken. Op zich een hopeloze aangelegenheid. Dan nog hebben de maten zich zo’n vaste plaats verworven dat het onmogelijk is om ze niet tegen te komen in karakterisaties van machines. Wij noemen hier een aantal veel gebruikte maten in een zeker niet uitputtend overzicht.
5.9.5.1
Integer Benchmarks
De MIP (Mega Instructie per Seconde) wordt vanoudsher gedefinieerd aan de hand van de prestatie van de (al lang niet meer bestaande) DEC VAX 11/780. In principe zou de MIP moeten beteken het uitvoeren van 1 miljoen instructies per seconde. Helaas is de standaard zelf al verward omdat de 11/780 zelf niet 1 MIPS is maar ongeveer de helft ! Dit bleek uit een publikatie van DEC waarbij zij stelden dat voor een gemiddelde instructie mix de 11/780 ongeveer 470000 instructies per seconde haalde. De drystone benchmark gaat een stap verder in de zin dat een groot aantal standaard programma’s geanalyseerd zijn op hun instructie mix (hoeveel add, mul, functie calls e.d.). Op grond van de percentages die men daarvoor gevonden had, is een synthetische benchmark geschreven die het gemiddelde gebruikers prorgamma moet voorstellen. De originele versie stond in Ada en is later vertaald naar C. Per definitie is 1 MIP gelijk aak 1757 Drystones. Er kleven (vele) beperkingen aan de drystone benchmark. De voornaamste is dat de integer bewerkingen dominant zijn en dat de meeste bewerkingen in de kleine loop gedaan worden die zich goed lenen om ’weggestopt’ te worden in caches of registers. Daarom kan de prestatie die zo voorspeld wordt een overschatting zijn van de werkelijke prestatie en zeker weinig voorspellende waarde hebben als het gaat om bewerkingen op niet gehele getallen.
5.9.5.2
Floating Point Benchmarks
Floating point benchmarks zijn vermoedelijk nog talrijker dan de integer benchmarks. De tegenhanger van de MIP is de MFlop (Mega Floating Points) operaties per seconde. De definitie van de Flop is dat het op hardware niveau één floating point operaties is. Let op, het gaat hier om hardware operaties. De vraag is direct hoe dat vertaalt naar een C statement als a=b+c De beroemdste benchmark op floating point gebied is de Linpack benchmark welke gebaseerd is op floating point operaties die nodig zijn voor het bewerken van een matrix. De 100x 100 Linpack benchmark was origineel geschreven als een test voor zowel floating point performance als een test van het geheugen systeem. 10.
De meest up-to-date lijst kan opgehaald worden door een mail te sturen naar [email protected] met als inhoud send performance from benchmark.
Inleiding Natuurkundige Informatica
137
1997
Algoritmen
Appendix: Performance meting
De benchmark haalt 100 x 100 sub matrices uit een grotere 200x200 of 201x201 matrix over allerlei mogelijke permutaties. De validiteit van test is echter afgenomen omdat de 320 kbyte benodigde geheugen ruimte in veel werkstations al in de cache gevonden kan worden. Werkstation leveranciers beperken zich meestal tot de 100 x 100 double precision benchmark. Het interessante van de Linpack benchmark is dat er een hoop ‘historische’ gegevens van bekend zijn. Naast de zogenaamde Linpack rating wordt om historisch redenen ook de Whetstone veel gebruikt. Ook hier gaat het, net zoals bij de Drystones om een synthetische benchmark die het gemiddelde gedrag van een programma moet modelleren. Aan het einde van de 80’er jaren is er een groep gevormd, de ‘Systems Performance Evaluation Cooperation’ (SPEC) die zich erop toegelegd heeft om relevante testen te ontwerpen en onderhouden voor (nieuwe) generaties van (super)computers. Er zijn nu een tweetal test pakketten door hen ontwikkeld die onderscheiden worden door het jaar waarin zij geproduceerd zijn, respectievelijk 1989 en 1992, dus SPEC89 en SPEC92. De SPEC92 bestaat uit 20 CPU benchmarks, de SPEC89 uit 10 codes. Hieronder vallen 6 integer en 14 floating point codes. De rating in de SPEC benchmark wordt berekend door het geometrische gemiddelde te nemen van de inverse executie tijden van de diverse onderdelen, die gecombineerd worden tot de zogenaamde SPECint en SPECfp rating. De SPEC92 bestaat alleen voor double precision. Hele studies zijn verschenen over het fenomeen van de benchmark vandaar dat enige relativerende opmerkingen op zijn plaats zijn. In vrijwel alle gevallen is het benchmark cijfer bedoeld om een inschatting te kunnen maken van de geschiktheid van een machine voor het draaien van bepaalde programma’s. De beste test blijft om indien mogelijk het eigenlijke programma te draaien. Pas als dat niet mogelijk is (omdat het nog geschreven moet worden) is het noodzakelijk om nota te nemen van benchmark gegevens. De waarschuwing die daarbij geldt is dat er goed gelet moet worden op wat de benchmark nu eigenlijk bepaalde, en welke versies van compiler en operating systeem gebruikt werden. Als aan al die voorwaarden voldaan is kan een benchmark gebruikt worden om inzicht te krijgen in de prestaties van de machine. Daarnaast is het nodig om te weten wat de schaling is van het programma dat men gaat draaien. Dat is meestal terug te voeren tot de afhankelijkheid van een aantal fundamentele algoritmes. Enige daarvan zijn in dit hoofdstuk behandeld. RS6000/58H Drystone
SUN SS10/51
87719
43290
Linpack (single)
7.6 Mflop
1.9 MFlop
Linpack (double)
11.0 Mflop
2.5 MFlop
SPECint
97.6
52.6
SPECfp
203.9
64.7
tabel 20
Inleiding Natuurkundige Informatica
Overzicht van de prestaties van facultaire machines op de meest populaire benchmarks.
138
1997
Hoofdstuk 6:Architecturen
Architecturen
6.1
Inleiding en Overzicht
Inleiding en Overzicht
De snelle ontwikkelingen op het gebied van informatieverwerkende systemen zijn al een aantal malen benadrukt in dit dictaat: men dient grote voorzichtigheid te betrachten met het in te veel detail bestuderen van snel verouderende systeemeigenschappen. In dit hoofdstuk zullen wij proberen de gulden middenweg te bewandelen tussen detail en globaal overzicht. Traditioneel verdeelt men data verwerkende systemen in hardware en software. De wisselwerking tussen hardware en software is aan voortdurende verandering onderhevig. Drijvende kracht hierbij is de afweging tussen snelheid, prijs en flexibiliteit. ’Hardware’ oplossingen zijn meestal het snelst, duur en moeilijk te veranderen. Software is meestal trager, vermoedelijk goedkoper1 en in elk geval eenvoudiger te veranderen. Algoritmen en mogelijkheden die eerst verkend worden in software, worden in een laatste, produktie, fase geïmplementeerd in hardware. Een duidelijk voorbeeld hiervan zijn de zogenaamde ’multi media’ instructies in de nieuwe Ultra Sparc CPU van SUN die het mogelijk maken om real-time image processing te doen. Daarmee vergelijkbaar zijn de MMX (multi media extensies) instructies van Intel processoren. Als uitgangspunt van de hardware ontwikkelingen wordt meestal het baanbrekende werk van Von Neumann genomen, reden waarom men dan ook vaak spreekt over ’de Von Neumann architectuur’. In dergelijke architecturen is het gebruikelijk om over twee eenheden te spreken ’instructies’ en ’data’. Globaal gesproken definiëren de instructies de akties die uitgevoerd moeten worden op de data. Die constatering is het uitgangspunt van de classificatie van Flynn die in de 70er jaren opgesteld is en nog steeds gehanteerd wordt. In deze taxonomie onderscheidt men - voor de volledigheid - vier categorieën: 1) Single Instruction Single Data (SISD) Tot deze groep rekent men al die machines die doen aan zogenaamde seriële (Von Neumann) processing. Zij vormen als het ware het startpunt van de computerontwikkelingen. 2) Single Instruction Multiple Data (SIMD) Tot deze groep rekent men al die machines die in staat zijn om met 1 instructie een serie data te bewerken. Vaak rekent men de zogenaamde vectormachines tot deze groep. 3) Multiple Instruction Single Data (MISD) Weinig mensen kunnen zich wat voorstellen bij deze klasse, behalve misschien Flynn zelf, immers wat stellen veel bewerkingen op dezelfde data voor. 4) Multiple Instruction Multiple Data (MIMD) Multiple instructies onderstellen dat er meerdere processoren in dezelfde computer zitten die actief kunnen zijn. Vandaar dat men tot deze klasse al de systemen rekent die multiprocessor configuraties omvatten. De hier aangegeven taxonomie kan men ook op een andere manier in kaart brengen, zoals aangegeven in Figuur 135. Uit het startpunt van de scalaire processoren zijn drie (bijna) orthogonale wegen gekomen: super scalar, vector en parallel. De trends en ontwikkelingen in dit veld zullen wij in de volgende paragrafen in kaart brengen.
6.2
Scalaire architectuur
Als uitgangspunt van de systemen zullen wij de scalaire architectuur nemen, die ook wel aangeduid wordt als de Von Neumann of de SISD. Zoals in de inleiding aangegeven beperken wij ons in eerste instantie tot twee eenheden: instructies en data, daarbij aard en type van instructies en data in het midden latend. 1.
Overwegend dat één regel foutvrije code $10 kost, is het de vraag of deze bewering helemaal waar is.
Inleiding Natuurkundige Informatica
140
1997
Architecturen
Scalaire architectuur
parallel > 10 (hardware/gebruiker)
10 (hardware/compiler/gebruker)
scalair
vectorieel 2 (hardware/compiler)
super scalar
Figuur 135
Overzicht van trends in High Performance Computing. Getallen geven de relatieve prestatie aan t.o.v. scalair.
De Von Neumann architectuur is opgebouwd rond deze twee eenheden. Globaal onderscheidt men drie componenten: -
memory, waar de instructies en de data opgeslagen worden, CPU, waar de instructies op de data toegepast worden en I/O, verbinding tussen CPU en memory
Gebaseerd op deze indeling doorloopt de klassieke Von Neumann machine een cyclus die uit 5 stappen bestaat, bekend als de zogenaamde Von Neumann cyclus; 1) 2) 3) 4) 5)
fetch haal de volgende instructie op uit het geheugen decode bepaal wat gedaan moet worden operand fetch haal, indien nodig de data op waarop de instructie moet werken execute voer de instructie uit store schrijf de eventuele resultaten weg.
Drie van de vijf onderdelen van deze cyclus hebben te maken met het ophalen van gegevens uit het geheugen. Het is belangrijk om twee zaken te benadrukken. Ten eerste worden bij dit schema nog geen aannames gemaakt over de aard en type van de data/instructies. De praktijk met name in de begin jaren van de computer was echter anders: men ging uitsluitend uit van numerieke informatie, data waren getallen en instructies waren rekenkundige instructies. De tweede overweging die belangrijk is, is dat nergens in de boven aangegeven cyclus aangenomen wordt dat geheugen en CPU zich op dezelfde plaats bevinden, d.w.z. geïntegreerd zijn binnen één fysiek systeem. Dit opent de weg naar genetwerkte systemen. In eerste instantie kan men het doorlopen van de vijf genoemde stappen zien als het uitvoeren van een instructie. De tijd die hiervoor nodig is, is in principe variabel: 40% van de cyclus is optioneel (stap 3 en 5) terwijl binnen het rekenkundige domein het uitrekenen van een wortel duidelijk complexer zal zijn dan de absolute waarde nemen van een getal. Het zal duidelijk zijn dat deze intrinsieke chaos een computer moeilijk hanteerbaar zou maken. Daarom is de tijd in een computer gediscretiseerd. De minimale tijdstap die te
Inleiding Natuurkundige Informatica
141
1997
Architecturen
Scalaire architectuur
cache
CPU
Main Memory
DMA
Memory Bus I/O Bus
Disk Control logic
Registers IR
Arithmetic Logical Unit
PC
CPU Figuur 136
Principeschema van een elementair scalair computer systeem
maken is wordt aangeduid als de cycle tijd. In een eerste benadering zou men kunnen stellen dat in een dergelijke tijdbestek de eerder genoemde cyclus, ook voor de traagste instructie uitgevoerd zou moeten kunnen worden, of t cycle = t fetch + t decode + t operand + max(t xeq) + t store
(142)
Naast discretisatie van de tijd is ook discretisatie van de informatie aanwezig in een dergelijk systeem: er is een minimale hoeveelheid informatie die gebruikt wordt. Deze basishoeveelheid wordt altijd aangeduid als het woord. Hiermee kunnen wij een werkdefinitie geven van een ’scalair’ systeem, namelijk een systeem dat (ten hoogste) per clock cyclus één instructie kan verwerken. De Von Neumann machine kan gezien worden als de technische realisatie van een dergelijk systeem. De vragen die daarbij een rol spelen zijn de volgende: -
-
hoe zorgt men ervoor dat de verwerkingssnelheid van de CPU gelijke tred houdt met de mogelijk om gegevens uit het geheugen te halen (adresseren van het geheugen) en naar de CPU te brengen (I/O). Men onderscheidt hierbij drie mogelijke systemen (systeemtoestanden): CPU bound (CPU is snelheidsbepalend, karakteristiek van zwaar rekenwerk), memory bound (veel geheugen is nodig), I/O bound (karakteristiek van allerlei transaktie systemen). welke mogelijkheden er zijn voor optimalisatie/snelheidsverhoging van het systeem. welke soorten instructie/informatie kan men eenvoudig verwerken.
Inleiding Natuurkundige Informatica
142
1997
Architecturen
6.2.1
Scalaire architectuur
CPU
Het is niet mogelijk om de structuur van de CPU te bespreken zonder duidelijk te maken over welke instructies men het heeft. Ver doorgevoerd is dat weer gekoppeld aan het type applicatie dat men op het computersysteem draait. De leverancier zal hierover het liefst zo weinig mogelijk aannames maken omdat dat zijn machine optimaal inzetbaar maakt, de gebruiker daarentegen zal het liefst optimale prestaties willen hebben (voor minimaal geld). Het allerlaagste niveau van instructies noemt men de machinetaal. Daaronder verstaat men het collectief van instructies die direct uitvoerbaar zijn voor de CPU. De tijd nodig voor het uitvoeren kan één of meerdere cycli duren. Het aantal lagen dat zich bevindt tussen de machinetaal en de taal waarin de applicatie uiteindelijk geschreven is varieert. De strijd hierover is met name in de laatste twee decennia opgelaaid in de vorm van het debat over RISC (Reduced Instruction Set Computer) versus CISC (Complex Instruction Set Computer). Hoewel RISC met name op het ogenblik een bijna magische klank heeft ligt de oorsprong al bij de CDC 6600 (1964) en het IBM 801 project (1975).
6.2.1.1
CISC
Bij CISC kiest men voor complexe machinetaal instructies die (bijna) direct gebruikt kunnen worden voor het omzetten van applicatie programma’s. Complexe instructies zullen in principe een lange cycle tijd hebben, terwijl de compiler - de vertaler tussen de applicatie taal en de machinetaal - intelligent genoeg moet zijn om te herkennen welke instructie het beste gebruikt kan worden. Typische vertegenwoordigers van de CISC benadering zijn bijvoorbeeld de Intel x86 chips en Motorola 680xx chips. Het succes van deze CPU’s (in bv. de MacIntosh, SUN/3 en veel PC systemen) maakt duidelijk dat er voor de CISC benadering wel wat te zeggen valt. Het probleem met CISC is tweeërlei. Complexe instructies betekent complexe chips wat synoniem is met grote hoeveelheden transistor equivalenten op de chip en dus complexe fabricage processen. Veranderingen en verbeteringen zijn hier (technologisch) moeilijk in aan te brengen. Voorts onderstelt een rijke instructie set een intelligente compiler. Het is praktisch een zeer lastige, zo niet voorlopig onmogelijke, zaak gebleken om dergelijke compilers in elkaar te zetten. Anders geformuleerd, een groot aantal complexe instructies, die de cycle tijd oprekken, wordt amper gebruikt. Deze waarneming wordt bevestigd door analyse van programma’s op de variatie in instructies. De meeste C compilers gebruiken ongeveer 30% van de instructies die voor de CISC processor gedefinieerd Instructie
Percentage
Betekenis
mov
31.0
move
bne
5.6
branch not equal
beq
4.1
branch equal
cmp
3.9
compare
jmp
3.9
jump
dec
3.7
decrement
add
3.7
add
jsr
3.6
jump subroutine
tabel 21Frequentie van assembler statements gemeten in 10.000.000 regels Fortran source code.
zijn, terwijl een onderzoek in een grote gebruikerspopulatie aangaf dat 80% van de programma’s typisch 20% van de instructies gebruikte. Uit dat onderzoek waarvoor 10.000.000 regels wetenschappelijk Fortran als input gebruikt werden staan de meest frequente instructies in tabel 21. Deze top-8 is verantwoordelijk voor ongeveer 60% van het totale aantal instructies. Opvallend is dat in de top-8 geen (!) rekenkundige in-
Inleiding Natuurkundige Informatica
143
1997
Architecturen
Scalaire architectuur
structies staan.
6.2.1.2
RISC
De boven aangegeven observaties hebben geleid tot de ontwikkeling van de RISC processoren waarbij met name het IBM 801 project de drijvende kracht geweest is. Uitgangspunt hierbij is om een (relatief) kleine instructieset van de CPU te definiëren die zo snel mogelijk uitgevoerd moet kunnen worden en die relatief ver van de taal waarin de applicatie geschreven wordt afstaat. Een beetje een RISC processor zal ’uit-zichzelf’ niet weten wat vermenigvuldigen is. Deze kleine instructieset zal echter voor de compiler beter te hanteren zijn. Daardoor zal de compiler beter in staat zijn om ’optimale’ code te genereren voor de toepassing die uitgevoerd moet worden. Tot de typische vertegenwoordigers van de RISC systemen kan men de SUN SPARC en de IBM PowerPC (6xx) rekenen. Op het ogenblik lijkt het debat tussen RISC en CISC in het voordeel van RISC beslist te zijn. Daarbij moet opgemerkt worden dat op het ogenblik ook naarstig naar opvolgers van RISC gekeken wordt omdat de meest voor de hand liggende optimalisatie van RISC nu wel uitgevoerd zijn. In tabel 22 zijn de eerder genoemde verschillen samengevat. Het grote verschillen tussen RISC
CISC
Cycles per instructie
1.3 - 1.7
4 - 10
Relatief aantal instructies
1.1 - 1.3
1
tabel 22Enige karakteristieken van RISC en CISC processoren.
RISC en CISC zit in het aantal cycles per instructie waarbij nog opgemerkt moet worden dat de CISC cycle in doorsnee trager is dan de RISC cycle. Een laatste beperking voor de instructies is dat zij moeten passen in het eerder gedefinieerde woord. Dat limiteert met name het aantal. Ook voor de data geldt in enige mate dat zij moeten passen in de beschrijving van het woord. De grootte van een woord is niet gestandaardiseerd, maar het aantal verschillende formaten begint wel hard af te nemen. Met uitzondering van Cray (64 bits woord) hebben de meeste werkstations en dergelijke een 32 bits woord. Een klein deel van de mensheid zucht nog onder het 16 bits regime van MS/ DOS, dat zullen wij echter buiten beschouwing laten. De grootte van het woord is van belang omdat het de basisgrootte van de te gebruiken types, en dus hun bereik en nauwkeurigheid definieert. Maar ook of andere informatie (geluid en beeldinformatie in deze multimedia tijd) makkelijk gedefinieerd en gemanipuleerd kan worden.
6.2.1.3
Pipelining
De definitie van de cycle tijd als de maximale tijd nodig voor het uitvoeren van een hele cyclus is duidelijk sub-optimaal. Uit de constatering dat de opeenvolgende stappen in principe onafhankelijk zijn kan men een optimalisatie afleiden die de naam van ’pipelining’ gekregen heeft. Essentieel voor dit proces is het verkorten van cycle tijd door de instructies niet in zijn geheel te laten verwerken, maar in een aantal opeenvolgende stappen. In eerste instantie is vijf een logische keuze hiervoor. Daardoor kan de cycle tijd dan terug gebracht worden tot t cycle = max ( t fetch, t decode, t operand, t xeq, t store )
(143)
Wat men hiermee wint is eenvoudig na te gaan. Stel dat de cycle tijd van de ongepipelinede machine t s bedraagt en neem aan dat wij een k pipelined processor hebben. De verwerkingstijd voor m instructies in het ongepipelinede geval wordt dan gegeven door T totaal = mt s
Inleiding Natuurkundige Informatica
144
(144)
1997
Architecturen
Scalaire architectuur
Aannemende dat elk onderdeel van de pipeline t s ⁄ k seconden in beslag neemt, beslaat de verwerking van dezelfde m instructies in een gepipelinede processor ts (k – 1) T totaal, pipe = m --- + ----------------t s k k
(145)
De winst die geboekt wordt door het principe van pipelining wordt dan gegeven door 1 Speedup = -------------------------1 (k – 1) --- + ---------------mk k
(146)
Uit vergelijking (146) is het duidelijk dat geldt dat lim Speedup = k
m→∞
(147)
Daarmee is ook het probleem van de pipelining direct gegeven: wil hij efficiënt zijn dan moet de pipe op gang gehouden worden. Voorts dient bedacht te worden dat de snelheidsverhoging niet groter is dan het aantal stappen waarin de instructie opgebroken kan worden. Het op gang houden van de pipeline heeft voornamelijk te maken met de (on)afhankelijkheid van opeenvolgende instructies. Beschouw als illustratie daarvan het volgende voorbeeld in Figuur 137. Het stuk code for(i=0; i
Figuur 137
Voorbeeld van een programmaconstructie die een pipeline processor sub-optimaal doet werken.
dient om een N bij N eenheidsmatrix te genereren. Met klem moet erop gewezen worden dat het voorbeeld programmeertaal onafhankelijk is. Op drie plaatsen is er sprake van een conditionele executie van instructies: de beide for loops en de if die voor de assignatie gebruikt wordt. Bij de for wordt N maal een test uitgevoerd om te zien of de loop nog gecontinueerd moet worden. N-1 maal zal deze test positief uitvallen, één maal - namelijk de laatste keer - zal de test falen. Die constatering heeft in de vroege versie van de IBM RS6000 chip (601) geleid tot een strategie waarbij er vanuit gegaan werd dat een conditionele test in principe slaagt. Daardoor kon er op de executie geanticipeerd worden en de RISC pipe op gang blijven. Deze aanpak noemt men algemeen branch prediction. Het hebben van een vaste keuze in dit soort gevallen heet statische branch prediction als (logische) tegenhanger van de dynamische branch prediction. De statische keuze is veel minder goed te motiveren bij de evaluatie van de if, hoewel analyse van code tot de conclusie geleid heeft dat programmeurs ’if’ statements meestal op een consistente manier programmeren. Moderne processoren (zoals de IBM RS6000/604 en 620) houden daarom een dynamische tabel bij van de score van de laatste branch instructies. Die tabel heeft overigens een gelimiteerde grootte met als consequenties dat te veel ’if’s niet bijgehouden kunnen worden. Bij het falen van de branch prediction moeten de stappen die verkeerd gedaan worden ongedaan gemaakt worden. Ook daarvoor houdt de processor een bepaalde hoeveelheid geheugen ter beschikking.
Inleiding Natuurkundige Informatica
145
1997
Architecturen
Scalaire architectuur
De les voor de gebruiker is dat daar waar mogelijk ’if’ statements vermeden moeten worden. Het bovenstaande voorbeeld illustreert dat op passende wijze: opsplitsen van de dubbele loops in tweeën: één voor het op nul zetten en één voor het of ’1’ zetten maakt de binnenste ’if’ compleet overbodig, met andere woorden maakt N2 instructies overbodig!
6.2.2
Geheugen
Noodzakelijke voorwaarde voor een gebalanceerd systeem dat een optimale prestatie levert is de eis dat gegevens uit het geheugen tenminste met de snelheid waarmee zij verwerkt kunnen worden opgehaald worden. Deze tijd wordt in eenheden ’cycle tijd’ uitgedrukt. De clock frequentie van ’state-of-the-art’ systemen zit op het ogenblik boven de 200 MHz zodat er cycle tijden tussen de 1 en 5 ns gehaald worden. Als men bedenkt dat de lichtsnelheid in vacuüm ongeveer 30 cm per nanoseconde bedraagt, dan is duidelijk dat alleen al de fysieke uitgebreidheid van een computer een belemmering is om aan de bovengenoemde voorwaarde voor een gebalanceerd systeem te voldoen. Vandaar dat alle hedendaagse systemen een hiërarchisch memory systeem hebben. Enige karakteristieken van deze piramide worden gegeven in tabel 23. Type
Locatie
Access tijd
Grootte
Register
on chip
cycle
1 - 136
Cache
on chip/on board
cycle (hit)
8 - 128 kb
Main memory
on same bus
20 - 100 ns
< 4 Gb
Disk
on system/remote
10 ms
< 9 Gb (/disk)
Tape
on system/remote
100 sec
< 4 Gb (/tape)
tabel 23Hiërarchische structuur van het geheugen
Als informatie opgehaald of weggeschreven wordt dan spreekt men over een memory reference. Voor memory references gelden twee (heuristische) principes: het ’locality-of-reference’ beginsel: memory references uit het recente verleden kunnen gebruikt worden om (met succes) de nabije toekomst te voorspellen. het ’cache’ beginsel: Hoe frequenter data gebruikt worden, des te sneller zou hun access moeten zijn. Het eerste beginsel hangt nauw samen met de manier waarop een programma doorlopen wordt: dit zal op een (redelijk) voorspelbare manier gebeuren. Bedenk overigens dat ’if’ and ’goto’ statements in een programma in principe de voorspelbaarheid negatief beïnvloeden. Het tweede beginsel spreekt voor zich: als data zeer frequent gebruikt worden loont het om dat gebruik te optimaliseren. De hiërarchie die men aantreft in het computer geheugen probeert gebruikmakend van het eerste beginsel het tweede te realiseren (met minimale kostprijs).
6.2.2.1
Basis begrippen
Om historische redenen is de logica van computer systemen gebaseerd op de binaire of tweewaardige logica, die symbolisch aangegeven worden met de waarden ’0’ en ’1’ of ook aangeduid worden als ’true’ of ’false’. Een systeem dat in staat is om een dergelijke binaire grootheid te bewaren noemt men een bit. Intermezzo Wat technische realisatie betreft was de overgang van de buis naar de transistor een grote vooruitgang maar
Inleiding Natuurkundige Informatica
146
1997
Architecturen
Scalaire architectuur
PL
BL
WL G D Figuur 138
S
One-device geheugencel gebaseerd op field effect transistor. WL, BL, PL = word-, bit, plate line
voor snelle geheugens maakte men gebruik van een schema waarbij 6 transistoren gebruikt werden en de aanwezigheid van de stroom als ’0’ of ’1’ geïnterpreteerd werd2. Een doorbraak werd gerealiseerd door het voorstel van Dennard3 dat schematisch aangegeven is in figuur 138 op pagina 147. Slechts één transistor wordt hier gebruikt! De source S is uitgebreid met een extra electrode die verbonden is met de ’plate line’ PL. De lading wordt klem gezet in het gebied tussen deze electrode en source. Het bekrachtigen van de woord lijn en de bit lijn maakt het mogelijk om lading toe te voegen of weg te laten lekken (write/read). Van deze bouwsteen wordt een geheugen chip gemaakt door dergelijke cellen in een matrix te zetten en rij, kolom en versterkers toe te voegen. Het laatste dient om de extreem kleine stromen zodanig te versterken dat ze goed gedetecteerd kunnen worden. Een enkele cel kan beschouwd worden als een klassiek RC circuit, dat zich ontlaadt in een karakteristieke tijd τ = RC waarin R de effectieve weerstand en C effectieve capaciteit is. In de praktijk is deze tijd in de orde van millisecondes. Voor een spanning V wordt er dan een vermogen gedissipeerd van V2/2R. Het karakteristieke speed-power produkt voor een chip met N cellen wordt dan gegeven door 2
Pτ = N V ( C ⁄ 2 ) Aannemende dat lading en capaciteit met dezelfde factor schalen levert dit op dat Pτ = N x
3
Als gevolg hiervan hebben een 64-kilobit chip met 2.5 micron technologie en een 1-megabit chip met 1micron technologie ongeveer dezelfde karakteristiek. Einde intermezzo Een geordende reeks van 8-bits heet een octet, dit ten onderscheid van het niet gestandaardiseerde byte dat echter in de praktijk ook meestal als 8-bits beschouwd wordt. De kleinst adresseerbare hoeveelheid informatie in een computer wordt aangeduid als een woord. De grootte van een woord is niet gestandaardiseerd maar de meeste hedendaagse systemen zijn gebaseerd op een 4byte (32 bit) woord. Tot de uitzonderingen behoren de supercomputers van Cray die gebaseerd zijn op een 8-byte woord. Binnen een computer wordt een woord gekenmerkt door een uniek getal, het adres. Onder bulkgeheugen verstaat men dat geheugen dat alleen in grotere groepen adresseerbaar is. Typische voorbeelden zijn een disk en een tape. Aangezien ook voor een adres een woord gebruikt wordt, is de beperkende factor voor het maximale aantal verschillende adressen de woordgrootte. Voor de hedendaagse 32-bits processoren betekent dat dat het maximale geheugen 232 = 4294967296 groot is. 2.
R. Helne et al, "The application of transistor technology to computers", IEEE transactions on computers, vol. c-25, no. 12, December 1976. 3. R. H. Dennard, "Field effect transistor memory", U.S. Patent 3,387,286, June 1968
Inleiding Natuurkundige Informatica
147
1997
Architecturen
Scalaire architectuur
Het is gebruikelijk om in de bekende eenheden kilo (1024), mega (1048576) en giga (1073741824) te werken. In die termen is het maximale geheugen 4 Gwoorden groot, een grens die allang niet meer onrealistisch is.
6.2.2.2
Registers
Het snelste geheugen bestaat uit zogenaamde registers die zich op de feitelijke CPU bevinden. Deze registers kunnen per definitie binnen 1 cycle geadresseerd worden. Register allocatie wordt gedaan door de compiler. In de oertijd toen die toewijzing nog niet altijd optimaal was bestond er ook de mogelijkheid voor de programmatuur om deze toewijzing te beïnvloeden. De taal C kent bijvoorbeeld het type register dat volgens de definitie van de taal zoveel mogelijk werd afgebeeld op een (fysiek) register. In een typische C constructie als: for(i=0; i
De variabelen a, i en N zullen in registers opgeslagen worden, de vectoren x en y zullen in de cache geplaatst worden
zullen de variabele i, N en a in een register geplaatst worden. Op de meeste hedendaagse systemen maakt men onderscheid tussen integer en floating point register. Een tweetal register plaatsen heeft een speciale functie en daarom ook een speciale naam gekregen. Het zijn het instruction register en de program counter. Het eerste register bevat de instructie die op het ogenblik uitgevoerd wordt, terwijl het tweede register het adres van de volgende instructie die uitgevoerd moet worden bevat. Deze zijn aangegeven in figuur 136 op pagina 142. De grootte van register is beperkt tot die van het woord. In sommige architecturen zijn ze hergroepeerbaar tot een dubbelwoord. Verdere uitbreiding moet in software gedaan worden. Voor de gebruiker, zelfs de programmeur in derde generatie talen, is het bestaan van registers transparant met dien verstande dat de compiler wel in staat moet zijn om te zien welke variabelen in registers geplaatst moeten worden en welke niet. Dat is bij kleine functies/subroutines meestal veel eenvoudiger dan bij een programma dat uit één groot onderdeel bestaat. Een programma (onderdeel) dat geheel in de registers past zal optimale prestaties hebben omdat de CPU geheel autonoom kan draaien. Sommige benchmarks, zoals de Drystone, benaderen dit gedrag in hoge mate, reden waarom zij vaak als weinigzeggend bestempeld worden. De grootste beperking van registers is hun beperkte aantal.
6.2.2.3
Cache
De hoeveelheid ruimte die beschikbaar is in de registers is (te) beperkt voor de meeste praktische gevallen. Als intermediair voor het (te trage) main memory en het (te geringe) register gebruikt men de cache (figuur 136 op pagina 142). Het cache geheugen is gebaseerd op het ’locality of reference’ beginsel. Dat kan ook aan het bovenstaande programmavoorbeeld geïllustreerd worden: als x[i] gebruikt wordt, dan moet ook y[i] gebruikt worden, en is het waarschijnlijk dat binnenkort ook x[i+1] en y[i+1] gebruikt zullen worden. Een cache is opgebouwd uit ’cache links’ waarin een beperkt aantal opeenvolgende (!) geheugen lokaties passen (meestal grootte orde 8). In het voorbeeld van figuur 139 op pagina 148 zullen dus 8 opeenvolgende x waarden in één cache lijn geplaatst worden zodra de eerste opgehaald wordt. Door speciale hardware constructies is men in staat om al die elementen in één keer te scannen bij een reference en te zien of het gevraagde adres aanwezig is. Verschillende lijnen kunnen op verschillende delen van het geheugen afgebeeld worden. Een voorbeeld hiervan wordt gegeven in figuur 140 op pagina 149. De manier waarop bepaald wordt welk geheugen plaats op welke cache lijn afgebeeld wordt hangt samen met de structuur van de cache. De meest eenvoudige is de zogenaamde ’direct mapped’ cache’ waarvoor de volgende relatie geldt: cache lijn = main memory adres modulo cache size
Inleiding Natuurkundige Informatica
148
1997
Architecturen
Scalaire architectuur
Cache Main memory
Line 0 Line 1 Line 2 ...........
Figuur 140
Cache bevatten data van verschillende plaatsen in het geheugen.
In principe is dit een ontaard probleem: er zijn meer plaatsen in het main memory die in aanmerking komen voor een cache lijn, dan plaatsen in de lijn zelf. De beloning voor de totale cache constructie zit in de access tijd die gegeven wordt door t eff = ht cache + ( 1 – h )nt main
(148)
waarbij h de hit ratio genoemd wordt en n staat voor de hoeveelheid extra werk die gedaan moet worden om de cache weer te vullen. Mogelijke waarden voor de access tijden staan in tabel 23 op pagina 146. Een hit ratio van 95% wordt als redelijk beschouwd. Daarbeneden begint de efficiency van de cache substantieel af te nemen. Voor de ’direct mapped cache’ is het eenvoudig om pathologische gevallen te bedenken. Neem als voorbeeld een cache ter grootte van 4K, en beschouw het volgende programma (Figuur 141). De arrays A en B real*4 a(1024), b(1024) common /glue/ a, b do 10 i=1, 1024 a(i) = a(i) * b(i) 10 continue Figuur 141
Fortran programma dat op een direct mapped cache van 4 Kb zal trashen.
zijn beide precies 4 Kb groot en zullen door de (Fortran common) constructie exact 4 Kb uit elkaar liggen in het geheugen. Dat betekent dat zij op dezelfde cache lijnen afgebeeld worden. Evaluatie van de ’do loop’ zal vanuit het oogpunt van de cache een catastrofe zijn: telkens zullen a en b elkaar afwisselen in de cache waardoor de hit ratio dramatisch laag zal worden. Een dergelijk fenomeen noemt men ’thrashing’. Voor de gebruiker is een thrashend programma soms te repareren. Beschouw weer het geval van Figuur 141. Stel dat wij array A één element meer geven (maar niet gebruiken). Dan schuift ook array B in zijn geheel één plaats op en dus ook één plaats in de cache. Het resultaat hiervan is dat het(zelfde) programma plotseling veel sneller loopt! Verbetering is ook te vinden door een betere cache strategie te bedenken. Het grote probleem bij de cache is dat er op gebruikersniveau weinig hulpmiddelen zijn om het gebruik te optimaliseren. De cache kan abstract gezien worden als een intermediair tussen de CPU en het main memory. Veranderingen in de cache worden periodiek teruggeschreven naar het main memory. Problematisch is echter het geval waarin de inhoud van het main memory verandert. Dan moet de cache ook geïnvalideerd worden. Twee schema’s worden hiervoor vaak gehanteerd bekend onder de naam ’write-through’ en ’write-back’. In het geval van ’write-through’ resulteert elke write naar de cache gelijktijdig in een write naar het memory dat zo aldoor in de pas loopt met de cache. Een conceptueel eenvoudige maar niet super efficiënte oplossing.
Inleiding Natuurkundige Informatica
149
1997
Architecturen
Scalaire architectuur
Cache Memory
Processor
Main Memory
A
I/O Processor
Cache Memory
Processor
I/O Processor Figuur 142
Main Memory
B
Twee mogelijkheden voor de combinatie van het I/O systeem met een cache: a) de cache multiplext de request van de I/O processor en de central processor; en b) de I/O processor heeft een direkt pad naar het memory. Noodzaak hiervoor is een interlock tussen de cache en het I/O systeem.
De tweede strategie heet de write-back. Dit impliceert dat veranderingen in de cache geschreven worden die op zijn beurt periodiek teruggeschreven wordt naar het geheugen. Teneinde de synchronisatie tussen de cache en het memory te kunnen garanderen houdt de I/O processor een kopie bij van de inhoud van de cache en consulteert die eerst om te zien of zijn data in de cache staan. Is dit niet het geval dan kunnen de data zonder meer uit het geheugen opgehaald worden anders wordt het item uit de cache opgehaald. Het voordeel van deze oplossing is dat hij veel efficiënter omgaat met de dure cache cycles. In de huidige RISC systemen waarbij minimalistie van de processor cycle tijd van groot belang is, is het gedrag van de cache allesbepalend voor het succes van een systeem.
6.2.2.4
(Virtueel) geheugen
Een principe dat ook in hedendaagse computers nog veel aangetroffen wordt staat bekend onder de naam virtueel geheugen. Het principe van het virtuele geheugen is ontstaan uit het Atlas project (1962). De motieven zijn enigszins veranderd; het principe is min of meer hetzelfde gebleven. Zeker in de 60er jaren was computergeheugen duur en dus klein. Voor de gebruiker betekende dat een ernstig probleem aangezien hij zich het hoofd moest breken hoe zijn probleem af te beelden was op het kleine geheugen. Kortom het priVan processor Virtueel adres
ADRES MAPPER
Fysiek adres Figuur 143
Page fault (Page is niet aanwezig) Naar geheugen
Structuur van de afbeelding van een virtueel adres naar een fysiek adres.
Inleiding Natuurkundige Informatica
150
1997
Architecturen
Scalaire architectuur
maire geheugen is kleiner dan de behoefte van de gebruiker (dat is er overigens sinds 1962 niet beter op geworden). De grootte van het adres is echter ruim voldoende om grotere hoeveelheden geheugen te adresseren. Ook hier, net zoals bij de cache zij het dan op een iets andere manier, speelt het principe van de locality of reference een rol. In termen van een programma betekent dat dat voor het draaien van een programma per tijdsvak slechts een gedeelte van de totale hoeveelheid instructies en data nodig zijn. Tot nog toe hebben wij voor het main memory aangenomen dat er met elke plaats één uniek adres verbonden was, en dat dat adres niet veranderde. Indien het adres wel zou veranderen en ergens een mapping van het adres bijgehouden zou worden, zou dezelfde fysieke locatie voor meerdere adressen, maar dan wel op verschillende tijden gebruikt kunnen worden. Dat vormt het principe van virtueel geheugen. Voor een hypothetische machine met een adres van 32 bits gaan wij er in principe vanuit dat alle 2 32 adreslocaties gebruikt kunnen worden. Voorts nemen wij aan dat het primaire geheugen van de betreffende computer niet zoveel main memory heeft. Dientengevolge kan het adres niet een fysiek adres zijn want er kan geen plaats in het geheugen mee corresponderen. Wij zullen dat adres dan ook een virtueel adres noemen. Het zal duidelijk zijn dat als wij een vertaaltabel bijhouden voor elke positie in het fysieke geheugen met welk virtueel adres die plaats correspondeert het gehele geheugen in beslag genomen wordt door deze vertaaltabel. Tevens zou Virtueel adres Page nummer
Adres binnen pagina Adres binnen page
Page map
Base adres van pagina
Fysiek geheugen
Figuur 144
Een typisch voorbeeld van een mogelijke implementatie van de afbeelding van een virtueel adres op fysiek geheugen.
dan elk adres apart vertaald moeten worden en potentieel op een verschillende plaats kunnen liggen in het fysieke geheugen. Dat zou enorm snelheidsvertragend kunnen werken. Vandaar dat men in grotere eenheden werkt, de zogenaamde pages. Een typische grootte van een page is 1K tot 4K adressen. Het gehele fysieke geheugen kan men dan opdelen in pages. Ergens in het geheugen wordt voorts een tabel bijgehouden die de informatie bevat hoe de mapping is van de virtuele adressen op de fysieke adressen. Een virtueel adres valt uiteen in twee delen: een page en een offset binnen de page. Met behulp van het virtuele adres en de page tabel kan het fysieke adres uitgerekend worden, dat wil zeggen als die in het main memory is, anders zal de referentie naar het virtuele adres resulteren in een zogenaamde page fault dat wil zeggen de betreffende page zal eerst in het geheugen gebracht worden waarna de eigenlijke vertaling plaats vindt. Met het groter worden van het main memory groeit ook de grootte van de vertaaltabel en dus de zoektijd. De methoden die voor de oplossing van dit probleem gevonden zijn liggen echter buiten de scope van deze syllabus. Men treft hier een zelfde soort probleem aan als bij de cache. De eindige grootte van het main memory maakt het nodig om een bepaalde strategie te hebben bij het vervangen van pages in het memory. Ook hierbij maakt men gebruik van b.v. de LRU (Least Recently Used) algoritme. Desalniettemin ligt de situatie iets anders dan bij de cache doordat de penalty voor een page fault veel groter is dan voor een cache fault. Ook
Inleiding Natuurkundige Informatica
151
1997
Architecturen
Scalaire architectuur
in dit geval kunnen er pathologische situaties ontstaan, doordat de pagina die de instructie bevatte die een page fault genereerde zelf verwijderd werd, waardoor een nieuwe page fault gegenereerd werd etc. Programma’s die excessief veel pagen noemt men thrashing. Het zal weinig nader betoog behoeven dat dit geen gunstige eigenschap is. Net als bij het cache mechanisme kan de gebruiker proberen de page faults te minimaliseren door te trachten zoveel mogelijk rekening te houden met de locality of reference. Ook hier geldt echter de regel dat hij het zelf niet in de hand heeft. Paradoxaal genoeg kan men dus een systeem waar veel thrashing optreedt versnellen door er meer geheugen aan toe te voegen. Men kan zich ook afvragen of de gebruiker nu wel baat heeft bij virtueel geheugen. Het antwoord daarop is dat een groot aantal applicaties tegenwoordig op grote schaal beroep doen op extreem grote geheugens. In dat soort gevallen is het niet mogelijk om te werken zonder virtueel geheugen. De dalende prijzen van fysiek geheugen maken anderzijds in een aantal gevallen ook uitbreiding van het fysieke geheugen als oplossing mogelijk. Hoewel er voor de gebruiker misschien een grote overeenkomst is tussen cache en virtueel geheugen dient er benadrukt te worden dat er op architectuurniveau grote verschillen zijn.
6.2.2.5
Disk
Een disk bestaat uit een ronde schijf waarop een magnetische coating is aangebracht. Deze coating is selectief magnetiseerbaar waardoor er informatie vastgelegd kan worden op de disk. In werking is deze vergelijkbaar met een bandrecorder. De grootte van de schijf is gestandaardiseerd in drie courante vormfactoren: 8 inch, 5.25 inch en 3.5 inch. Teneinde alle delen van het oppervlak te kunnen bereiken worden er twee soorten mechanische bewegingen uitgevoerd: translatie en rotatie (zie ook Figuur 145) Principe schema disk
rotatie
seek
Figuur 145
Principe schema van een disk oppervlak.
De disk zelf draait rond met een gefixeerde snelheid (meestal ongeveer 3600 rpm). Bedenk dat de snelheid waarmee de disk ronddraait bepaalt wat de tijd is die verloopt tussen twee opeenvolgende keren dat eenzelfde plek op de disk onder de heads passeert: 1 Rotational delay = ---------------------------------------------------aantal omwentelingen
(149)
Deze tijd noemt men de rotational delay. De tijd die nodig is voor het positioneren van de arm noemt men de seektijd. Het zal duidelijk zijn dat de grootte van de seektijd afhankelijk is van de “vorige” positie van de arm. Daarom zijn een drietal karakteristieken gangbaar: gemiddeld, minimum en maximum. De schijf zelf is onderverdeeld in concentrische cirkels, de zogenaamde tracks, terwijl de tracks weer onderverdeeld zijn in zogenaamde sectoren. Deze sectoren zijn de eigenlijke informatieeenheden voor de
Inleiding Natuurkundige Informatica
152
1997
Architecturen
Scalaire architectuur
schijf en zijn meestal 512 bytes groot. De minimale seektijd correspondeert met 1 track opschuiven van de head, terwijl de maximale seektijd correspondeert met het opschuiven van de head over de volle straal van de disk. Aangezien alle delen van het disk oppervlak nog ongeveer even eenvoudig bereikt kunnen worden Aantal tracks Aantal sectoren/track Aantal heads Aantal oppervlakken Rotation speed Seektijd
300 - 1400 20 - 80 2 - 28 1-7 3600 - 5400 10 ms 3 ms 22.5 ms
rpm typical min. max.
tabel 24Overzicht van enige karakteristieken van 5.25 inch disks.
is de disk in zekere mate nog een voorbeeld van een random access memory. Beide kanten van een disk oppervlak kunnen gebruikt worden, terwijl in één zogenaamde disk-drive meestal meerdere disks zitten. Al de disk oppervlakken zijn uitgerust met “eigen” lees/schrijf koppen. Bepalend voor de schijf zijn de grootte en de snelheid waarmee de data bereikt kunnen worden. De karakterisatie van de data geschiedt nu in de vorm van track/sectoren paren. Het zal duidelijk zijn dat een disk niet direct door een computer bestuurd wordt maar door een dedicated stuk hardware. Dit noemt men de disk controller. Deze controller is uitgerust met een eigen soort geheugen om data tijdelijk op te slaan. Ook hier speelt de grootte van de buffer weer een rol. Enig redeneer werk toont aan dat een disk controller optimaal kan functioneren als hij in staat is om een hele track te bufferen. Uit tabel 24 blijkt dat er een groot verschil is tussen de tijd die nodig is om twee naast elkaar liggende tracks te lezen in vergelijking tot twee ver uit elkaar liggende tracks. Zeker gezien de tijd die de disk nodig heeft om gemiddeld gepositioneerd te raken (grootte orde 10 ms) kan er wel enige processing gedaan worden op de track/sector paren die opgevraagd worden. Daarom houden de meeste disk controllers een lijst bij van track/sector paren die opgehaald moeten worden en sorteren deze op minimale beweging van arm. Dit verhoogt de prestatie van de disk aanzienlijk. In relatie tot het primaire geheugen verschilt de opzoektijd van de disk echter grootte ordes. Dat betekent dat een gebruiker zich er van bewust zal zijn wanneer hij de disk veel gaat gebruiken. In relatie tot het principe van het virtuele geheugen dat in de vorige paragraaf behandeld is wordt dit bijzonder manifest. Tussen 5 het main memory en de disk bestaat een snelheidsverschil van ongeveer 10 . Als het page replacement algoritme voor het virtuele geheugen dus faalt en de juiste pagina is niet in het geheugen (page fault) maar moet van de disk gehaald worden, kan men met een formule van het type als (149) de vertraging uitrekenen. Een nieuwe ontwikkeling op het gebied van de disks vormt de zogenaamde optische disk waarbij de informatie niet meer magnetisch maar optisch wordt vastgelegd. De technische problemen die hierbij een rol spelen zijn nog erg groot, vandaar dat de mogelijkheden van dit medium voorlopig nog beperkt zijn tot een eenmalig schrijven van de informatie, en daarna een herhaald uitlezen. Voor een aantal toepassingen is dat ruim voldoende maar voor doorsneegebruik van een computer duidelijk niet. Het grote voordeel van de optische disks is dat zij een grote informatiedichtheid hebben; het huidige nadeel is, naast het feit dat de informatie maar 1 keer geschreven kan worden, dat de zoektijd voor de informatie nog relatief groot is. Tot de aperte voordelen behoren de robuustheid van het medium en de grote opslagcapaciteit: op een standaard CD past met de huidige technologie 600 Mb terwijl in de nieuwe standaard 9.1 Gb geschreven kan worden!
6.2.2.6
Tape
Verder doorgaand in de piramide van de geheugenstructuur komt men na de disk uit bij het klassieke medium van de tape. Deze is in werking (ook) te vergelijken met de bandrecorder tape. Ook hier is weer de essentiële werking dat selectief gebieden gemagnetiseerd worden wat weer als informatie geïnterpreteerd
Inleiding Natuurkundige Informatica
153
1997
Architecturen
Scalaire architectuur
kan worden. Het zal duidelijk zijn dat de tape weer een tragere zoeksnelheid heeft dan de disk, maar anderzijds ook weer goedkoper is, en, wat een zeer belangrijk voordeel is, veel makkelijker te transporteren en uit te wisselen is. De organisatie van de informatie op een tape is in blokken: Tape structuur Figuur 146
Block 1
Block 2
Block 3
inter record gap De tape wordt door allerlei mechanisch fantastische constructies langs een koppenstelsel gevoerd dat in staat is om de informatie van de tape te lezen. Wil dat echter een zinvolle activiteit zijn, dan dient de tape een bepaalde snelheid te hebben. Teneinde de tape drive de mogelijkheid te geven om op snelheid te komen is daarom tussen de blokken informatie een zogenaamde interrecord gap geplaatst, die in doorsnee 1 inch groot is. Dat betekent al dat de tape niet 100% efficiënt gebruikt zal worden. De blokgrootte van een tape kan nogal variëren en loopt meestal van 512 bytes tot 10K bytes. Het zal duidelijk zijn dat een grote blokgrootte leidt tot een efficiënt tape gebruik. Bij een tape wordt altijd gerekend in bytes vanwege de manier waarop de informatie op de tape gedecodeerd wordt: een byte wordt verticaal over de tape weggeschreven. Meestal doet men dat zelfs in een formaat waarbij er negen bits parallel worden weggeschreven: 8 informatie bits en 1 controle bit om te verifiëren of de informatie van het byte correct is. Bepalende karakteristieken voor een tape zijn de vormfactor, oftewel de breedte van de tape. Die is meestal gestandaardiseerd op 2 maten: 4 en 8 mm. Voorts is bepalend voor een tape de recording density die aangeeft hoe dicht de informatie op elkaar geschreven wordt. De recording density wordt uitgedrukt in bytes per inch (bpi). Een courante maat daarvoor is 6250 bpi. De schrijfsnelheid wordt aangegeven in inch per second (ips) en bedraagt meestal iets in de grootte orde tussen 20 en 150. Tenslotte bepaalt de lengte de totale opslag capaciteit van een tape. Meestal is die ongeveer 120 m. Op die manier kan men op een tape (met compressie) ongeveer 1-4 Gb informatie kwijt. Een tape drive is goedkoop en kan een grote hoeveelheid informatie bevatten maar de tol die men moet betalen in zoektijd is ook niet gering. Om van het begin naar het einde van een tape te spoelen duurt ongeveer 200 sec. Daaruit volgt ook dat een tape een voorbeeld is van een sequentieel medium. Wil men blok N kunnen lezen dan moet men eerst de voorliggende N-1 blokken laten passeren. Voor een aantal soorten gegevens is dat alleen maar handig, men denke hierbij aan spectrale gegevens van een experiment die na elkaar afgehandeld moeten worden. Een tweede nadeel van een tape is dat het moeilijk is om informatie te veranderen die eenmaal op een tape geschreven is. Zelden zal de nieuwe informatie exact passen in de ruimte die gebruikt werd door de oude informatie. Dat betekent dat in de meeste gevallen de veranderde informatie op een nieuwe plek op de tape weggeschreven moet worden. Wel heeft de tape, net als de disk, het voordeel dat de informatie er lange tijd op bewaard kan blijven. Met name echter door overspraak door de verschillende lagen van de tape die over elkaar opgewonden zijn, degradeert de informatie met de loop van de tijd, dat betekent dat ook een tape maar een gelimiteerde levensduur heeft. Het is een goede vuistregel om aan te nemen dat een tape ongeveer 4 jaar gebruikt kan worden. Met name de eenvoudige manier waarop data getransporteerd kunnen worden door middel van een tape maakt het tot een heel aantrekkelijk medium.
6.2.3
I/O
Algemeen duidt men memory, disk, tape e.d. aan als devices. Onder I/O wordt de informatie uitwisseling verstaan tussen devices. Dit kan zowel binnen één computersysteem zijn als tussen verschillende systemen. Drie eigenschappen zijn met name bepalend voor de I/O
Inleiding Natuurkundige Informatica
154
1997
Architecturen -
Scalaire architectuur
de latency de bandbreedte de connectiviteit (of topologie).
De tijd die nodig is om de hoeveelheid data te versturen van het ene device naar het andere kan meestal lineair gemodelleerd worden als t totaal = t 0 + Nt elt
(150)
De constante term t 0 brengt de tijd in rekening die nodig is voor het tot stand brengen van de communicatie terwijl de term t elt de (lineaire) afhankelijkheid van de totale tijd op het aantal data dat verzonden wordt modelleert. Naar de constante refereert men als de latency, terwijl de tweede term wordt aangeduid als de bandbreedte. Het is niet alleen van belang om een grote bandbreedte te hebben maar ook om te zorgen dat de latency minimaal is. Als er meer dan twee devices in het spel zijn is de topologie van de verbinding (ook) van belang. Voor gewone computersystemen varieert zij niet veel, maar bij de interconnectie van systemen kan de topologie voorkennis omtrent het soort problemen waarvoor de architectuur gebruikt gaat worden in rekening brengen.
6.2.3.1
Topologie
Onder de topologie verstaat men de verbindingsstructuur tussen een aantal devices. Algemeen maakt men een onderscheid tussen statische en dynamische topologieën. Bij de eerstgenoemde benadering zijn de verbindingen gedurende een bepaalde tijd onveranderbaar, bij de tweede kunnen ze in de loop van een proces aangepast worden. Een voorbeeld van de laatste topologie zijn telefoonverbindingen. In dit overzicht zullen
Topologie
Latency
Maximum Bandbreedte
Connectiviteit
Wiring Complexiteit
Switch Complexiteit
all-to-all
const.
const.
any to any
N2
-----
binaire boom
log N
const.
3 nearest neighbors
N
-----
bus
const.
1/N
any to any, if bus free
constant
N
tabel 25Drie verschillende topologieën van verbindigen met hun voornaamste karakteristieken. N duidt het aantal devices dat verbonden wordt aan.
wij ons beperken tot de statische verbindingen. Drie verschillende topologieën die men vaak aantreft staan in tabel 25, terwijl een schematische representatie staat in Figuur 147. Het conceptueel meest eenvoudige
all-to-all Figuur 147
binaire boom
bus
Overizcht van enige topologieën voor netwerken.
Inleiding Natuurkundige Informatica
155
1997
Architecturen
Scalaire architectuur
schema is het all-to-all. De voordelen zijn apert: geen latency en geen afname van de bandbreedte bij toename van het aantal devices. De prohibitieve factor echter voor de meeste praktische toepassingen (i.e. waarden van N) is de kwadratische complexiteit van de bedrading. In stervormige schema’s wordt geprobeerd om dit probleem te minimaliseren tot orde N. Het nadeel daarvan is dat er een latency ingevoerd wordt (alles moet via de centrale node) en dat de kwetsbaarheid fors toeneemt. Een voordeel van de all-to-all structuur is dat er geen (!) arbitrage nodig is bij het gebruik van de beschikbare verbindingen: alle communicatie kan parallel geschieden. De binaire boom heeft als voordeel dat hij de bedradingscomplexiteit reduceert tot orde N. De prijs die daarvoor betaald moet worden is een toename van de latency. Van een constante naar log N, immers, alle componenten op de boom moeten het signaal doorgeven. In tegenstelling tot de ster is nu elke node ’onmisbaar’ zodat de kwetsbaarheid toeneemt. Omdat over alle subtakken van de boom ook onafhankelijk communicatie bedreven kan worden blijft de bandbreedte vrijwel constant. De bus is populairste topologie binnen veel computersystemen. Dat volgt met name uit de eenvoud waarmee nieuwe devices toegevoegd kunnen worden aan de structuur. De nadelen zijn echter minstens even apert: over de bus kan maar 1 transfer tegelijk gedaan worden, zodat effectief de bandbreedte van de communicatie afneemt met toenemend aantal devices aan de bus. een verder gevolg van het delen van het communicatie medium is dat er een vorm van arbitrage moet zijn op de bus. In figuur 136 op pagina 142 ziet men in het principe schema van een computer al een tweetal bussen getekend: de meest belangrijke bus is de memory bus die CPU, cache en main memory met elkaar verbindt. Via een adapter wordt op deze bus onder andere de bus aangesloten waarop de disk gezet wordt. Een dergelijke getrapte structuur kan zich een aantal malen herhalen. In complexe situaties als hier bedoeld spreekt men dan ook wel eens over een zogenaamde ’fat tree’.
6.2.3.2
Voorbeelden.
Naast de topologie is ook de fysieke structuur van de I/O verbinding van belang. Een algemene behandeling valt buiten het kader van dit college. Wij zullen ons hier beperken tot een paar bussen die (in de fysica) veel voorkomen. Het vijftal bussen dat wij beschouwen staat vermeld in tabel 26. Bus breedte
Max. Lengte (m)
Max. Slots
Bandbreedte (MByte)
Latency
Arbitrage
CAMAC
24+24
0.48
23
3
<1
µs
Nee
VME
32+32
0.48
31
10
<1
µs
Ja
IEEE-488
8
20.
15
1
>20
µ sa
Nee
SCSI
8
6
8
1.5b - 4c
>10
µ sd
Ja
Ethernet
1
500
1024
1
>10
µs
Ja
tabel 26Karakteristieken van enige communicatie bussen.
a.+ tijd voor adressering b.asynchroon c.synchroon d.+ tijd voor adressering
De eerste twee, CAMAC en VME, behoren tot de computer instrumentatie bussen. Zij kunnen als basis dienen voor complete computersystemen of data-acquisitie systemen die zeer snel moeten kunnen werken. CAMAC, dat oorspronkelijk uit de kernfysische hoek komt, is een 24-bits systeem in tegenstelling tot VME
Inleiding Natuurkundige Informatica
156
1997
Architecturen
High Performance Computing
dat een compleet woord kan bevatten. De breedte van deze bussen weerspiegelt dat niet alleen data maar ook adressen uitgewisseld moeten worden: beide bussen zijn ’dubbel’ uitgevoerd. Ook de lengte van de bussen laat zien dat het hier om volwaardige computerbussen gaat. De bandbreedte van deze systemen is hoog, terwijl de latency klein is. IEEE-488 en SCSI behoren tot de zogenaamde instrumentatie bussen, waarbij de eerste van oudsher gebruikt is voor koppeling van meetinstrumenten aan computers en de tweede meer ontwikkeld is voor het koppeling van (externe) devices aan computer systemen. Dat blijkt onder andere uit de lengte van de bus, die substantieel groter is dan in het geval van CAMAC en VME. De bandbreedte is ook wat kleiner voor deze bussen, hoewel er wel op gewezen moet worden dat deze twee instrumentatie bussen byte georiënteerd zijn. Ethernet tot slot is de technologie die grote opgang gevonden heeft als methode om computersystemen aan elkaar te verbinden. Om kabeltechnische redenen is het in dat geval eenvoudiger om bit georiënteerd te werken.
6.3
High Performance Computing
Op een aantal manieren kunnen de prestaties van een scalair systeem vergroot worden. Daarmee bereikt men het domein dat meestal aangegeven wordt met de naam High Performance Computing. Drie wegen zijn symbolisch aangegeven in figuur 135 op pagina 141. Elk van de drie methoden is gebaseerd op een karakteristieke vooronderstelling omtrent het programma dat uitgevoerd moet worden. Bij de super scalaire processor wordt de onafhankelijkheid van de instructie stroom op een laag niveau geoptimaliseerd, vector processoren buiten de (herhalings)structuur van vector (matrix) operaties uit terwijl parallelle architecturen de onafhankelijkheid op gebruikers (hoog) niveau van sommige problemen benutten. Wat de juiste architectuur voor een bepaald probleem is, is dus een kwestie van afweging. De navolgende paragrafen dienen om enige inzicht te geven in motivaties voor een dergelijke afweging.
6.3.1
Super Scalar architecture
Een super scalar processor kan gezien worden als een multi processor systeem in één CPU. Het parallelliseren wordt op een laag niveau gedaan: de uiteindelijke instructiestroom. Deze wordt onderverdeeld in drie groepen: integer instructies, floating point instructies en branch instructies. Van cruciaal belang is een goede compiler die in staat is om een gebruikersprogramma om te zetten in een stroom van parallelle instructies. De meeste compilers kunnen onder ideale omstandigheden drie to vier operatier per clock cyclus uit voeren en een gemiddelde halen van twee. Voorbeeld van zulke processoren zijn de IBM RS/6000, de TI SuperSPARC en de Motorola 88110. De IBM RS6000 is de trend setter in deze reeks. De meest geavanceerde (620) kan 6 instructies simultaan afhandelen (3 integer, 1 floating point, 1 load/store en 1 branch). Het principe schema dat gegeven wordt in Figuur 148 is gebaseerd op het oudere 601 ontwerp. Instructies worden opgehaald en in de instructie cache gestopt. Daar wordt er vast een gedeeltelijk interpretatie gedaan zodat de uiteindelijke decode sneller kan gebeuren. De processing in de fixed en floating point units is gecoördineerd, maar onafhankelijk in gescheiden instructie pipelines. Beide hebben queues die maximaal 12 instructies kunnen bufferen. In het meest optimale geval hoeven de beide units niet helemaal in de pas te lopen. De fixed point unit kan twee instructies voorlopen op de floating point, terwijl omgekeerd de floating point 6 instructies kan voorliggen op de fixed point voordat het noodzakelijk is delay instructies, die cycles verteren maar niets doen, te gaan starten. De branch unit heeft de overall contrôle over het geheel en functioneert als ‘instructie verdeler’. Als een branch voldoende ver van te voren gestart kan worden zal er geen vertraging opgelopen worden. In het slechtste geval worden er drie cycles verspeeld. De Pentium van Intel kan in sommige opzichten ook beschouwd worden als een Super scalar processor: hij wordt gekenmerkt door een kern van RISC instructies omgeven door een uitgebreide ondersteuning van 80x86 instructies die om compatibiliteits redenen toegevoegd zijn. (De AMD K5 processor, de tegenhanger van Intel’s Pentium, weet die RISC instructies weer op te breken in sub-instructies die parallel uitgevoerd
Inleiding Natuurkundige Informatica
157
1997
Architecturen
High Performance Computing
Q7 cache
Q0 Instruction queue Issue Logic FPU Buffer Decode Execute 1 Execute 1 Writeback Figuur 148
BPU
IPU
Decode Writeback
Decode Buffer Execute Writeback
Deel van het IBM RS6000/601 ontwerp gebaseerd op een viervoudige simultaanheid van operaties. Bij de 604 is dit verhoogd naar 6.
worden en slaagt er zo in om twee maal zo hard te lopen. In dit marktsegment is de concurrentie maximaal omdat de belangen enorm zijn: men schat dat er wereldwijd 100 miljoen IBM PC clonen zijn, terwijl er maar ongeveer 650.000 SPARC machines zijn, oftewel een ratio van 154 : 1! Het aantrekkelijke van de super scalaire architecturen is dat de transparantheid voor de gebruiker hoog is: hercompileren van de orginele applicatie is in de meeste gevallen voldoende om een (aanzienlijke) snelheidswinst te boeken. Het nadeel blijft dat de winst beperkt is en staat of valt met de kwaliteit van de compiler en de cache. Alternatieve benaderingen zoals superpipelined processoren of long instruction word processoren vallen buiten het kader van dit diktaat.
6.3.2
Vector Processoren (SIMD)
Het fundamentele verschil tussen scalaire processoren en vector processoren is de herhaling van een operatie. Beschouw als voorbeeld de overbekende ‘saxpy’ operatie, die gedefinieerd wordt door y = y + ax . Op een klassieke architectuur dient de complete cycle voor elk element van x en y doorlopen te worden. Het zal duidelijk zijn dat de herhaalde fetch, decode, operand fetch, en store zinloos zijn: het is voldoende om ze eenmalig uit te voeren. Daarbij wordt dan wel aangenomen dat de benodigde (vector) data in één keer opgehaald en weggeschreven kunnen worden. Dus komt men tot de volgende eigenschappen van een vector processor -
t 0 setup tijd van de vector processor. Wij verstaan hieronder de tijd die nodig is om het eerste element uit te rekenen, (zie tabel 27) t c cycle tijd van de vector processor, t s de tijd die nodig is om in scalaire mode een bewerking uit te voeren, n vec de lengte van de vector registers.
Karakteristiek voor een vectorpijp is dat na deze opzet fase elke clock cyclus een resultaat behaald kan worden. Voor een aantal machines zijn de karakteristieken in tabel 28 neergezet.
Inleiding Natuurkundige Informatica
158
1997
Architecturen
High Performance Computing
VECTOR FACILITY Vector Memory Controller and Address generator
Memory (Data and program)
Vector Controller
Local memory
Arithmetic Pipeline
Processor Scalar Instruction Processor
Figuur 149
Principeschema van een vector processor.
Secondary Memory
Mnemonic ADDXV SUBXV CMPV SHFV IORV XORV ANDV CNIFV CNFIV ADDFV SUBFV MULFV DIVFV
Instructie Setup tijd Integer add 52+N Integer substract 52+N Integer compare 53+N Shift 55+N Inclusive or 52+N Exclusive or 52+N And 52+N Convert integer to float53+N Convert float to integer53+N Floating add 56+N Floating sub 56+N Floating multiply 56+N Floating divide 80+7N
tabel 27Setup tijden voor een aantal vector instructies van de Cyber 995. N is vector lengte, maximale vector lengte is 512. Cycle tijd is 16 ns.
Uitgaande van de hiervoor gegeven definities is eenvoudig in te zien dat de tijd die nodig is om vectorieel n operaties uit te voeren gegeven wordt door: t n = t 0 + ( n – 1 )t c
Inleiding Natuurkundige Informatica
159
(151)
1997
Architecturen
High Performance Computing
Machine t c (ns)
t0 Vector length r ∞ (cycles) ( µs ) – 1
n1 ⁄ 2
IBM 309014.5 Cyber 995E16.0 Cyber 20520 Cray 1 12.5 Cray X-MP8.5
30 52 - 80 51 - 80 20 -
29 51 -79 50 - 79 19 - 20 -
256 512 65536 64 64
68 62.5 50 80 117
tabel 28Karakteristieken van enige vectormachines plus classificatie volgens Hockney.
Bedenk dat wij hier de setup tijd definiëren als de tijd nodig om het eerste resultaat uit te rekenen vandaar de factor n-1. Andere auteurs gebruiken soms een andere conventie. Het zal duidelijk zijn dat voor grote waarden van n de invloed van de setup tijd steeds minder wordt. Op grond van deze beschrijvingen heeft Hockney een tweetal parameters gedefinieerd die het mogelijk maken om vector processoren te vergelijken en te karakteriseren. De eerste maat wordt aangeduid met r ∞ en gedefinieerd als het maximale aantal operaties per seconde dat voor een uit te voeren probleem door de machine gehaald kan worden. Het zal duidelijk zijn dat een bovengrens hiervoor gegeven wordt door de inverse cycle tijd van de machine, oftewel 1 r ∞ = ---tc
(152)
De tweede maat wordt meestal aangeduid als de n 1/2 en wordt gedefinieerd als die lengte van de vector waarbij de helft van de maximale verwerkingssnelheid gehaald wordt. Uit vergelijking (151) is eenvoudig af te leiden dat geldt t0 n 1/2 = ---- – 1 tc
(153)
Gezien de opstarttijd van de vector processor zal gelden dat er een bepaalde minimale lengte van de vector nodig is om het break even point ten opzichte van scalaire verwerking te bereiken. Definiëren we de versnelling ten opzichte van scalaire verwerking als ts ⁄ tc ts ⁄ tc T scalair nt s T ( n ) = ------------------- = -------------------------------- = -------------------------- ≈ ----------------t0 n – 1 t0 T vector t 0 + ( n – 1 )t c ------- + ------------ ------- + 1 nt c n nt c
(154)
dan wordt het break even point gegeven door T ( n min ) ≥ 1 wat resulteert in t0 n min ≥ -------------ts – tc
(155)
Het bovenstaande resultaat laat zien dat vector verwerking niet in alle gevallen voordelig hoeft te zijn maar dat er een bepaalde minimale lengte nodig is om het break-even point met scalaire verwerking te bereiken. De hiervoor gegeven overwegingen zijn op zich zelf relevant voor een gebruiker van vectormachines maar hij zal eerder geneigd zijn om zich af te vragen wat het overall voordeel is van vector processing voor zijn speciale probleem. In eerste benadering is daar een antwoord op te geven dat bekend staat als de wet van
Inleiding Natuurkundige Informatica
160
1997
Architecturen
High Performance Computing
Amdahl. In woorden kan men de wet van Amdahl formuleren als: “het traagste proces is snelheidsbepalend”. In formulevorm kan men de wet als volgt formuleren. Stel een programma bestaat uit N instructies waarvoor de volgende relatie geldt: N = Ns + Nv
(156)
waarbij N s het aantal instructies is dat scalair uitgevoerd wordt, en N v het aantal instructies dat vectorieel uitgevoerd kan worden. Gegeven de definities van sectie 4.2.2, dan geldt voor de tijd nodig voor scalaire verwerking ( T s ) en scalaire/vectoriële verwerking ( T v ) T s = ( N s + N v )t s T v = N sts + N vtv
(157)
Het quotiënt van beide kan men definiëren als de versnelling S Ts 1 S = ------ = ------------------------------Tv tv ( 1 – f ) + f ---ts
(158)
waarbij de vectoriseerbare fractie f = N v ⁄ ( N v + N s ) . In Figuur 150 wordt een grafische representatie gegeven van (158). Wat opvalt is dat voor niet te grote frac16
Vector/Scalar ratio
14
2 4 8
Speedup
12
16
10
8
6
4
2
0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0
Factorized fraction Figuur 150
Snelheidsverbetering als functie van de verhouding vector/scalar processing en de fractie vectoriseerbare code. Dit resultaat staat ook wel bekend als de wet van Amdahl.
ties vectoriseerbare code de snelheidswinst weinig varieert als functie van de verhouding tussen scalaire en vectoriële processing. Dit wordt pas manifest bij hele hoge percentages vectoriseerbare code. Mede hierdoor is men geneigd om soms drie gebieden te onderscheiden:
Inleiding Natuurkundige Informatica
161
1997
Architecturen -
High Performance Computing
0 ≤ f ≤ 0.5 dominant scalair, 0.5 ≤ f ≤ 0.8 vector/scalar balance, 0.8 ≤ f ≤ 1. dominant vector.
Ook vector processing is met name afhankelijk van de kwaliteit van de compiler/hardware combinatie. Het vectoriseren is vrijwel een autonome activiteit van de compiler geworden waarvoor automatische rapporten eenvoudig gemaakt kunnen worden. Na een aanvankelijk optimisme over de mogelijkheden van vector processing is de werkelijkheid wat weerbarstiger gebleken. De problemen waren met name gelegen in het feit dat de vectoren in de praktijk veel korter bleken te zijn dan men gehoopt en verwacht had. Een meer gedetailleerdere behandeling van vector processing ligt buiten de orde van dit college. Wij volstaan ermee om er op te wijzen dat in principe alle matrix/vector operaties zoals lineaire systemen, numerieke integraties etc. zich lenen voor vectorisatie.
6.3.3
Parallel computing (MIMD)
Met name het laatste decennium is de aandacht voor het parallelle rekenen sterk toegenomen. Dit is voor een groot deel te danken aan het voorhanden zijn van krachtige microprocessoren tegen (extreem) lage prijzen. In vergelijking met andere technologieën staat het parallelle rekenen pas in zijn kinderschoenen. Voor een groot deel is het nog handwerk om optimale strategieën te vinden. Dat is niet verwonderlijk als men bedenkt dat grote delen van onze wiskunde strikt sequentieel zijn. Als men als voorbeeld denkt aan het oplossen van een stelsel vergelijkingen door LU decompositie, dan ziet met dat het terugsubstitueren van de oplossingen sequentieel moet gebeuren. In tegenstelling tot super scalar en vector rekenen is bij parallel rekenen de orde van de versnelling niet beperkt tot één grootte orde maar in principe lineair afhankelijk van het aantal gebruikte processoren. De ongebreidelde rekenkracht is nodig voor een aantal zeer rekenintensieve toepassingen die men de ’grand challenges’ noemt (Figuur 151). Het doel dat voor het jaar 2000 gesteld is, is het doorbreken van de Tera Flop grens. Gezien de huidige ontwikkelingen lijkt dit doel haalbaar, zoals ook aangegeven is in tabel 29. De behandeling van parallelle systemen is hier alleen maar verkennend om enige trends aan te geven. Parallel computing wordt meestal ingedeeld naar de zogenaamde granulariteit. Hieronder verstaat men het quotiënt van runtijd van een processor ( t R ) en de daarvoor benodigde communicatie tijd ( t C ). Het quotiënt Q is een maat voor de zogenaamde task granularity: bij coarse grain parallellisme is Q groot, wat impliceert dat er veel rekenwerk gedaan wordt ten opzichte van de daarbij behorende communicatie, terwijl bij fine-grain parallellisme Q (zeer) klein is, wat een grote overhead betekent. Deze twee verschillende typen van activiteiten kunnen ook afgebeeld worden op verIBM SP/2 Clock cycle Peak per proc. Max. peak Memory # of procs.
Cray YMP
Parsytec SN9000
15 ns
4 ns
12.5 ns
266 MFlop
1 GFlop
80 MFlop
32 GFlop
16 GFlop
5 GFlop
< 2048 MB
< 16 GB
(16-128 Mb)
8 - 512
2 - 16
8-64
tabel 29Overzicht van enige massaal parallele systemen en hun karakteristieken.
schillende architecturen. die algemeen bekend staan onder de namen ’shared memory’ en ’distributed memory’ architecturen. Bij de laatst genoemde die schematisch getekend is in Figuur 152 heeft iedere processor zijn eigen geheugen. In sommige gevallen betreft het zelfs een compleet systeem. Al die systemen
Inleiding Natuurkundige Informatica
162
1997
Architecturen
High Performance Computing
1000
Vereiste rekenkracht (GFlop)
Grand Challenges klimaat modellering quantum chromo dynamica halfgeleider modellering supergeleiding onderzoek vloeistof dynamica
100
10
YULSI design
72 uur weer
1 48-uur weer
chemische dynamica
Schatting massa Higgs boson
3-d plasma
0.1 2-D plasma modellering
Figuur 151
2000
1990
1980
De ’grand challenges’ conform het overzicht van IEEE computer 1991.
processor 0
processor 1
proces-
processor
sor ..
n
Memory
Memory
Memory
Memory
0
1
...
n
Interconnection Network
Figuur 152
Voorbeeld van een zogenaamde ’distributed memory’ machine: iedere processor heeft zijn eigen geheugen. De topologie van de verbindingen wordt hier in het midden gelaten.
worden verbonden door een netwerk waarover de systemen met elkaar kunnen communiceren. Een dergelijk structuur wordt gekenmerkt door een grote opstarttijd voor de communicatie en is alleen zinvol als de granulariteit hoog is. Alleen voor het lokale geheugen geldt dan dat het ’snel’ dat wil zeggen binnen een paar processor cycles op te halen is, voor andere geheugenplaatsen moet via het netwerk gecommuniceerd worden wat soms in de orde van een milliseconde duurt. Voor een machine met een nano seconde klok staat dit gelijk met 1 miljoen operaties. De IBM SP serie is een typische vertegenwoordiger van deze aanpak. De Cray YMP vertegenwoordigt de andere aanpak: het shared memory. Hierbij geldt wel dat alle proces-
Inleiding Natuurkundige Informatica
163
1997
Architecturen
High Performance Computing
soren alle geheugen plaatsen snel kunnen bereiken mits die op dat moment niet geconsulteerd worden door andere processoren. Zonder op de constructie in te gaan zal duidelijk zijn dat dit hardwarematig moeilijk realiseerbaar is waardoor shared memory machines beperkt in aantallen processoren en vooral kostbaar zijn. Anders geformuleerd: de distributed memory machines hebben de meeste toekomst mogelijkheden, maar Processor
Processor
Memory
Processor
Processor
Figuur 153
Schematisch voorbeeld van een shared memory machine.
zijn niet in alle gevallen succesvol inzetbaar. Het numerieke integreren van sektie 5.3 is daar een goed voorbeeld van. Voor grote aantallen punten kan men de integraal opsplitsen in onafhankelijke deelsommen maar dat is alleen maar zinvol op een shared memory architectuur. Formeel kan men voor een vereenvoudigde situatie hier een relatie voor afleiden. Zij gegeven een proces dat bestaat uit M verschillende deelprocessen. Zij voorts een totaal van N processoren gegeven waarbij elke processor k i deelprocessen krijgt, terwijl natuurlijk geldt dat N
M =
∑ ki
(159)
i=1
Dan geldt door de totale executie tijd van dit proces tC t total = t R MAX(k i) + ----2
∑ ki( M – ki)
(160)
i
waarbij we aannemen dat er puntsgewijze verbindingen bestaan tussen alle processorparen. De eerste term correspondeert met de executie tijd van de langst durende deeltaak terwijl de tweede term de overhead van de communicatie in rekening brengt. Ten aanzien van de processoren zullen wij aannemen dat zij identiek zijn en volledig ter beschikking staan. De beste verdeling lijkt dan om aan alle processoren evenveel deelprocessen toe te wijzen. Dit impliceert dat M k i = ----N
(161)
Als wij voor voorwaarde (161) formule (160) evalueren en corrigeren voor de tijd die nodig is om alle processen op één processor te draaien resulteert dit in
Inleiding Natuurkundige Informatica
164
1997
Architecturen
Netwerken 2
2
tC M tR M tC M ∆t = ---------- + ------------- – ------------- – t R M 2N 2 N
(162)
Het break-even point kan berekend worden door vergelijking (162) nul te stellen en op te lossen. Het is eenvoudig in te zien dat dit de volgende oplossing levert: tR M Q ≡ ----- = ----tC 2
(163)
Hieruit blijkt dat als de Q waarde groter is dan M/2 een gelijkmatige verdeling van de taken over de processoren resulteert in de snelste verwerking. Indien Q echter kleiner is dan deze drempelwaarde is verwerking op één processor het voordeligste. Tevens is op grond van formule (160) eenvoudig uit te rekenen wat de versnelling in eerste benadering zal zijn. Deze wordt gegeven door tR N ⁄ tC Speedup = -------------------------------------------------------(tR ⁄ tC + M ( N – 1) ⁄ 2)
(164)
Voor kleine waarden van M en N blijkt uit (164) dat de versnelling evenredig is met het aantal processoren. Voor grote waarden van M en N echter wordt de versnelling evenredig met t R ⁄ ( t C M ) , wat onafhankelijk is van het aantal processoren N! Hier benadert de versnelling dus een asymptotische limiet. De les die hieruit getrokken kan worden is dat parallelliseren zeker niet altijd voordelig is. Voorts dient benadrukt te worden dat deze hardwarematige problemen slechts één kant van de medaille zijn: ook de software moet in staat zijn om de taken op de juiste manier op te splitsen.
6.4
Netwerken
Het is onmogelijk om in een overzicht van ontwikkelingen in de dataverwerkende industrie niet in te gaan op het aspect van netwerken. Het Internet, ’s werelds grootste netwerk, is van een kleine 6000 nodes eind 1986 gegroeid tot 600.000 nodes 5 jaar later, en dreigt gezien de huidige toename uit het 32-bits adres te groeien. De basisoverweging achter netwerking is dat een logisch systeem fysiek uit meerdere systemen kan bestaan die door een netwerk verbonden zijn. Als het netwerk én snel én transparant genoeg is, zal een gebruiker niet in staat zijn om te beoordelen wat de eigenlijk structuur is van het systeem.
6.4.1
Enige basis begrippen
Alle systemen die verbonden zijn aan een netwerk zullen we aanduiden met de naam node. Dit kan een werkstation zijn, een PC maar ook een grote parallelle machine. De naam netwerk zullen wij reserveren voor de structuur van de verbinding van de nodes. Wanneer twee nodes met elkaar willen communiceren kunnen zij dat doen op twee verschillende manieren. In het geval van circuit switching wordt er eerst een (fysieke) verbinding gemaakt tussen de betrokken nodes, waarna de communicatie zonder vertraging over de verbinding plaatsvindt. Een telefooncentrale is in principe een voorbeeld hiervan. De verbinding blijft bestaan voor de logische duur van de informatie overdracht. Bij message/packet switching wordt de communicatie tussen twee (of meer) nodes opgedeeld in stukjes die elk voor zich verzonden worden. Voor dit geval hoeft er niet een fysieke verbinding te bestaan tussen de betrokken nodes. Deze manier van verzenden noemt men ’store-and-forward’. Indien de afstand tussen de twee nodes niet direct overbrugd kan worden is een directe verbinding onmogelijk terwijl store-and-forward hiervan ’niets’ merkt. De communicatie over een verbinding wordt gestandaardiseerd door een zogenaamd protocol, een verzameling van regels die het gedrag vastleggen. Binnen de netwerk ontwikkeling heeft de International Stand-
Inleiding Natuurkundige Informatica
165
1997
Architecturen
Netwerken
ard Organization (ISO) baanbrekend werk verricht met de definitie van het zogenaamde Open System Interconnect (OSI) Reference model. De kracht van dit model is dat het totale probleem van verbinding van system wordt opgedeeld in een aantal sub-problemen elk met een wel gedefinieerd taakomschrijving. Het volledige OSI model omvat zeven lagen en is aangegeven in Figuur 154. Voor de gebruiker is eigenlijk al-
Application Layer (user)
Presentation Layer (presentatie data aan applicatie) Figuur 154
De zeven lagen van het OSI Reference model
Session Layer (sessies tussen applicaties)
Transport Layer (end-to-end error detection and correction)
Network Layer (verbinding over netwerk)
Data Link Layer (betrouwbare overdracht data over fysieke verbinding)
Physical Layer (fysieke karakteristieken van het netwerk) leen de applicatielaag van belang, al het andere is in principe onzichtbaar. Vanwege de stack achtige structuur van dit soort protocollen refereert men hier ook naar als een stack of een protocol stack. Het OSI model is overigens ook een uitstekende leidraad voor de opzet van programmatuur.
6.4.2
TCP/IP
Te midden van de vele netwerk software is TCP/IP (Transmission Communication Protocol/Internet Protocol) de de facto standaard geworden. Wij zullen ons in het vervolg beperken tot deze standaard. De ontwikkeling van TCP/IP is verbonden met de ontwikkeling van het zogenaamde ARPANET. Dit project werd in 1969 geïnitieerd door het ’defense advanced research projects agence’ (DARPA) in een poging om de mogelijkheden en eigenschappen van een multi vendor netwerk te onderzoeken. In 1975 werd de experimentele fase afgesloten en kwam het netwerk in produktie. Deze overgang werd ge-
Inleiding Natuurkundige Informatica
166
1997
Architecturen
Netwerken
stimuleerd door het grote succes van het netwerk. Het TCP/IP protocol werd als militaire standaard (pas) in 1983 vastgesteld en alle hosts die aan het netwerk verbonden waren werden verplicht om deze standaard over te nemen. Teneinde deze overgang te vereenvoudigen gaf DARPA opdracht aan Bolt, Beranek en Newman (BBN) om TCP/IP in Berkely (BSD) Unix te implementeren. Dit vormde de start van de verbinding tussen Unix en TCP/IP. De term Internet stamt uit de tijd dat TCP/IP ontwikkeld werd. Een teken van het succes van het netwerk is de verwarring rond de naam internet. In het begin was deze naam exclusief gereserveerd voor een netwerk dat op IP gebaseerd was. Nu wordt de naam internet (kleine i) gebruikt als aanduiding voor een collectie van verschillende netwerken die via een gemeenschappelijk protocol verbonden zijn. De naam Internet (grote I) wordt daarentegen gebruikt voor het wereldwijde netwerk dat gegroeid is uit het ARPANET en dat IP gebruikt als protocol. Er bestaat enige verwarring over hoe men TCP/IP binnen een gelaagd model zou moeten beschrijven maar Figuur 155 vertegenwoordigt een structuur waarover een zekere mate van consensus bestaat. Informatie die
Application Layer (user)
Host-to-Host transport layer (end-to-end data exchange)
Figuur 155
Verschillende lagen in het TCP/IP protocol
Internet Layer (routing of data)
Network Access Layer (access of physical networks)
verzonden moet worden over het netwerk wordt opgedeeld in pakketten die gegarandeerd en foutloos over het net worden verzonden. Zonder in te gaan op de details van een dergelijke methode van communicatie is het duidelijk dat dit een aantal slagen vereist. Laat men de eis van de betrouwbaarheid vallen, m.a.w. wil men als gebruiker zelf controleren of boodschappen aankomen of gaat men er van uit dat de verbinding absoluut betrouwbaar is, dan kan er ook van andere protocollen gebruik gemaakt worden. User Datagram Protocol (UDP) is een dergelijk protocol dat wel snel is maar geen garantie geeft over de aankomst. Fundamenteel voor de netwerking is het probleem van de adressering. Bij IP wordt aan elke node een uniek adres toegekend. Dit adres bestaat uit twee delen: een netwerk gedeelte en een node gedeelte. Het totale adres beslaat 32-bits wat betekent dat 4 G systemen in principe gekoppeld kunnen worden. Anno 1997 beginnen de adressen uitgeput te raken en is men hard aan het werk aan een 64-bits adres. Het binnenadres is onderverdeeld in drie klasses, A, B, en C. De klasses verschillen door de hoeveelheid nodes die zij kunnen accomoderen: klasse A kan 224 verschillende nodes adresseren, klasse C maar 28. De VU als geheel heeft een klasse B adres (130). Binnen de adressering is ook nog een sub-adressering mogelijk die bits, gereserveerd voor de nodes verder kan opdelen in sub-klassen. Binnen de VU zijn zo aan elke faculteit een of meer sub-netten toegedeeld, de faculteit Natuurkunde heeft twee van dergelijke sub-netten. Het zal weinig nader betoog behoeven dat het toekennen van IP adressen ook een hiërarchisch proces is.
Inleiding Natuurkundige Informatica
167
1997
Architecturen
Netwerken
Figuur 156
IP adres structuur
Klasse A 8 network bits
24 node bits
Klasse B 16 network bits
16 node bits
Klasse C
24 network bits
8 node bits
Voor het contact tussen twee nodes is nu ’alleen nog maar’ het vinden van de juiste route, ook wel aangeduid als het ’routing’ probleem van belang. Essentieel hiervoor zijn de zogenaamde gateways die verschillende netwerken met elkaar verbinden. Als een pakketje in TCP/IP verzonden wordt bevat het altijd het IP adres van zowel de zender als de geadresseerde. De routing is simpel -
Als de geadresseerde in het lokale netwerk zit, dus hetzelfde netwerkadres heeft, dan kan het direct afgeleverd worden, Als de geadresseerde niet in het lokale netwerk zit dan wordt de boodschap gestuurd naar de lokale gateway.
Hier wordt het probleem recursief opgelost, de gateway moet maar weten hoe hij het beste de geadresseerde kan bereiken. Die informatie is niet statisch opgeslagen in de gateways maar wordt dynamisch bijgehouden in de vorm van de zogenaamde routing tables. 32 bits getallen blijven voor mensen toch niet de meest hanteerbare grootheden, vandaar dat een mapping of een ’symbolische naam’ vaak toegepast wordt. Het (sub) netwerk wordt dan de zogenaamde domeinnaam terwijl de node een nodename krijgt. Deze afbeelding van namen op nummers wordt bijgehouden in het file /etc/hosts. De inhoud van de routing tabellen kan de gebruiker ook te zien krijgen. Een voorbeeld hiervan # # Sun Host Database # # If the NIS is running, this file is only consulted when booting # 127.0.0.1 localhost loghost # 130.37.36.10 euterpe 130.37.36.2 laurel 130.37.36.2 laurel 130.37.32.2 laurel-gw
Figuur 157
Voorbeeld van een IP name afbeelding in het /etc/hosts file van een machine (euterpe)
is gegeven in Figuur 158. Voor grotere systemen kunnen deze routing tables aanzienlijk zijn.
Inleiding Natuurkundige Informatica
168
1997
Architecturen
Netwerken
Route Tree for Protocol Family 2: (root node) default 130.37.36.3 127 127.0.0.1 130.37.32 130.37.32.7 130.37.36 130.37.36.5 (root node) Figuur 158
UG U U U
2 65771 en0 5 1867401 lo0 8 332700 en1 30 2183966 en0
Routing table van een systeem (gonzo), verkregen met het commando netstat -nr
Op deze manier is het mogelijk (bijna) transparant voor de gebruiker boodschappen uit te wisselen tussen twee systemen en processen op die systemen. Met name in het laatste geval spreekt men ook vaak over sockets als een gestandaardiseerde vorm van de zogenaamde Inter Proces Communicatie (IPC).
6.4.3
Network layer: Ethernet
Binnen de IP adressering zoals hierboven aangegeven is nog geen aanname gemaakt over de aard van de fysieke verbinding tussen nodes. De IEEE standaard 802.3, beter bekend als Ethernet heeft zich in de afgelopen jaren tot een zeer succesvolle vorm van fysieke verbinding ontwikkeld. Het basis idee achter Ethernet is een één bits verbinding in de vorm van een coax kabel. Aan de kabel wordt gekoppeld door middel van
Figuur 159
Voorbeeld van een netwerk met 4 systemen
disk
transceiver
zogenaamde ’transceivers’ die het eigenlijke elektrische contact maken. Elke transceiver wordt gekarakteriseerd door een nummer, het zogenaamde Ethernet adres. Aan één kabel is maximaal plaats voor 1024 transceivers. De methode van communicatie over de kabel is dat, elektrisch gezien, bit voor bit over kabel gezet wordt gedurende een zodanig lange tijd dat elk station dat verbonden is aan het Ethernet tijd gehad heeft om het te kunnen horen. Deze tijd wordt bepaald door de propagatiesnelheid van het elektrische signaal in de kabel. De tijd op de kabel is onderverdeeld in discrete intervallen, zogenaamde slots. Een slot wordt als een ondeelbare eenheid beschouwd waarbinnen de communicatie tussen zender en ontvanger ononderbreekbaar verloopt. Na een dergelijk slot wordt er bepaald wie de kabel kan gebruiken. Op Ethernet is geen arbiter aanwezig zodat er een wel gedefinieerd protocol gevolgd moet worden. Het bij Ethernet gebruikte protocol staat bekend onder Carrier Sense Collision Detect (CSCD). De betekenis hiervan is dat een station dat iets wil verzenden eerst ’luistert’ of de kabel vrij is (Carrier Sense). Is hij vrij dan neemt het station de kabel, anders wacht hij een willekeurige tijd (!). Waarna hij het opnieuw probeert. Is de kabel dan weer bezet dan wordt de wachttijd verdubbeld. Dit procédé wordt 10 maal herhaald waarna aangenomen wordt dat er iets verkeerd is met de kabel. In principe is het mogelijk dat twee stations tegelijk gaan zenden over de kabel. In dat geval zullen zij constateren dat hun data verminkt wordt (collision detect), stoppen met zenden en beide een willekeurige (verschillende) tijd wachten. Dit laatste is van belang om te voorkomen dat het systeem in oscillatie komt. De pakketten die over het Ethernet uitgewisseld kunnen worden hebben een maximale lengte, zodat een gebruikers eenheid (bijvoorbeeld één file) zelden correspondeert met één pakket op het Ethernet.
Inleiding Natuurkundige Informatica
169
1997
Architecturen
Computer Instrument koppeling
De verbinding tussen het IP adres en het Ethernet adres wordt gevormd door het zogenaamde Adres Resolution Protocol (ARP) dat in staat is om, voor lokale nodes, een IP adres om te zetten in een Ethernet adres. Daarmee kunnen de onderste twee lagen van figuur 155 op pagina 167 ingevuld worden want voor een vereuterpe% arp gonzo.nat.vu.nl gonzo.nat.vu.nl (130.37.36.5) at 2:60:8c:2e:f7:95
binding wordt nu éénmalig eerst het IP adres vertaald in een Ethernet adres wat vervolgens herhaald gebruikt wordt. Het voordeel van Ethernet is zijn conceptuele eenvoud en het gemak waarmee het aan te leggen is. Beperking zijn de bandbreedte (zie tabel 26 op pagina 156) en het gebrek aan arbitrage.
6.5
Computer Instrument koppeling
De technische levensduur van een computersysteem is op het ogenblik drie jaar, de economische levensduur is niet veel langer. Dat overwegend is het duidelijk dat een direct koppeling van een meetinstrument aan een computer onpraktisch is: (te) snel zal men gedwongen zijn om het werk over te doen. De ontwerper van programmatuur maakt een dramatische fout als hij karakteristieken van acquisitie hardware direct opneemt in een gebruikersprogramma. Hoe logisch dit ook moge klinken: het aantal programma’s dat uitgaat van b.v. de aanwezigheid van een VT100 terminal is legio, en ook acquisitie programma’s gaan nog vaak uit van de mogelijkheid van directe koppeling aan bijvoorbeeld een computerbus. Verhoging van het abstractie niveau is noodzakelijk om hieraan te ontkomen. Die ontwikkeling is niet nieuw. Op het gebied van de instrumentatie bussen hebben IEEE, SCSI en met name door de PC (AT-bus) bijgedragen aan standaardisatie, ieder op hun eigen wijze. Op het gebied van apparatuur is met name de invloed van het object georiënteerde paradigma van belang. Onder interface of instrumentatie bus verstaan wij een aan een computer koppelbare externe bus die het elektrisch mogelijk maakt om apparaten daaraan te verbinden. Wil dit uitwisselbaar zijn dan dient gestandaardiseerd te worden op de volgende punten: -
Mechanisch, omvattend de definitie van vormfactoren van connectoren en kabels. Electrisch, omvattend de definitie van aard en duur van de elektrische signalen. Functioneel, omvattend de werking van de interface, triggers e.d Operationeel omvattend een definitie van de werking van de aangesloten apparaten.
De instrumentatie bus dient alleen als verbinding van het systeem en de (randapparatuur) maar legt niets vast over de inhoud van de informatie die over de bus uitgewisseld wordt. Daarvoor is een (abstracter) model vereist.
6.5.1
IEEE-488.1: hardware
Het werk aan de standaardisatie van de instrumentatiebus is met name door Hewlett Packard geïnitieerd in de 60er jaren met de ontwikkeling van een instrumentatie bus, de HPIB (Hewlett Packard Interface Bus) ook wel bekend als GPIB (General Purpose Interface Bus). Deze werd in 1975 overgenomen door de Amerikaanse IEEE organisatie en een jaar later door ANSI erkend onder de norm IEEE-488/1975. Een aanpassing van de vorm heeft plaats gevonden in 1987 onder invloed van de snelle technische ontwikkelingen resulterend in de huidige standaard IEEE-488.1. IEEE-488.1 is één van de best ontworpen standaarden op het gebied van interfacing die overgenomen is door 200 fabrikanten en geïntegreerd in meer dan 2000 apparaten. Een principeschema van IEEE-488.1 is aangegeven in Figuur 160. De standaard beperkt zich tot het apparaat onafhankelijke gedeelte en omvat dus de eerste drie onderdelen van de standaard: mechanisch, elektrisch en functioneel. In Figuur 161 is één en
Inleiding Natuurkundige Informatica
170
1997
Architecturen
Computer Instrument koppeling
Talker Transmit data only
Listener Receive data only
Controller Send and receive data
Talker/Listener Send and receive data
Bus Management (5)
Handshake (3)
Bus
Bidirectionale data bus (8)
IEEE-488.1
Instrument independent
Figuur 160
Instrument dependent
IEEE-488.1 standaard
Leverancier
ander getekend voor een praktische situatie: zowel de computer als het meetapparaat (de voltmeter) hebben een interface kaart die koppelt aan hun (verschillende) interne structuur.Deze twee interfaces zijn verbonden door de bus. Softwarematig gezien is er een programma (bibliotheek) nodig om de interface kaart op Interface
V-
Interface
bus
V~
mAFiguur 161
Toepassing van IEEE 488 in een experimentele opstelling
de juiste manier aan te sturen. Dit wordt meestal de driver genoemd. Bus structuur De basis van IEEE-488.1 is de definitie van de bus die uit 24 aders bestaat, waarvan er 8 voor aarde gereserveerd zijn, met 8 aders voor data, 3 voor de handshake en 5 voor bus management. In tabel 30 worden de lijnen gegeven. Over de IEEE-488.1 bus kan dus een byte in parallel verstuurd worden. De logica die op de kabel gebruikt wordt is zogenaamde negatieve TTL, d.w.z. dat false (0) alles is groter dan 2.0 V terwijl true (1) alles is kleiner dan 0.8 V. De lengte van de kabel is beperkt: maximaal 15 devices mogen aangesloten worden op de kabel met niet meer dan 2 meter kabel per instrument en een maximum van 20 meter. De devices mogen lineair, stervormig, of in combinaties van beide verbonden worden aan de kabel.
Inleiding Natuurkundige Informatica
171
1997
Architecturen
Computer Instrument koppeling
Group Databus
Handshake
Management
Mnemonic
Description
DIO 1 - 8
Data bits
NRFD
Not ready for data
DAV
Data Available
NDAC
Not Data Accepted
EOI
End Of Information
REN
Remote Enable
ATN
Attention
IFC
Interface Clear
SRQ
Service request
tabel 30Overzicht van de IEEE 488 data lijnen.
Informatie De informatie die over de bus uitgewisseld wordt valt uiteen in twee categorieën: boodschappen voor de interface zelf en boodschappen voor het device dat verbonden is met de interface. De eerstgenoemde categorie wordt door de interface afgevangen en geïnterpreteerd, de tweede soort wordt ‘zonder meer’ doorgegeven aan het device. Het verschil tussen beide typen boodschappen wordt bepaald door de status van de ATN lijn. Devices Over de aard van de devices die verbonden zijn aan de bus worden weinig veronderstellingen gemaakt. De rol die zij spelen kan echter verschillen. De IEEE standaard onderscheidt: talker, listener en controller. Naast deze functionele onderscheiding worden devices ook gekenmerkt door een adres. Dit wordt meestal handmatig op het device door middel van dip-switches ingesteld. Een listener is een device dat in staat is om data te ontvangen (een printer is een typisch voorbeeld van een listener). De eerste boodschap die vervolgens op de bus gezet gaat worden is het (hardware) adres van de listener. Meerdere listeners mogen hetzelfde adres hebben (er kunnen meerdere printers aan dezelfde kabel zitten). De manier waarop de actuele communicatie over de bus plaats heeft zullen we nog verder behandelen. Teneinde chaos te voorkomen op de bus kan er maar één talker tegelijk zijn. In tegenstelling tot bijvoorbeeld Ethernet is er op de IEEE bus wel sprake van arbitrage: alleen de controller kan een talker aanwijzen. De beperking die hieraan opgelegd wordt is dat er maar één actieve controller en actieve talker tegelijk op de bus kan zijn. De controller kan zelf ook talker zijn. Voor het talken van een apparaat wordt dezelfde sequentie in principe gevolgd. De talker herkent zijn preferent status doordat zijn adres voorkomt in het bericht dat door de controller wordt verzonden. Daarna weet de talker dat hij de bus mag gebruiken totdat een einde gekomen is aan de transmissie. Het starten van communicatie kan door de controller gebeuren maar ook door de devices aan de bus. Hiervoor dient de SRQ, service request. Met deze lijn kunnen devices aangeven dat zij communicatie willen bedrijven. Aangezien er meerdere devices aan de bus verbonden kunnen zijn die allemaal de SRQ lijn kunnen bedienen, moet de controller nadat hij geconstateerd heeft dat de status van de lijn veranderd is, eerst uitzoeken welke device(s) om aandacht vragen. Dit testen van de devices noemt men een poll. De actie die de controller onderneemt op de resultaten van de poll zijn voor ‘zijn rekening’ en hangen af van de snelheid waarmee gehandeld moet worden. Als een device SRQ uitzendt omdat door gebrek aan geheugen de gemeten data verloren kunnen gaan is het noodzakelijker om sneller te handelen dan wanneer een systeem alleen
Inleiding Natuurkundige Informatica
172
1997
Architecturen
Computer Instrument koppeling
maar aangeeft dat het zijn data klaar heeft. De controller heeft uiteindelijk de controle over de aan de bus verbonden apparaten en hun status. Handshake Bijzonder aan de IEEE-488.1 bus is de handshake waarmee de data uitgewisseld worden. Laten we aannemen dat een ‘source’ informatie wil uitwisselen met een ‘destination’. In het totaal zijn bij de IEEE handshake drie controle lijnen in gebruik. De eerste, DAV, wordt door de source gebruikt om aan de destination duidelijk te maken dat de signaallijnen gebruikt kunnen worden en vormt dus als het ware de start van de cyclus. Daarop antwoorden de sources met de lijn NRFD om aan te geven dat zij in staat zijn om de data te ontvangen. De laatste stap wordt gemaakt door het vrijgeven van de NDAC lijn om aan te geven dat het byte ontvangen is. Als meerdere apparaten luisteren naar de lijn zal de totale lijn NDAC pas vrijgegeven worden door het laatste device dat het byte ontvangen heeft. In tegenstelling tot andere systemen blijft hier dus de informatie op de bus staan tot de laatste en het traagste device de data geaccepteerd heeft. Daarna herhaalt de cyclus zich zolang totdat alle data overgestuurd zijn tussen source en destination. Een consequentie hiervan is dat voor transfers over de IEEE een lineaire relatie geldt tussen de benodigde tijd en het aantal overgezonden bytes t transfer = t 0 + nt 1
(165)
waarbij t0 de opzettijd is die ligt, afhankelijk van de implementatie en leverancier, tussen de 1 en 10 milliseconden, en t1 de per byte transfer tijd is die tussen de 1 en 10 micro seconde ligt. De maximale bandbreedte van IEEE-488.1 is 1 Mbyte/s maar die wordt zelden in applicaties van enige omvang gehaald. Het voordeel van het ‘strakke’ protocol is dat men verzekerd is van de correcte aankomst van data aan de destination kant. Met name in vergelijking met het vaak gebruikte RS 232 protocol - dat eigenlijk ontwikkeld is voor communicatie tussen een toetsenbord en een computer - kan dit voordeel niet overschat worden.
6.5.2
IEEE-488.1: Software
Zo gestandaardiseerd als de IEEE-488.1 software bus is, zo woest en ledig is het software veld. De device driver die in de meeste gevallen meegeleverd wordt zorgt voor een eerste primitieve afbeelden van de basale IEEE commando’s op hogere programmeertalen. In Figuur 162 wordt een voorbeeld gegeven van een aanchar Result[132]; /* * clear the interface to address 6 * SendIFC(6); DevClear(6); Send(6, “*RST;VDC;RANGE 2;*trigger 2;TRG;VAL?”, NLend); Receive(6, Result, STOPend); Figuur 162
Voorbeeld van een programmeermodel zoals gebruikt door National instruments voor de aansturing van een IEEE-488 device (digitale multimeter)
sturing vanuit C. In de aanroepen van de C functies zijn de primitieve IEEE commando’s herkenbaar. Zodra de meter op het gebruiker adres 6 echter vervangen zal worden door een ander zal dit programma niet meer werken. In de ‘Send’ string is duidelijk de structuur van de toetsen op het frontpaneel van de meter herkenbaar. Het resultaat van de meting wordt tenslotte als afbeelding van de display strings overgestuurd.
6.5.3
IEEE-488 en verder
De variatie die men op instrumentatiebussen vindt is met name gelegen in de verschillende oplossingen voor
Inleiding Natuurkundige Informatica
173
1997
Architecturen
Computer Instrument koppeling
het probleem van de arbitrage, bandbreedte en setup tijd van een bus. Een aantal alternatieven bestaat en karakteristieken zijn gegeven in tabel 26 op pagina 156. Voor de instrumentatie zijn, in het Unix domein, naast IEEE-488 SCSI, VME en CAMAC de meest gebruikt opties. De populariteit van SCSI komt voort uit haar aanwezigheid op vrijwel elk systeem voor het aansluiten van (externe) disks. SCSI verschilt op een aantal punten van IEEE-488. De meest belangrijkste zijn dat bij SCSI een prioriteitschema bestaat, wie het laagste adres heeft wordt het snelst geholpen, en er is de mogelijkheid om een transfer tijdelijk af te breken. Dit impliceert dat als de source ontdekt dat de destination nog wel even nodig zal hebben met het prepareren van de data, de bus tijdelijk vrijgegeven kan worden voor gebruik door anderen. VME en CAMAC zijn meer computerbussen die gekenmerkt worden door een (extreem) kleine opzettijd van een transfer en hoge bandbreedtes. Tussen veel bussen bestaat tegenwoordig een soort ‘verloop’ stukken die het mogelijk maakt om ze te koppelen. Zo wordt binnen de faculteit op het ogenblik of de SCSI poort van een werkstation gebruikt om via een converter omgezet te worden in een IEEE bus of er wordt direct op systeembus van het werkstation een (gebufferde) interface aangesloten.
6.5.4
SCPI Instrument model
Teneinde te kunnen komen van een sensor naar een meetinstrument op een gestandaardiseerde manier die aansluit bij of aansluitbaar is op geautomatiseerde systemen is een abstract model vereist. Merkwaardig genoeg heeft het aan een dergelijk model tijden lang ontbroken, en zelfs nu nog is de vooruitgang mager te noemen. Baanbrekend werk op dit gebied wordt door met name Hewlett-Packard verricht. Drijvende kracht is om programmatuur optimaal portable te houden: conformering aan een instrumentatie model moet dit garanderen. De belangrijke poging in deze richting is het zogenaamde SCPI model (Standard Commands for Programmable Instruments) dat in 1991 geaccepteerd is. In de definitie van SCPI, zoals aangegeven in FiSignal Routing
Measurement Function Trigger
Signal Routing Figuur 163
Format
data bus
Memory
Signal Generation
Format
data bus
SCPI (Standard Commands for Programmable Instruments) instrumentatiemodel.
guur 163 is er sprake van een abstract instrumentatie model waar op een hoog niveau commando’s aan gegeven kunnen worden. Van dit model wordt niet noodzakelijk geeist dat het zich in één behuizing bevindt, het mag ook over een netwerk verspreid zijn. Geassocieerd met dit model is een syntax definitie voor operaties. Deze zijn voor een deel voorgedefinieerd. Aangezien een dergelijke lijst niet uitputtend is bestaat er ook de mogelijkheid om een aktie vast te leggen in een programma, dat vervolgens in het geheugen van het meetinstrumenten te ‘downloaden’ en het vervolgens te draaien. Het SCPI model leent zich goed voor visuele representatie. HP-VEE en LabView maken daar dankbaar gebruik van om een omgeving op te zetten die speciaal voor data-acquisitie kan dienen.
Inleiding Natuurkundige Informatica
174
1997
Referenties
Referenties 1. Almasi, G.S., Gottlieb, A. (1994) Highly Parallel Computing. Second edition. Benjamin Cummings. 2. J.D. Foley, A. van Dam, S.K. Feiner en J.F. Hughes, (1996) Computer graphics, Principles and Practice. Second edition. Addison Wesley. 3. Fowles, G.R., Cassiday, G.L. (1993) Analytical Mechanics. Fifth edition. Saunders. 4. Kernighan, B.W., Ritchie, D.M. (1988) The C Programming Language. Second edition. Prentice Hall. 5. Wolfram, S. (1996) The Mathematica Book. Third edition. Cambridge University Press. 6. Zimmerman, R. L., Olness, F. I. (1995) Mathematica for Physics; Addison Wesley 7. Mead, C.A. ,Conway, L. A. (1980) Introduction to VLSI systems. Addison Wesley 8. Press, W.H., Teukolsky, S.A., Vetterling, W.T., Flannery, B.P. (1992) Numerical Recipes in C. Second edition. Cambridge University Press.
Inleiding Natuurkundige Informatica
175
1997
Trefwoorden
Trefwoorden ADC
Analoog naar Digitaal Converter. Apparaat dat een continue signaal discretiseert.
ADRES
Eén-één duidige karakterisatie van een bepaalde geheugenplaats.
ALGORITME Verzameling van regels volgens welke een probleem opgelost wordt. BANDBREEDTE Maximale hoeveelheid informatie die over een communicatie kanaal uitgewisseld kan worden. BAUD
Eenheid van informatie.
BENCHMARK (Software) maat voor de prestatie van een computer systeem. BIT
COARSE GRAIN Parallellisme waarbij de rekentijd groot is ten opzicht van de communicatietijd. COGNITIEVE AFSTAND Maat voor het verschil tussen een probleemstelling en haar oplossing in een bepaalde PSE. COMPILER Vertaalprogramma dat een hogere programmeertaal omzet in machine code die direct uitvoerbaar is. CONDITIEGETAL Getal dat aangeeft hoe gevoelig de oplossing van een stelsel vergelijking is voor veranderingen in de elementen. CONTROL FLOW Executie model waarbij (extern) de executievolgorde bepaald wordt.
Basis eenheid van binaire informatie CYCLE
BUS
Lineaire verbindingsstructuur tussen componenten van een computer.
BYTE
Geordende set van (meestal) acht bits.
C
Derde generatie programmeertaal.
C++
Object georiënteerde opvolger van C.
CACHE
geheugen dat in een cycle een antwoord kan produceren.
DATA ACQUISITIE Het geautomatiseerd meten van gegevens. DATA FLOW Executie model waarbij de beschikbaarheid van data de volgorde van executie bepaalt.
CANCELLATION ERROR Optreden van numerieke onnauwkeurigheden door normalisatie van floating points. CIRCUIT SWITCHING Communicatie methode waarbij een fysieke verbinding bestaat tussen zender en ontvanger voor de duur van de informatie uitwisseling. CISC
Processor architectuur waarbij ernaar gestreefd wordt om zo’n klein mogelijke afstand te hebben tussen 3 gl statements en machine instructies.
Inleiding Natuurkundige Informatica
Tijd die verstrijkt tussen twee opeenvolgende pulsen van de klok en geldt als kortste synchronisatie periode.
176
DATA HIDING Principe uit object georiënteerd programmeren waarbij informatie alleen via functies bereikbaar is. DEVICE Algemene benaming voor een apparaat. DOUBLE PRECISION Aanduiding van berekening die met twee maal de standaard nauwkeurigheid uitgevoerd worden. DYNAMISCH PROGRAMMEREN Vorm van algoritme waarbij getracht wordt een probleem op te lossen door optimalisatie van een kostenfunctie.
1997
Trefwoorden ELAPSED TIJD Tijd die een programma nodig heeft om te draaien gemeten volgens de wall klok. ESSL
Engineering and Scientific Subroutine Library (IBM specifiek).
EXECUTIE MODEL Verzameling van regels volgens welke een programma uitgevoerd wordt.
Representatie van een geheel getal. Het bereik is beperkt door de grootte van het woord dat gereserveerd is. INTERPRETER Programma dat een input programma vertaalt en statement voor statement uitvoert. LIFO
EXECUTIE TIJD Tijd die nodig is om een bepaald programma te voltooien; men onderscheid usertijd, system tijd en elapsed tijd. FIFO
INTEGER
First In First Out. Algoritme waarbij de orde van afhandeling door de orde van binnenkomst bepaald wordt.
MACHINE NAUWKEURIGHEID Intrinsieke fout die gemaakt wordt bij representatie van niet-gehele getallen. MACHINE TAAL Verzameling van instructies die een gegeven stuk hardware direct kan uitvoeren.
FINE GRAIN Parallellisme waarbij de verhouding tussen rekentijd en communicatie tijd klein is. FLOATING POINT Representatie van een niet-geheel getal. Meestal conform IEEE-754 standaard.
MFLOP
Maat voor de prestatie van een systeem, uitdrukkend hoeveel floating point operaties er per seconde uitgevoerd kunnen worden.
MIP
Maat voor de prestatie van een systeem, uitdrukkend hoeveel integer operaties er per seconde uitgevoerd kunnen worden.
OCTET
Gestandaardiseerde verzameling van 8bits. Wordt vaak aangeduid als byte.
FORTRAN(77) Derde generatie programmeertaal. FORTRAN 90 Object georiënteerde opvolger van Fortran 77. GPIB
Last In First Out. Algoritme waarbij het laatst ingekomen item het eerst afgehandeld wordt, zoals bij recursie.
OPERATOR OVERLOADING Mechanisme waarbij de betekenis van operatoren als +, * etc door de gebruiker gehederdefinieerd kan worden.
General Purpose Instrumentation Bus Zie IEEE-488.
HANDSHAKE Gecoördineerde wijze van overdracht van informatie tussen source en destination.
ORDE
Meestal de afhankelijkheid van de executie tijd van een algoritme van zijn input parameters.
HPC
Aanduiding van hardware en software methoden om superscalaire prestaties te krijgen.
PAGE
Basiseenheid in het virtueel geheugen systeem.
HPIB
Hewlett-Packard Instrumentation Bus. Zie IEEE-488.
POINTER Datastructuur die doorverwijst naar een bepaalde hoeveelheid informatie.
IEEE488 interface standaard voor data acquisitie. IMSL
International Mathematical and Statistical Libraries.
Inleiding Natuurkundige Informatica
177
1997
Trefwoorden REAL-TIME Aanduiding van systemen die binnen een extern gedefinieerde tijd in staat zijn om een bepaalde handeling uit te voeren, of te constateren dat de handeling niet uitgevoerd kan worden. RECURSIE Programmeertechniek waarbij een probleem in termen van zichzelf opgelost wordt. REGISTER Geheugen plaats die zich op de CPU bevindt. RISC
SEED
Processor architectuur waarbij ernaar gestreefd wordt om de clock cyclus van de CPU maximaal te reduceren. Startwaarde van een random-generator.
USER TIJD De tijd die een programma nodig heeft om te draaien, gecorrigeerd voor de belasting van het systeem, i.e. alsof het systeem onbezet was. VERDEEL-EN-HEERS Methode waarbij de orde van een probleem door opsplitsing verlaagd wordt. WALL CLOCK Benaming voor de ’echte’ tijd die verloopt. WOORD Kleinste hoeveelheid informatie die op een systeem direct adresseerbaar is. Woord kan variëren van systeem tot systeem WYSIWIG Acroniem voor What You See Is What You Get. Benaming van type software waarbij de gebruiker direct ziet wat het resultaat is van zijn akties.
SEEKTIJD Tijd die een arm van een disk nodig heeft om zich mechanisch te positioneren. SPARSENESS Verhouding tussen niet-nul elementen en totaal aantal elementen van een matrix. STACK
Lineaire datastructuur waarvoor het FIFO principe geldt.
SUPER SCALAR ARCHITECTUUR Processor structuur (RISC) waarbij meer dan één instructie per cycle uitgevoerd kan worden. STATEMENT Kleinste basiseenheid van een programma. TRANSDUCER Apparaat dat een fysische grootheid omzet in een elektrisch signaal. TRANSLATOR Vertaalprogramma dat source code van de ene naar de andere programmeertaal kan omzetten.
Inleiding Natuurkundige Informatica
178
1997
Trefwoordenregister
Trefwoordenregister
CPU 141, 143 CPU-bound 133 Cray 6 Cray YMP 162 cross-compiler 45 curvature scale 96 cycle tijd 142, 158
A Adaptieve quadratuur 111 ADC 24 alfabet 27, 33 Amdahl 161 and 28 ANSI-C 49 ARPANET 166 array 121 ASCII code 72 assembler 39 AVL bomen 84 Axiom 53
D data 140 Databus 172 Dataflow 44 DEC 7 DEC VAX 11/780 137 Devices 172 DFT 36 Diepte 89 Disk 152 disk controller 153 distributed memory machines 164 Double-precision 77 Double-precision extended 77 Drystone 136, 148 dubbel gelinkte lijst 86 Dynamisch programmeren 122
B Backus 40 bandbreedte 155 benchmarks 137, 148 Besselfuncties 104 betrouwbaarheid 105 binair 70 bit 33, 70, 146 Boole 27 Boolse Algebra 27 Boolse algebra 23 bpi 154 branch prediction 145 branches 88 bus 156 Bus structuur 171
E EBCDIC 72 emax 77 emin 77 ENIAC 6, 8 entropie 22, 33 Ethernet 156, 169 exclusive or 28 Exponentiële groei 9
C C 12 C Shell 43 C++ 12, 44, 50 cache 146, 148 calloc 84 CAMAC 156 cancellation error 79, 96 CCD 26 CERN 14 circuit switching 165 CISC 143 client/server 42 coarse grain 162 Cobol 12 Comma Codering 36 Commando taal 43 compilatie 44 compiler 40 Compressie 33 conditie getal 129 connectiviteit 155
Inleiding Natuurkundige Informatica
F F90 44 fine-grain 162 fixed point arithmetic 75 floating point 75, 76 Flynn 140 Fortran 12 Fortran77 50 free 84 G Gauss eliminatie 125 Geheugen 146 genormaliseerd 76 getrusage 134 grootste gemene deler 102 GUI 47 H Handshake 172 hexadecimaal 70 High Performance Computing 157 hogere programmeertalen 40
179
1997
Trefwoordenregister Hufman code 35
MFlop 137 MIMD 140, 162 minmax procedure 121 MIP 137 MISD 140 Moore 9 wet van 9 most-significant bit 70 Motorola 10 Motorola 68000 7 Motorola 680xx 143 MPEG 34 MTBF 8, 10
I I/O 141 IBM 360 7 IBM 801 144 IBM PowerPC 144 IBM SP/2 9, 162 IBM ThinkPad 9 IEEE 23 IEEE 754 77 IEEE-488 156 IEEE-488.1 170 Illiac IV 7 IMTC 23 Informatie 172 instructies 140 Intel 9 Intel x86 143 Internet 165 interpretatie 44 interpreter 53 interrecord gap 154 ISO 166
N Natuurkundige Informatica 19 netwerken 165 Niet-procedurele taal 43 normalisatie 76 not 28 Nyquist 26 O object 40 object georiënteerde taal 40, 44 octaal 70 octet 147 onderhoudbaarheid 105 or 28 orde 105, 106 OSI 166 overflow 29 overhead 121
J Jacobi methode 126 Java 44 JCL 43 JPEG 34, 36 K K&R C 49 Kernighan 49 kostenfunctie 122 kwantisatiefout 25
P packet switching 165 page 151 page fault 151 Parallel computing 162 Parsytec 162 peak performance 136 Performance 132 Pipelining 144 POP 92 portabiliteit 105 prestatie 132 Problem Solving Environment 42 Procedurele taal 43 Prolog 12 protocol 165 PUSH 92
L latency 155 least-significant bit 70 LIFO 91 Lineaire stelsels 124 linear congruential method 116 Linked List 84 Linpack 137 LISP 12 locality-of-reference 146 Logicals 30 LRU 151 LSB 25 M machine nauwkeurigheid 78 machinetaal 39, 143 mantisse 76 Mathematica 12, 53 MatLab 53 memory 141 memory reference 146
Inleiding Natuurkundige Informatica
Q quantum interval 25 R radix 70, 76 Random 115 recording density 154
180
1997
Trefwoordenregister recursie 122 Recursieve algoritmes 108 Registers 148 Rent 11 reverse polish 92 RISC 143, 144 Ritchie 49 robuustheid 105 root 88 rotational delay 152
Userinterface 46 user-tijd 133 V Vector Processoren 158 vector registers 158 Verdeel-en-heers 119 virtueel geheugen 150 virtuele instrumenten 17 VME 156 Von Neumann 140 Von Neumann cyclus 141 Von Neumann machine 141
S sample frequentie 24, 26 sample resolutie 24 sample tijd 24 SARA 8 SCPI 174 SCSI 156 sectoren 152 seed 116 seektijd 152 Sensors 23 SET/RESET latch 29 setup tijd 158 SIMD 140, 158 Simpson 114 simulatie 17, 18 Single-precision 77 Single-precision extended 77 SISD 140 software crisis 12 sparseness 126 SPEC 138 SQL 12 stack 91 stack overflow 92 stack underflow 92 state full 29 state less 29 SUN SPARC 144 super scalar processor 157 Systeem programmeertalen 43 systemtijd 133
W wall-clock 132 Weerstandsnetwerk 130 Wetenschappelijke talen 43 Whetstone 136, 138 woord 142, 144 write-back 149, 150 write-through 149 X xor 29 XWindows 48
T T(n) 120 Tape 153 task granularity 162 TCP/IP 166 terminal branches 88 thrashing 152 time 133 Topologie 155 tracks 152 U Unix 49
Inleiding Natuurkundige Informatica
181
1997