Grundlegende Datentypen



Ähnliche Dokumente
4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes.

1 Mathematische Grundlagen

Grundbegriffe der Informatik

Algorithmen und Datenstrukturen 3. Vorlesung

Lernziele: Ausgleichstechniken für binäre Bäume verstehen und einsetzen können.

Semantik von Formeln und Sequenzen

Programmiersprachen und Übersetzer

Grundlagen der Informatik. Prof. Dr. Stefan Enderle NTA Isny

Primzahlen und RSA-Verschlüsselung

Folge 19 - Bäume Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12

Objektorientierte Programmierung

Programmierkurs Java

Zeichen bei Zahlen entschlüsseln

Objektorientierte Programmierung. Kapitel 12: Interfaces

1 topologisches Sortieren

SEP 114. Design by Contract

Einführung in die Algebra

Software Engineering Klassendiagramme Assoziationen

1. Man schreibe die folgenden Aussagen jeweils in einen normalen Satz um. Zum Beispiel kann man die Aussage:

Also kann nur A ist roter Südler und B ist grüner Nordler gelten.

Würfelt man dabei je genau 10 - mal eine 1, 2, 3, 4, 5 und 6, so beträgt die Anzahl. der verschiedenen Reihenfolgen, in denen man dies tun kann, 60!.

Informationsblatt Induktionsbeweis

7 Rechnen mit Polynomen

WS 2009/10. Diskrete Strukturen

Beweisbar sichere Verschlüsselung

Einführung in die Programmierung

Kapitel 6. Komplexität von Algorithmen. Xiaoyi Jiang Informatik I Grundlagen der Programmierung

Systeme 1. Kapitel 6. Nebenläufigkeit und wechselseitiger Ausschluss

Fachdidaktik der Informatik Jörg Depner, Kathrin Gaißer

Data Mining: Einige Grundlagen aus der Stochastik

