BuK/IIm11/Studenten/sijeheid/hilbert
Aus ProgrammingWiki
Wiki-Quelltext dieser Seite.
<language>Scheme</language>
<language>Scheme</language>
== Hintergrund ==
Ein wirkliches Hotel verfügt über genau ''n'', also endlich viele Zimmer, und bietet folglich Platz für höchstens ''n'' Gäste.
Dem sehr bekannten Mathematiker ''David Hilbert'' (23.01.1862-14.02.1943) verdanken wir die Geschichte von einem Hotel mit ''unendlich'' vielen Zimmern. Wie viele Gäste können in dem "unendlichen Hotel" untergebracht werden?
Die Zimmer des unendlichen Hotels werden fortlaufend durchnummeriert. Darüber hinaus verabreden wir, dass die Nummerierung mit der Zahl 0 beginnt. Zur Vereinfachung gehen wir hier davon aus, dass das Hotel ausschließlich über Einzelzimmer verfügt.
== Arbeiten mit Streams ==
Um das Hilberthotel zu realisieren, ist es nötig, mit Streams zu arbeiten. In Scheme kann dies mit Hilfe der verzögerten Evaluation implementiert werden.
Dafür ist es zunächst nötig, einige Grundfunktionen zur Verfügung zu stellen. Diese basieren auf dem Buch Programmierparadigmen Seite 63 ff.
<code id="4f774a106346f">
;; Selektoren
(define stream-car car)
(define stream-cdr
(lambda (stm)
(force (cdr stm))))
;; Konstruktoren
(define-syntax stream-cons
(syntax-rules ()
((_ head tail) (cons head (delay tail)))))
;; Praedikat
(define stream?
(lambda (stm)
(and (pair? stm)(promise? (cdr stm)))))
;; Operationen mit streams
(define print-stream
(lambda (stm n)
(cond
((<= n 0) (display "...\n"))
(else
(display (stream-car stm))
(display ", ")
(print-stream (stream-cdr stm) (- n 1))))))
(define stream-item
(lambda (str k)
(if (= k 0)
(stream-car str)
(stream-item (stream-cdr str) (- k 1)))))
</code>
== Hilberthotel ==
Zunächst wird das Hilberthotel errichtet. Leere Zimmer werden mit "#f" gekennzeichnet.
<code id="4f774a1063a00">
(define baue-hilberthotel
(lambda ()
(stream-cons #f (baue-hilberthotel))
)
)
(define zeig-mir
(lambda(hotel n)
(print-stream hotel n)
)
)
(define hilberthotel (baue-hilberthotel))
</code>
Zeigt man sich die ersten 11 Zimmer an, so sieht man, dass bisher noch kein Gast eingezogen ist.
<run id="4f774a1063dbe">
(zeig-mir hilberthotel 11)
</run>
== einzelne Gäste ==
Als erstes ziehen einzelne Gäste in das Hotel ein. Diesen wird eine feste Zimmernummer zugewiesen. Bei der Buchung wird überprüft, ob das gewünschte Zimmer frei ist, wenn nicht, wird ein Fehler ausgegeben.
<code id="4f774a1063ac2">
(define einzelbuchung
(lambda (value stream index)
(if (<= index 0)
(if (eq? (stream-car stream) #f)
(stream-cons value (stream-cdr stream))
(error (string-append "Error in einzelbuchung: Dieses Zimmer ist schon durch " (stream-car stream) " belegt."))
)
(stream-cons (stream-car stream) (einzelbuchung value (stream-cdr stream) (- index 1)))
)
)
)
(set! hilberthotel (einzelbuchung "Otto Grundmann" hilberthotel 3))
(set! hilberthotel (einzelbuchung "Hella Milkel" hilberthotel 2))
(set! hilberthotel (einzelbuchung "Heribert Wuischke" hilberthotel 7))
</code>
Nachdem die ersten drei Gäste eingezogen sind, sieht die Zimmerbelegung folgendermaßen aus:
<run id="4f774a1063e80">
(zeig-mir hilberthotel 12)
</run>
== kleine Reisegruppe ==
Anschließend fährt ein Kleinbus mit acht Passagieren vor. Da immer freie Zimmer zur Verfügung stehen, wird der jedem Gast der Reihe nach das nächste freie Zimmer zugewiesen.
<code id="4f774a1063b65">
(define baue-kleinbus
(lambda (name count)
(if (> count 0)
(append (baue-kleinbus name (- count 1)) (list (cons name count)))
(list (cons name count))
)
)
)
(define kleingruppenbuchung
(lambda (liste stream)
(if (null? liste)
stream
(if (eq? (stream-car stream) #f)
(stream-cons (car liste) (kleingruppenbuchung (cdr liste) (stream-cdr stream)))
(stream-cons (stream-car stream) (kleingruppenbuchung liste (stream-cdr stream)))
)
)
)
)
(define zeig-mir-kleinbus
(lambda (liste)
(display liste)
)
)
(define kleinbus815 (baue-kleinbus 815 8))
(set! hilberthotel (kleingruppenbuchung kleinbus815 hilberthotel))
</code>
Die Passagiere des Kleinbusses treten in folgender Reihenfolge zum Check-In an:
<run id="4f774a1063f3a">
(zeig-mir-kleinbus kleinbus815)
</run>
Nach dem Bezug der Zimmer ergibt sich folgende Zimmerbelegung:
<run id="4f774a1064002">
(zeig-mir hilberthotel 15)
</run>
<language>Scheme</language>
<language>Scheme</language>
== Hilbertbus ==
Als nächstes kommt ein weiterer Bus gefahren. Diesmal handelt es sich um einen Hilbertbus. Das heißt, dass dieser eine unendliche Menge von Passagieren befördert.
Wie das Hotel, ist der Bus auch als Stream realisiert. Das Verfahren zum Check-In verläuft analog der kleinen Reisegruppe: Jedem Gast wird der Reihe nach das nächste freie Zimmer zugewiesen.
<code id="4f774a1063c09">
(define baue-hilbertbus
(lambda (name)
(letrec
((fabrik
(lambda (start)
(stream-cons (cons name start) (fabrik (+ start 1)))
)
))
(fabrik 0)
)
)
)
(define check-in
(lambda (bus-stream hotel-stream)
(if (eq? (stream-car hotel-stream) #f)
(stream-cons (stream-car bus-stream) (check-in (stream-cdr bus-stream) (stream-cdr hotel-stream)))
(stream-cons (stream-car hotel-stream) (check-in bus-stream (stream-cdr hotel-stream)))
)
)
)
(define bus3813 (baue-hilbertbus 3813))
(set! hilberthotel (check-in bus3813 hilberthotel))
(define bus33 (baue-hilbertbus 33))
</code>
Vor dem Check-In betrachten wir zunächst die Gäste:
<run id="4f774a10640ee">
(zeig-mir bus3813 8)
</run>
Die Zimmerbelegung nach dem Check-In gestaltet sich wie folgt:
<run id="4f774a10641b6">
(zeig-mir hilberthotel 20)
</run>
Es scheinen keine freien Zimmer mehr zu sein, doch da kommt noch ein Bus gefahren:
<run id="4f774a1064279">
(zeig-mir bus33 8)
</run>
Um den neuen Gästen trotzdem ein Zimmer anbieten zu können, müssen die bisherigen Gäste umziehen. Dabei wechselt jeder Gast aus Zimmer ''n'' in das Zimmer mit der Nummer ''2n''. Somit ist jedes zweite Zimmer wieder unbelegt.
<code id="4f78907d5331b">
(define gehe-in-gerade
(lambda (hotel-stream)
(stream-cons (stream-car hotel-stream) (stream-cons #f (gehe-in-gerade (stream-cdr hotel-stream))))
)
)
(set! hilberthotel (gehe-in-gerade hilberthotel))
</code>
Nach dem Umzug der bisherigen Gäste stehen wieder unendlich viele freie Zimmer zur Verfügung:
<run id="4f774a106433e">
(zeig-mir hilberthotel 30)
</run>
Nun können die neuen Gäste ihr Zimmer beziehen.
<code id="4f78907d533f5">
(set! hilberthotel (check-in bus33 hilberthotel))
</code>
Sobald alle Gäste ihr Zimmer bezogen haben, ergibt sich nachfolgende Zimmerbelegung:
<run id="4f774a1064424">
(zeig-mir hilberthotel 30)
</run>
== Hilbertbus-Konvoi ==
Um das ganze zu komplettieren, kommt nun ein Hilbertbus-Konvoi angefahren. Dieser besteht aus unendlich vielen Hilbertbussen. Nun soll jedem Gast aus diesem Konvoi auch ein Zimmer zugeteilt werden.
Zuerst wird der Konvoi erstellt. Dieser ist ein Stream von Hilbertbussen. Also Streams innerhalb eines Streams.
<code id="4f7893099718e">
(define konvoimacher
(lambda (name)
(letrec
((fabrik
(lambda (abteilung)
(stream-cons (baue-hilbertbus (string-append name (number->string abteilung))) (fabrik (+ abteilung 1)))
)
))
(fabrik 0)
)
)
)
(define zeig-mir-konvoi
(lambda (stream n m)
(if (<= n 0)
(if (<= m 0)
(display "...\n")
(begin
(display "..., ")
(zeig-mir-konvoi stream 0 (- m 1))
)
)
(begin
(print-stream (stream-car stream) m)
(zeig-mir-konvoi (stream-cdr stream) (- n 1) m)
)
)
)
)
(define k1 (konvoimacher "MH"))
</code>
Folgender Konvoi kommt angefahren (eine Zeile = 1 Bus):
<run id="4f7893099721e">
(zeig-mir-konvoi k1 4 8)
</run>
== Konvoi->Stream ==
Damit die Gäste des Konvois ihre Zimmer beziehen können, müssen sie der Reihe nach antreten. Da pro Bus unendlich viele Gäste befördert werden, können nicht zunächst nur Gäste eines Busses der Reihe nach einchecken, sondern alle Gäste (aus allen Bussen) müssen sich zu einer einzigen Kette zusammenschließen. So tritt zunächst Passagier 0 aus Bus 0, gefolgt von Passagier 0 aus Bus 1 und dann Passagier 1 aus Bus 0 usw. an.
Um für jeden Index der Kette den jeweiligen Bus und darin den entsprechenden Passagier zu ermitteln, wird die Cantorsche Paarungsfunktion angewandt. Damit ist es möglich, zwei abzählbar unendliche Mengen auf eine abzählbar unendliche Menge abzubilden und für jedes Element der neuen Menge den Index zu bestimmen. Mit Hilfe der Umkehrfunktion kann nun für den jeweiligen Index die Indizes der beiden Ursprungsmengen ermittelt werden. (x = Bus, y = Passagier innerhalb des Busses )
<code id="4f78971c01ba5">
(define calculate-t
(lambda (w)
(/ (* (+ w 1) w) 2)
)
)
(define calculate-w
(lambda (z)
(floor (/ (- (sqrt (+ (* 8 z) 1)) 1) 2))
)
)
</code>
<code id="4f78971c01c20">
(define zimmer
(lambda (nr stream)
(if (<= nr 0)
(stream-car stream)
(zimmer (- nr 1) (stream-cdr stream))
)
)
)
(define konvoi->kette
(lambda (konvoi)
(letrec
((platzanweiser
(lambda (z)
(letrec (
(hole-person
(lambda (busse bus-nr platz-nr)
(if (<= bus-nr 0)
(zimmer platz-nr (stream-car busse)) ;; zimmer-funktion hat gleiches Prinzip :-)
(hole-person (stream-cdr busse) (- bus-nr 1) platz-nr)
)
)
))
(let ((w (calculate-w z)))
(let ((y (- z (calculate-t w))))
(let ((x (- w y)))
(stream-cons (hole-person konvoi x y) (platzanweiser (+ z 1)))
)
)
)
)
)
))
(platzanweiser 0)
)
)
)
</code>
Nachdem das oben beschriebene Prinzip angewandt wurde, ergibt sich eine einzige Kette an Passagieren. Diese treten in folgender Reihenfolge zum Check-In an:
<run id="4f78971c01c9a">
(zeig-mir (konvoi->kette k1) 20)
</run>
Damit die neuen Gäste einchecken können, müssen die bisherigen Hotelgäste erneut umziehen, damit die ungeraden Zimmer frei werden. Danach beziehen die Konvoi-Gäste ihre Zimmer.
<code id="4f789838b013e">
(set! hilberthotel (gehe-in-gerade hilberthotel))
(set! hilberthotel (check-in (konvoi->kette k1) hilberthotel))
</code>
Nach dem Check-In ergibt sich folgende Zimmerbelegung:
<run id="4f789838b01a0">
(zeig-mir hilberthotel 42)
</run>
== Wer wohnt in Zimmer 234? ==
Auch wenn das Hotel unendlich viele Zimmer besitzt und bereits unendlich viele Gäste ein Zimmer gebucht haben, kann genau angegeben werden, wer in welchem Zimmer wohnt.
So ist folgender Gast in das Zimmer 234 eingezogen:
<run id="4f789838b01f1">
(zimmer 234 hilberthotel)
</run>