Verhindert, dass eine Methode überschrieben wird. public final int holekontostand() {...} public final class Girokonto extends Konto {...

Beispiel Zusammengesetzte Zufallsvariablen

Einführung in die Java- Programmierung

Grundlagen der Theoretischen Informatik, SoSe 2008

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Vorlesung Diskrete Strukturen Graphen: Wieviele Bäume?

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Grammatiken. Einführung

Der Zwei-Quadrate-Satz von Fermat

50. Mathematik-Olympiade 2. Stufe (Regionalrunde) Klasse Lösung 10 Punkte

Einführung. Vorlesungen zur Komplexitätstheorie: Reduktion und Vollständigkeit (3) Vorlesungen zur Komplexitätstheorie. K-Vollständigkeit (1/5)

a n auf Konvergenz. Berechnen der ersten paar Folgenglieder liefert:

15 Optimales Kodieren

Einrichten eines Postfachs mit Outlook Express / Outlook bis Version 2000

Motivation. Formale Grundlagen der Informatik 1 Kapitel 5 Kontextfreie Sprachen. Informales Beispiel. Informales Beispiel.

In diesem Thema lernen wir die Grundlagen der Datenbanken kennen und werden diese lernen einzusetzen. Access. Die Grundlagen der Datenbanken.

Übung Theoretische Grundlagen

Die Beschreibung bezieht sich auf die Version Dreamweaver 4.0. In der Version MX ist die Sitedefinition leicht geändert worden.

Fachschaft Mathematik und Informatik (FIM) LA I VORKURS. Herbstsemester gehalten von Harald Baum

Erstellen einer digitalen Signatur für Adobe-Formulare

Verwendung des IDS Backup Systems unter Windows 2000

Abamsoft Finos im Zusammenspiel mit shop to date von DATA BECKER

Wollen Sie einen mühelosen Direkteinstieg zum Online Shop der ÖAG? Sie sind nur einen Klick davon entfernt!

Theoretische Informatik SS 04 Übung 1

Kurzanleitung zur Bereitstellung von Sachverhalten und Lösungen zum Universitätsrepetitorium auf dem Server unirep.rewi.hu-berlin.

Analysis I für Studierende der Ingenieurwissenschaften

So die eigene WEB-Seite von Pinterest verifizieren lassen!

Übungen Programmieren 1 Felix Rohrer. Übungen

WS 2008/09. Diskrete Strukturen

Über Arrays und verkettete Listen Listen in Delphi

Theoretische Grundlagen der Informatik

Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung: Lösungsvorschlag

Individuelle Formulare

Einrichtung des Cisco VPN Clients (IPSEC) in Windows7

Folge 18 - Vererbung

Fakultät Angewandte Informatik Lehrprofessur für Informatik

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang

Grundbegriffe der Informatik

Second Steps in eport 2.0 So ordern Sie Credits und Berichte

5.2 Neue Projekte erstellen

Terme stehen für Namen von Objekten des Diskursbereichs (Subjekte, Objekte des natürlichsprachlichen Satzes)

Virtueller Seminarordner Anleitung für die Dozentinnen und Dozenten

Algorithmen & Datenstrukturen 1. Klausur

Praktische Mathematik: Lineare und Netzwerk-Optimierung (SS 2015) Praktikumsaufgaben

Erstellen von x-y-diagrammen in OpenOffice.calc

Modellierung und Programmierung 1

Einführung in. Logische Schaltungen

Theoretische Grundlagen des Software Engineering

Klausur WS 2006/07 Programmiersprache Java Objektorientierte Programmierung II 15. März 2007

Berechnungen in Access Teil I

Division Für diesen Abschnitt setzen wir voraus, dass der Koeffizientenring ein Körper ist. Betrachte das Schema

Basis und Dimension. Als nächstes wollen wir die wichtigen Begriffe Erzeugendensystem und Basis eines Vektorraums definieren.

geben. Die Wahrscheinlichkeit von 100% ist hier demnach nur der Gehen wir einmal davon aus, dass die von uns angenommenen

3. Zusammenhang. 22 Andreas Gathmann

8: Zufallsorakel. Wir suchen: Einfache mathematische Abstraktion für Hashfunktionen

Vorkurs Informatik WiSe 15/16

10 Erweiterung und Portierung

OUTLOOK (EXPRESS) KONFIGURATION POP3

Datensicherung. Beschreibung der Datensicherung

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

In diesem Tutorial lernen Sie, wie Sie einen Termin erfassen und verschiedene Einstellungen zu einem Termin vornehmen können.

Lineare Gleichungssysteme

Rekursionen (Teschl/Teschl )

Fallbeispiel: Eintragen einer Behandlung

Grundlagen Theoretischer Informatik I SoSe 2011 in Trier. Henning Fernau Universität Trier fernau@uni-trier.de

ERGÄNZUNGEN ZUR ANALYSIS II MITTELWERTSATZ UND ANWENDUNGEN

ecaros2 - Accountmanager

Cookies. Krishna Tateneni Jost Schenck Übersetzer: Jürgen Nagel

3.2 Binäre Suche. Usr/local/www/ifi/fk/menschen/schmid/folien/infovk.ppt 1

Musterlösungen zur Linearen Algebra II Blatt 5

Transkript:

Foliensatz 4 Michael Brinkmeier Technische Universität Ilmenau Institut für Theoretische Informatik Sommersemester 2009 TU Ilmenau Seite 1 / 50 Grundlegende Datentypen TU Ilmenau Seite 2 / 50

Atomare Datentypen Die meisten höheren Programmiersprachen, kennen verschiedene atomare 1 Datentypen. Für uns sind das im Wesentlichen die folgenden: boolean Wahrheitswert, zwei mögliche Werte: true/false, 0/1 integer ganze Zahlen, Z real Reelle Zahlen, R char ASCII oder Unicode Zeichen pointer Zeiger auf Speicherbereiche Auf realen Rechnern sind die Typen in der Regel mit Größen- oder Genauigkeitsbesschränkungen versehen ( z.b. short/int/long, float/double ). 1 griech.: atomos unteilbar TU Ilmenau Seite 3 / 50 Zusammengesetzte Datentypen Mittels verschiedener Operationen können weitere Datentypen konstruiert werden. Zu ihnen gehören: Verbünde struct; Kombinationen von mehreren Daten Felder oder Arrays int[5]; Listen fester Länge, in denen jeder Eintrag denselben Typ hat Diese Arten von Datentypen und der Umgang mit ihnen wird als bekannt vorausgesetzt. TU Ilmenau Seite 4 / 50

Grundlegende Datenstrukturen Im Folgenden wollen wir uns mit einigen grundlegenden Datenstrukturen beschäftigen: Stacks Listen Queues Dynamische Mengen Bäumen Hashtabellen TU Ilmenau Seite 5 / 50 Die informale Beschreibung Die natürliche Sprache ist mit Sicherheit die einfachste Methode einen Datentyp zu beschreiben. Beispiel (Stack in natürlicher Sprache) Ein Stack (oder Stapel, Keller, LIFO last-in-first-out) ist wie ein Stapel von Gegenständen organisiert. Man kann immer nur einen neuen Gegenstand auf den Stapel drauf legen (push), den obersten Gegenstand vom Stapel betrachten (top) oder den obersten Gegenstand entfernen (pop). TU Ilmenau Seite 6 / 50

Die informale Beschreibung Alternativ kann man versuchen die Datenstruktur grafisch oder mit Hilfe von Beispielen zu beschreiben. Beispiel (Beschreibung eines Stacks mittels einer Graphik) TU Ilmenau Seite 7 / 50 Die informelle Beschreibung von Datentypen Die informale Beschreibung erweist sich im Allgemeinen als zu unpräzise. Eine Verbesserung stellt die informelle Beschreibung dar, bei der der Datentyp mittels einer konkreten Implementation in einer beliebigen Programmiersprache beschrieben wird. Vorteile: einfach und direkt Nachteile: verschleiert die wesentliche Funktionalität (eine Operation wird über den auszuführenden Quelltext definiert) erschwert Korrektheitsbeweise die gegebene Implementierung ist nicht universell nutzbar (Programmiersprache ist fixiert) fixiert bestimmte technische Entscheidungen (Eine andere Implementation könnte für die gegebene Aufgabe deutlich geeigneter sein) TU Ilmenau Seite 8 / 50

Abstrakte Datentypen Die Verwendung von Abstrakten Datentypen (ADT) erlaubt die Definition des Datentyps von seiner Implementierung zu trennen. Ein Abstrakter Datentyp besteht im Wesentlichen aus zwei Komponenten: einer Signatur und einer Semantik. Für beide Teile spielt die Implementierung keine Rolle. TU Ilmenau Seite 9 / 50 Signaturen Die Signatur eines ADT entspricht im Wesentlichen dem klassischen C-Header, bzw. den Deklaratoren. Sie beschreibt, welche Operationen im Zusammenhang mit dem ADT ausgeführt werden können. Definition (Signatur) Eine Signatur Σ = (S, O, domain, codomain) besteht aus einer Menge S von Sorten, die im Zusammenhang mit der ADT benötigt werden, einer Menge O von Operationen, zwei Abbildungen domain: O Seq(S) und codomain: O S wobei Seq(S) = {(S 1,...,S n ) n N, S 1,...,S n D} die Menge aller endlichen Tupel über S ist. TU Ilmenau Seite 10 / 50

Signaturen Ist Σ = (S, O, domain, codomain) eine Signatur, dann kann man eine Operation op O als Abbildung von ihrem Definitionsbereich (Domain, Domäne) domain(op) in ihren Wertebereich (Codomain) codomain(op) interpretieren. Notation Operationen beschreibt man häufig in der Form op: domain(op) codomain(op) Wenn domain(op) = (I 1,..., I n ) und codomain(op) = O schreibt man auch op: I 1 I n O. Die Forderung, dass jede Operation genau einen Ausgabetyp hat, ist auf technische Gründe zurück zu führen. TU Ilmenau Seite 11 / 50 Die Signatur eines Stacks Beispiel (Die Signatur eines Stacks) Sorten: Operationen: Elements Stacks Boolean empty : () Stacks push : Stacks Elements Stacks pop : Stacks Stacks top : Stacks Elements isempty : Stacks Boolean Bemerkung Der Definitionsbereich () von ist explizit erlaubt. Man spricht dann von Konstanten oder auch von Konstruktoren. TU Ilmenau Seite 12 / 50

Signaturen Bemerkung Wenn eine Operation den Inhalt eines Objektes verändert, muss das Objekt das Ergebnis der Operation sein! Bei den Signaturen handelt es sich um eine funktionale (applikative) Herangehesweise. Beispiel verändern beide den Stack. push: Stacks Elements Stacks pop: Stacks Stacks TU Ilmenau Seite 13 / 50 Algebren Eine Signatur Σ = (S, O, domain, codomain) ist lediglich eine Hülle, die die Syntax der Datenstruktur beschreibt. Es fehlt noch der Inhalt. Definition (Algebra) Sei Σ = (S, O, domain, codomain) eine Signatur. Eine Σ-Algebra A = (T, F) besteht aus einer Familie T = {T S S S} von Mengen und einer Familie F = { f op op O und f : T domain(op) T codomain(op) } von Abbildungen. Dabei ist T (S1,...,S n ) = T S1 T Sn. TU Ilmenau Seite 14 / 50

Algebren Anders gesagt: Eine Σ-Algebra A = (T, F) mit Σ = (S, T, domain, codomain) weist jeder Sorte S S eine Menge T S von Objekten dieser Sorte zu und jeder Operation op: I 1 I n O eine Abbildung f op : T I1 T In T O. TU Ilmenau Seite 15 / 50 Eine Algebra für Stacks Beispiel (Eine Algebra für Stacks) Elements = (nicht-leere) Menge D Boolean = {true, false} Stacks = Seq(D) Das leere Tupel () stellt den leeren Stack dar. TU Ilmenau Seite 16 / 50

Eine Algebra für Stacks Beispiel (Eine Algebra für Stacks Fortsetzung) empty() := () push((a 1,..., a n ),x) := (a 1,..., a n,x) ( steht für einen Fehler oder einen undefi- ((a 1,..., a n 1 ),a n ) falls n 1 pop((a 1,..., a n )) := nierten falls Wert. n = 0 ( (a 1,..., a n 1 ) falls n 1 pop((a 1,..., a n )) := falls n = 0 ( a n falls n 1 top((a 1,..., a n )) := falls n = 0 ( false falls n 1 isempty((a 1,..., a n )) := true falls n = 0 TU Ilmenau Seite 17 / 50 Axiome Um die Semantik einer Signatur festzulegen genügt es in der Regel nicht eine Algebra anzugeben. Häufig müssen zusätzlich bestimmte Gleichungen oder Axiome erfüllt werden. Beispiel (Die Axiomatik eines Stacks) s Stacks, x Elements: top(push(s, x)) = pop(push(s, x)) = isempty(empty()) = true isempty(push(s, x)) =false x s top(empty()) = pop(empty()) = TU Ilmenau Seite 18 / 50

Die Algebra und die Axiome Beispiel (Die Axiome und unsere Stack-Algebra) top(push((a 1,..., a n ), x)) = top((a 1,..., a n, x)) = x pop(push((a 1,..., a n ), x)) = pop((a 1,..., a n, x)) = (a 1,...,a n ) isempty(empty()) = isempty(()) = true isempty(push((a 1,..., a n ), x)) = isempty((a 1,...,a n, x)) = false TU Ilmenau Seite 19 / 50 Die Modellalgebra Hat man eine Signatur Σ und dazugehörige Axiome gegeben, so spricht man von einer algebraischen Spezifikation. Aus einer solchen algebraischen Spezifikation läßt sich eine Modellalgebra (oder auch mathematisches Modell) konstruieren (Stichwort: Quotiententermalgebra, AuP). Diese Modellalgebra hat einige sehr wichtige Eigenschaften, die bei dem Beweis der Korrekheit einer Implementierung helfen können. Unter Umständen lässt sich diese Modellalgebra auf elegante und einfach Weise beschreiben. Beispiel Die beschriebene Algebra für Stacks ist eine Modellalgebra für die gegebene algebraische Spezifikation. TU Ilmenau Seite 20 / 50

Algebraische Spezifikation eines ADT Vorteile: Universelle und vollständige Beschreibung Die axiomatische Darstellung ermöglicht automatisiere Beweise von Eigenschaften der Datenstruktur Nachteile: Das Finden eines geeigneten und vollständigen Axiomensystems ist in der Regel nicht einfach. Umsetzung in eine Implementation erfordert in der Regel einen weiteren Zwischenschritt über die Modellalgebra. Diese explizit zu beschreiben ist in der Regel ebenfalls sehr schwer. TU Ilmenau Seite 21 / 50 Implementierungen von Abstrakten Datentypen Das Konzept von Abstrakten Datentypen lässt sich in gewisser Weise in der Objektorientierung wiederfinden. Im Wesentlichen entspricht die Signatur der Definition der Klassen (z.b. in C++ und Java) und Interfaces. Abstrakte Klassen entsprechen direkt einer Signatur. Eine Implementierung entspricht einer Algebra. Sogar die abstrakte Definition von Signaturen mit nicht näher spezifizierten Sorten wie z.b. die Sorte Elements eines Stacks findet sich in modernen objektorientierten Sprachen in Templates wieder. TU Ilmenau Seite 22 / 50

Implementierungen eines Stacks Zur Realisierung eines Stacks über eine Menge D von Elementen, ergeben sich im wesentlichen zwei mögliche Implementierungen: Die Listenimplementierung Die Arrayimplementierung TU Ilmenau Seite 23 / 50 Die Listenimplementierung von Stacks TU Ilmenau Seite 24 / 50

Die Listenimplementierung eines Stacks Listen haben Sie schon in AuP kennengelernt. Nullzeiger S : a 1 a 2 a n empty LI : Erzeuge eine leere Liste. S : push LI (s, x): Füge am Anfang der Liste einen neues Element ein. S : x a 1 a 2 a n TU Ilmenau Seite 25 / 50 Die Listenimplementierung eines Stacks top LI (s): Gebe den Inhalt des ersten Listenknotens zurück, falls vorhanden S : a 1 a 2 a n pop LI (s): Entferne das erste Element der Liste. S : a 1 a 2 a 3 a n isempty LI (s): Gibt true zurück, falls die Liste leer ist, sonst false. TU Ilmenau Seite 26 / 50

Korrektheit der Listenimplementierung Da eine Modellalgebra für Stacks bekannt ist, kann die Korrektheit einer Implementierung auf einfache Art bewiesen werden. Dabei nutzt man das folgende Resultat: Sei A = (T, F) eine Modellalgebra von Σ = (S, O, domain, codomain) und I = (T I, F I ) eine Implementierung (interpretiert als Algebra). T S und f op seien die Mengen und Abbildungen in A und TS I und f op I die in I. I ist eine korrekte Implementierung von Σ oder A, wenn für jede Sorte S S eine Bijektion ϕ S : T S T I S existiert und für jede Operation op: I 1 I n O Folgendes gilt: ϕ O ( fop (x 1,..., x n ) ) = f I op (ϕ I1 (x 1 ),..., ϕ In (x n )). TU Ilmenau Seite 27 / 50 Korrektheit der Listenimplementierung Anders formuliert: Wir benötigen für jede Sorte S eine Bijektion ϕ S : T S TS I zwischen den Elementen der Sorte in der Modellalgebra und den Objekten der Sorte in der Implementation. Weiter müssen diese Bijektionen mit den Operationen kompatibel sein, d.h. im folgenden Diagramm ist es egal welchen der beiden Wege wir von der linken oberen Ecke zur rechten unteren Ecke nehmen. Beide führen zum selben Ergebnis. I 1 I n f op O ϕ I1 ϕ In ϕ O I I 1 II n f I op O I TU Ilmenau Seite 28 / 50

Korrektheit der Listenimplementierung Wie sieht nun die Bijektion zwischen Seq(D) und den Listen aus? Notation S : a 1 a 2 a n Das oben dargestellte Array notieren wir zur Abkürzung in der Form [a 1 a 2 a n ] Damit definieren wir ϕ((a 1,...,a n )) := [a n a n 1 a 2 a 1 ]. (Beachten Sie, dass die Reihenfolge umgekehrt wurde). Für Elements und Boolean gehen wir einfach von den Identitäten aus. TU Ilmenau Seite 29 / 50 Korrektheit der Listenimplementierung ((a 1,..., a n ), x) (a 1,...,a n, x) Seq(D) D push Seq(D) ϕ ϕ List < D > D List < D > push LI ([a n a 1 ], x) [x a n a 1 ] TU Ilmenau Seite 30 / 50

Korrektheit der Listenimplementierung (a 1,..., a n ) (a 1,...,a n 1 ) pop Seq(D) Seq(D) ϕ ϕ List < D > List < D > pop LI [a n a 1 ] [a n 1 a 1 ] TU Ilmenau Seite 31 / 50 Korrektheit der Listenimplementierung Auf ähnliche Weise kann man die übrigen Operationen auf Kompatibilität mit ϕ überprüfen. Satz Die beschriebene Listenimplementierung ist eine korrekte Implementierung des abstrakten Datentyps Stack. TU Ilmenau Seite 32 / 50

Zeitaufwand der Listenimplementierung Behauptung Jede Operation der Listenimplementierung hat Kosten O(1). Die Behauptung ist offensichlich korrekt. Allerdings erfordert push das Anlegen eines neuen Listeneintrages. Diese Operation (malloc in C, new in C++ und Java) ist auf realen Maschinen in der Regel deutlich teurer als der Zugriff auf einen Listeneintrag. Noch ein Problem Auf realen Maschinen kann push zu einem Speicherüberlauf führen. Im Beweis sind wir stillschweigend von einem unendlich großem Speicher ausgegangen. Die Korrektheit gilt also nur bis auf Speicherüberläufe. TU Ilmenau Seite 33 / 50 Die Arrayimplementierung von Stacks TU Ilmenau Seite 34 / 50

Die Arrayimplementierung von Stacks In der Arrayimplementierung besteht ein Stack s aus zwei Komponenten: einem Array A[1...m] der festen Größe m und einer Integervariablen p, genannt Pegel. s: p : n A : 1 a 1 a 2 n m a n TU Ilmenau Seite 35 / 50 Die Arrayimplementierung von Stacks empty(): A = new Elements[1...m] p = 0. push(s, x): if p == m then return else p = p + 1 ; A[p] = x pop(s): if p == 0 then return else p = p 1 ; return A[p + 1] top(s): if p == 0 then return else return A[p] isempty(s): if p == 0 then return true else return false TU Ilmenau Seite 36 / 50

Korrektheit der Arrayimplementierung Diesmal gehen wir einen anderen Weg für den Korrektheitsbeweis. Wir betrachten eine Folge (op 0 = empty,op 1,...,op N ) von Operationen auf demselben Stack mit op 1,...,op N empty. Im mathematischen Modell erzeugt diese Operationsfolge eine Folge von Tupeln mit s 0 = () und eine Folge (s 0, s 1,...,s N ) (z 0, z 1,..., z N ) von Ausgaben aus Elements {true, false} {, }, wobei die Ausgabe von empty und push ist und, falls ein Fehler auftritt (pop oder top auf leeren Stack). pop und top liefern das entsprechende Element als Ausgabe. TU Ilmenau Seite 37 / 50 Korrektheit der Arrayimplementierung Behauptung Die Arrayimplementierung erzeugt genau dieselbe Ausgabefolge wie das mathematische Modell, solange weder im mathematischen Modell ein Fehler auftritt (top ode pop auf leeren Stack), noch die Höhe des Stacks s i die Arraygröße m überschreitet. Beweis Man beweist per Induktion über i = 0, 1,..., N, dass nach Ausführen von op i die folgende Invariante gilt, sofern kein Fehler aufgetreten ist: p enthält die Länge n i von s i und s i = (A[1],...,A[n i ]). D.h. das Array A[1...n i ] stellt genau s i dar.... TU Ilmenau Seite 38 / 50

Korrektheit der Arrayimplementierung Beweis (Fortsetzung) Induktionsanfang (i = 0) : Nach op 0 = empty() hat p den Inhalt 0 und somit ist das Array A[1...p] leer, also gleich s 0 = (). Induktionsvoraussetzung: Die Behauptung gilt für i 1. Induktionsschritt: Wir müssen verschiedene Fälle unterscheiden: Falls s i 1 = d.h. im mathematischen Modell ist vorher ein Fehler aufgetreten ist nichts zu zeigen, da dieser Fall ausgeschlossen wurde. Falls in den Schritten 1,...,i 1 in der Implementierung ein Fehler aufgetreten ist, ist ebenfalls nichts zu zeigen. Wir können also s i 1 = (a 1,...,a ni 1 ) annehmen und p hat den Inhalt n i 1 und a j = A[j] für 1 j n i 1.... TU Ilmenau Seite 39 / 50 Korrektheit der Arrayimplementierung Beweis (Fortsetzung) Falls op i = push(s,x), ist s i = (a 1..., a ni 1, x). Wenn n i 1 < m, wird von der Prozedur push(s, x) das Objekt x an die Stelle A[n i 1 + 1] gesetzt und p auf den Wert n i 1 + 1 erhöht. Das impliziert die Induktionsbehauptung in diesem Fall. Wenn n i 1 = m ist, tritt in der Implementierung ein Fehler auf und die Behauptung ist trivialerweise erfüllt. Die anderen Operationen können analog behandelt werden (Übungsaufgabe). TU Ilmenau Seite 40 / 50

Die Laufzeit der Arrayimplementierung Behauptung Bei der Arrayimplementierung hat jede Operation die Kosten O(1). Die Behauptung ist wahr, falls bei empty() das Array lediglich angelegt, nicht aber initialisiert wird. Im letzten Fall, wäre die Laufzeit für empty gerade O(m). Da der Inhalt in A[p + 1...m] aber unerheblich ist, beeinflusst diese Einschränkung die Korrektheit jedoch nicht. TU Ilmenau Seite 41 / 50 Arrayimplementierung ohne Platzprobleme Frage Ist es möglich, die Begrenzung auf ein Array fester Länge zu beheben? Antwort Ja! Mittels der Verdoppelungsstrategie! Sobald während push(s, x) ein Überlauf eintritt d.h. das m + 1-te Element würde hinzugefügt werden verdoppeln wir die Größe des Arrays. Genauer: Legen ein neues Array B der Größe 2m an. Kopieren das alte Array A in die ersten m Felder des neuen Arrays B. Gebe A frei. Setze A = B, d.h. setze die Referenz um. TU Ilmenau Seite 42 / 50

Die Verdoppelungsstrategie s: p: m A: 1 a 1 a 2 m a m B : 1 a 1 a 2 m 2m a m TU Ilmenau Seite 43 / 50 Kostenanalyse der Verdoppelungsstrategie Frage Wieviele Verdoppelungen können auftreten? k sei die Anzahl der push-opertationen in unserer Folge und j die Anzahl der Verdoppelungen. Damit enthält der Stack stets k Elemente. Also gilt vor der letzten Verdoppelung m 0 2 j 1 < k denn sonst, müsste nicht verdoppelt werden, und somit j 1 < log k m 0 = log k log m 0. TU Ilmenau Seite 44 / 50

Kostenanalyse der Verdoppelungsstrategie Sei L = log k log m 0. Dann lassen sich die Gesamtkosten für die Verdoppelungen nach oben abschätzen: L O ( ( m 0 2 i 1) = O m 0 i=1 = O = O(k) L 1 i=0 2 i ) (m 0 2 log k m 0 ) = O = O ( m 0 2 L) ( m 0 k m 0 ) Gesamtkosten mit k push-operationen N O(1) + O(k) = O(N) TU Ilmenau Seite 45 / 50 Kostenanalyse der Verdoppelungsstrategie Satz Wenn ein Stack mit Arrays und der Verdoppelungsstrategie implementiert wird, sind die Gesamtkosten für N Operationen in O(N). Der gesamte Platzbedarf ist O(k), wenn k die maximale je erreichte Stackgröße ist. Selbst wenn unbenutzte Arrays nie freigegeben werden, tritt ein Speicherüberlauf erst dann ein, wenn die Zahl der Stackeinträge zu einem bestimmten Zeitpunkt mehr als 1 4 des zur Verfügung stehenden Speichers beansprucht (Übungsaufgabe). TU Ilmenau Seite 46 / 50

Amortisierte Analyse Eine Analyse der Kosten, wie wir sie gerade durchgeführt haben, nennt man Amortisierte Analyse. Idee hinter der amortisierten Analyse Eine Datenstruktur wird häufig nicht für eine Operation, sondern für eine Reihe von insgesamt N Operationen genutzt. Betrachtet man die Kosten der einzelnen Operationen, so ergeben sich unter Umständen sehr hohe Worst-Case Kosten, wie z.b. die Kosten von push bei einem Stack mit Verdoppelungsstrategie. Betrachtet man jedoch die gesamte Folge, so könnte es sein, das diese relativ hohen Kosten nur in bestimmten Situationen eintreten (z.b. wenn besonders viele billige push Operationen seit der letzten teuren durchgeführt wurden). Dadurch ergeben sich insgesamt niedrigere Kosten, als bei der Verwendung der Worst-Case Laufzeit. TU Ilmenau Seite 47 / 50 Amortisierte Analyse Amortisierte Kosten Benötigt man im schlechtesten Fall T(N) Zeit für N Operationen auf einer Datenstruktur, so benötigt jede Operation die amortisierten Kosten T(N) N. Wichtig! Amortisierte Kosten müssen klar von Average-Case Kosten unterschieden werden! Bei letzteren geht eine Wahrscheinlichkeitsverteilung ein, und man betrachtet die erwartete Laufzeit. Bei den amortisiertern Kosten handelt es sich um eine tatsächliche obere Schranke, d.h. N beliebige Operationen auf der Datenstruktur benötigen höchstens T(N) Zeit. Die amortisierten Kosten für jede einzelne Operation sind dann die entstandenen durchschnittlichen Kosten für jede Operation. TU Ilmenau Seite 48 / 50

Vergleich von Listen- und Arrayimplementierung Arrayimplementierung: O(1) Kosten pro Operation, amortisiert d.h. im Mittel über alle Operationen. Sequentieller, indexbasierter Zugriff auf den Speicher (cachefreundlich) Höchstens die Hälfte des allozierten Speichers ist ungenutzt Listenimplementierung: O(1) Kosten pro Operation im schlechtesten Fall Hohe Allokationskosten, da jedes Listenelement einzeln angelegt wird Zusätzlicher Platz für Zeiger benötigt TU Ilmenau Seite 49 / 50