Skip to content

Commit 6bed734

Browse files
committed
Abschnitt "Maximaler Aktiengewinn".
1 parent 6d8a824 commit 6bed734

3 files changed

Lines changed: 233 additions & 18 deletions

File tree

i1akku.tex

Lines changed: 195 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,8 @@ \section{Zwischenergebnisse mitführen}
371371
372372
\section{Schablonen für Funktionen mit Akkumulator}
373373
374-
Auch für Funktionen mit Akkumulator wollen wir eine
375-
Konstruktionsanleitung entwickeln. Vorher wollen wir aber noch einmal
374+
Auch für Funktionen mit Akkumulator entwickeln wir eine
375+
Konstruktionsanleitung. Vorher wollen wir aber noch einmal
376376
anhand eines weiteren Beispiels Revue passieren lassen, wie der
377377
Konstruktionsprozess bei solchen Funktionen eigentlich genau aussieht.
378378
@@ -536,7 +536,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
536536
könnte das so aussehen:
537537
%
538538
\begin{center}
539-
\lstinline{Sum} ist die Summer aller Elemente in \lstinline{list0} vor
539+
\lstinline{sum} ist die Summer aller Elemente in \lstinline{list0} vor
540540
\lstinline{list}.
541541
\end{center}
542542
%
@@ -565,7 +565,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
565565
%
566566
\begin{lstlisting}
567567
(define list-sum-helper
568-
;; sum ist die Summer aller Elemente in list0 vor list
568+
; sum ist die Summer aller Elemente in list0 vor list
569569
(lambda (list sum)
570570
(cond
571571
((empty? list) sum)
@@ -581,8 +581,8 @@ \section{Schablonen für Funktionen mit Akkumulator}
581581
%
582582
\begin{lstlisting}
583583
(define list-sum-helper
584-
;; sum ist die Summer aller Elemente in list0 vor list
585-
e (lambda (list sum)
584+
; sum ist die Summer aller Elemente in list0 vor list
585+
(lambda (list sum)
586586
(cond
587587
((empty? list) sum)
588588
((cons? list)
@@ -600,7 +600,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
600600
(define list-sum
601601
(lambda (list0)
602602
(define list-sum-helper
603-
;; sum ist die Summer aller Elemente in list0 vor list
603+
; sum ist die Summer aller Elemente in list0 vor list
604604
(lambda (list sum)
605605
(cond
606606
((empty? list) sum)
@@ -617,7 +617,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
617617
(define list-sum
618618
(lambda (list0)
619619
(define accumulate
620-
;; sum ist die Summer aller Elemente in list0 vor list
620+
; sum ist die Summer aller Elemente in list0 vor list
621621
(lambda (list sum)
622622
(cond
623623
((empty? list) sum)
@@ -661,7 +661,7 @@ \section{Schablonen für Funktionen mit Akkumulator}
661661
die Listen akzeptiert: Du musst eine Invariante finden, und dafür gibt
662662
es nur wenig allgemeingültige Hilfestellung.
663663
664-
\begin{aufgabeinline}
664+
\begin{aufgabeinline}\label{aufgabe:list-min-nonemepty-acc}
665665
Schreibe die Funktion \lstinline{list-min-nonempty} aus
666666
Abschnitt~\ref{sec:list-min-nonempty} auf
667667
Seite~\pageref{sec:list-min-nonempty} noch einmal, diesmal mit
@@ -751,7 +751,7 @@ \section{Über natürliche Zahlen akkumulieren}
751751
(define power
752752
(lambda (base exponent0)
753753
(define accumulate
754-
;; power ist base^(exponent0 - exponent)
754+
; power ist base^(exponent0 - exponent)
755755
(lambda (exponent power)
756756
(cond
757757
((zero? exponent) ...)
@@ -775,7 +775,7 @@ \section{Über natürliche Zahlen akkumulieren}
775775
(define power
776776
(lambda (base exponent0)
777777
(define accumulate
778-
;; power ist base^(exponent0 - exponent)
778+
; power ist base^(exponent0 - exponent)
779779
(lambda (exponent power)
780780
(cond
781781
((zero? exponent)
@@ -825,9 +825,192 @@ \section{Über natürliche Zahlen akkumulieren}
825825
Produkt \[1\times 2\times \cdots \times (n-1)\times n\].
826826
\end{aufgabeinline}
827827
828-
829828
\section{Aktienkurse analysieren}
830829
830+
Es gibt durchaus Funktionen, bei denen mehrere Zwischenergebnisse
831+
nötig sind. Um das zu demonstrieren, schreiben wir eine Funktion, die
832+
den maximalen Gewinn aus einer Reihe von Aktienkursen berechnet.
833+
834+
Diese Reihe von aufeinanderfolgenden Kursen repräsentieren wir als
835+
Liste von Zahlen. Entsprechend sehen Kurzbeschreibung und Signatur so
836+
aus:
837+
%
838+
\begin{lstlisting}
839+
; Bestmöglichen Gewinn durch Kauf und Verkauf ermitteln
840+
(: max-profit ((nonempty-list-of real) -> real))
841+
\end{lstlisting}
842+
%
843+
Gewinn erzielt man hier, indem die Aktie zunächst gekauft und dann
844+
wieder verkauft wird~-- und nicht umgekehrt. Die Differenz zwischen
845+
Verkaufs- und Kaufpreis ist dann der Gewinn. Der folgende Testfall
846+
illustriert dies:
847+
%
848+
\begin{lstlisting}
849+
(check-expect (max-profit (list 5 2 7 3 9 5 1)) 7)
850+
\end{lstlisting}
851+
%
852+
Hier hätte man den maximalen Gewinn erzielt durch Kauf zum Kurs 2 und
853+
Verkauf zum Kurs 9.
854+
855+
Das Gerüst sieht so aus:
856+
%
857+
\begin{lstlisting}
858+
(define max-profit
859+
(lambda (spots)
860+
...)))
861+
\end{lstlisting}
862+
%
863+
("<Spot"> ist ein englisches Wort für "<Kurs">.)
864+
865+
Aber wie geht es weiter?
866+
867+
Die einfache Strategie, einfach die Differenz zwischen Maximum und
868+
Minimum der Liste als maximalen Gewinn auszuweisen, funktioniert
869+
leider nicht: Das Minimum ist 1, liegt aber leider hinter dem Maximum
870+
von 9.
871+
872+
Ebensowenig eignet sich dieses Problem für eine naive, "<normale">
873+
rekursive Funktion. Die Schablone wäre diese hier:
874+
%
875+
\begin{lstlisting}
876+
(define max-profit
877+
(lambda (spots)
878+
(cond
879+
((empty? spots) ...)
880+
((cons? spots)
881+
... (first spots) ...
882+
... (max-profit (rest spots) ...)))))
883+
\end{lstlisting}
884+
%
885+
Das Problem ist, dass der Profit des Rests der Liste nicht ausreicht,
886+
um daraus und dem ersten Element den Profit der Gesamtliste
887+
auszurechnen. Wir müssten dafür auch noch den dazugehörigen
888+
Verkaufskurs wissen.
889+
890+
Wir versuchen es also mal mit Zwischenergebnissen. Dafür sieht die
891+
Schablone so aus:
892+
%
893+
\begin{lstlisting}
894+
(define max-profit
895+
(lambda (spots0)
896+
(define accumulate
897+
(lambda (spots acc)
898+
(cond
899+
((empty? spots) acc)
900+
((cons? spots)
901+
(accumulate (rest spots)
902+
... (first spots) ... acc ...)))))
903+
(accumulate spots0 ...)))
904+
\end{lstlisting}
905+
%
906+
Da die Funktion im ganzen den maximalen Gewinn ausrechnen soll, bietet
907+
es sich an, als Zwischenergebnis den maximalen Gewinn der bisher
908+
gesehenen Kurse zwischen \lstinline{spots0} und \lstinline{spots} als
909+
Zwischenergebnis mitzuführen:
910+
%
911+
\begin{lstlisting}
912+
(define max-profit
913+
(lambda (spots0)
914+
(define accumulate
915+
; max-profit ist der maximale Gewinn zwischen
916+
; spots0 und spots
917+
(lambda (spots max-profit)
918+
(cond
919+
((empty? spots) max-profit)
920+
((cons? spots)
921+
(accumulate (rest spots)
922+
... (first spots) ... acc ...)))))
923+
(accumulate spots0 0)))
924+
\end{lstlisting}
925+
%
926+
Das dumme ist, dass wir auch hier nicht genug Daten haben, um beim
927+
rekursiven Aufruf von \lstinline{accumulate} einen neuen Wert für
928+
\lstinline{max-profit} zu berechnen. Grundsätzlich gibt es aber nur
929+
zwei Möglichkeiten:
930+
%
931+
\begin{itemize}
932+
\item \lstinline{Max-profit} bleibt, wie es ist.
933+
\item \lstinline{Max-profit} wird aktualisiert, weil es besser ist,
934+
zum Kurs \lstinline{(first spots)} zu verkaufen, als zum bisher
935+
besten Verkaufskurs.
936+
\end{itemize}
937+
%
938+
Wir müssen also herausbekommen, wie hoch der Gewinn wäre, wenn wir zum
939+
Kurs \lstinline{(first spots)} verkaufen würden. Das könnten wir,
940+
wenn wir den bestmöglichen \emph{Kaufkurs} wüssten. Etwas Überlegung
941+
ergibt, dass dieser Kaufkurs der minimale Kurs unter den vergangenen
942+
Kursen ist: Dieses Minimum führen wir als weiteres Zwischenergebnis
943+
mit. Aktualisiert wird dieses Minimum mit der eingebauten Funktion
944+
\lstinline{min}:
945+
%
946+
\begin{lstlisting}
947+
(define max-profit
948+
(lambda (spots0)
949+
(define accumulate
950+
; min-spot ist das Minimum der Elemente zwischen
951+
; spots0 und spots
952+
; max-profit ist der maximale Gewinn zwischen
953+
; spots0 und spots
954+
(lambda (spots min-spot max-profit)
955+
(cond
956+
((empty? spots) max-profit)
957+
((cons? spots)
958+
(accumulate (rest spots)
959+
(min (first spots) min-spot)
960+
...)))))
961+
(accumulate spots0 ... 0)))
962+
\end{lstlisting}
963+
%
964+
Es fehlt noch der richtige Anfangswert für \lstinline{min-spot} beim
965+
ersten Aufruf von \lstinline{accumulate}. Hier machen wir Gebrauch
966+
von der Tatsache, dass \lstinline{spots0} eine nicht-leere Liste ist:
967+
Wir können sie in erstes Element und Rest aufteilen wie schon bei
968+
\lstinline{list-min} in Abschnitt~\ref{sec:list-min-nonempty} auf
969+
Seite~\pageref{sec:list-min-nonempty} sowie in
970+
Aufgabe~\ref{aufgabe:list-min-nonemepty-acc} auf
971+
Seite~\pageref{aufgabe:list-min-nonemepty-acc}. Das erste Element ist
972+
dann das erste Minimum:
973+
%
974+
\begin{lstlisting}
975+
(accumulate (rest spots0) (first spots0) 0)))
976+
\end{lstlisting}
977+
%
978+
Es fehlt nur noch der aktualisierte Wert für \lstinline{max-profit}:
979+
Der ist das Maximum aus dem bisherigen \lstinline{max-profit} und dem
980+
möglichen Gewinn aus dem Verkauf zum Kurs \lstinline{(first spots)}.
981+
Hier das Ergebnis:
982+
%
983+
\begin{lstlisting}
984+
(define max-profit
985+
(lambda (spots0)
986+
(define accumulate
987+
; min-spot ist das Minimum der Elemente zwischen
988+
; spots0 und spots
989+
; max-profit ist der maximale Gewinn zwischen
990+
; spots0 und spots
991+
(lambda (spots min-spot max-profit)
992+
(cond
993+
((empty? spots) max-profit)
994+
((cons? spots)
995+
(accumulate (rest spots)
996+
(min (first spots) min-spot)
997+
(max (- (first spots) min-spot)
998+
max-profit))))))
999+
(accumulate (rest spots0) (first spots0) 0)))
1000+
\end{lstlisting}
1001+
%
1002+
Fertig!
1003+
1004+
\begin{aufgabeinline}\index{Fibonacci-Folge}
1005+
Die \textit{Fibonacci-Folge} ist eine Folge natürlicher Zahlen, die
1006+
mit 0 und 1 anfängt. Jede weitere Zahl ist die Summer der beiden
1007+
Zahlen davor.
1008+
1009+
Schreibe eine Funktion, welche für eine Zahl $n$ die $n$-te Zahl aus
1010+
der Fibonacci-Zahl berechnet. Schreibe dafür eine Funktion
1011+
mit zwei Akkumulatoren.
1012+
\end{aufgabeinline}
1013+
8311014
\section{Kontext und Endrekursion}
8321015
\label{sec:iteration}
8331016

i1akku/accumulator.rkt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@
5656
(lambda (list0)
5757
(list-sum-helper list0 0)))
5858

59-
;; sum ist die Summe der Zahlen in list0 vor list
59+
; sum ist die Summe der Zahlen in list0 vor list
6060
#;(define list-sum-helper
61-
;; sum ist die Summer aller Elemente in list0 vor list
61+
; sum ist die Summer aller Elemente in list0 vor list
6262
(lambda (list sum)
6363
(cond
6464
((empty? list) sum)
@@ -68,7 +68,7 @@
6868
(define list-sum
6969
(lambda (list0)
7070
(define accumulate
71-
;; sum ist die Summer aller Elemente in list0 vor list
71+
; sum ist die Summer aller Elemente in list0 vor list
7272
(lambda (list sum)
7373
(cond
7474
((empty? list) sum)
@@ -85,7 +85,7 @@
8585
(define power
8686
(lambda (base exponent0)
8787
(define accumulate
88-
;; power ist base^(exponent0 - exponent)
88+
; power ist base^(exponent0 - exponent)
8989
(lambda (exponent power)
9090
(cond
9191
((zero? exponent)
@@ -104,7 +104,7 @@
104104
(define power2
105105
(lambda (base exponent0)
106106
(define accumulate
107-
;; power ist (exponent0 - exponent)mal mit sich selbst multipliziert
107+
; power ist base^(exponent0 - exponent)
108108
(lambda (exponent power)
109109
(cond
110110
((zero? exponent)
@@ -125,7 +125,7 @@
125125
(define factorial
126126
(lambda (n0)
127127
(define accumulate
128-
;; acc ist das Produkt aller Zahlen von (+ n 1) bis n0
128+
; acc ist das Produkt aller Zahlen von (+ n 1) bis n0
129129
(lambda (n acc)
130130
(cond
131131
((zero? n) acc)

i1akku/profit.rkt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
;; Die ersten drei Zeilen dieser Datei wurden von DrRacket eingefügt. Sie enthalten Metadaten
2+
;; über die Sprachebene dieser Datei in einer Form, die DrRacket verarbeiten kann.
3+
#reader(lib "vanilla-reader.rkt" "deinprogramm" "sdp")((modname profit) (read-case-sensitive #f) (teachpacks ((lib "image.rkt" "teachpack" "deinprogramm" "sdp"))) (deinprogramm-settings #(#f write repeating-decimal #f #t none explicit #f ((lib "image.rkt" "teachpack" "deinprogramm" "sdp")))))
4+
; Bestmöglichen Gewinn durch Kauf und Verkauf ermitteln
5+
(: max-profit ((nonempty-list-of real) -> real))
6+
7+
(check-expect (max-profit (list 5 2 7 3 9 5 1)) 7)
8+
9+
(define max-profit
10+
(lambda (spots0)
11+
(define accumulate
12+
; min-spot ist das Minimum der Elemente zwischen
13+
; spots0 und spots
14+
; max-profit ist der maximale Gewinn zwischen
15+
; spots0 und spots
16+
(lambda (spots min-spot max-profit)
17+
(cond
18+
((empty? spots) max-profit)
19+
((cons? spots)
20+
(accumulate (rest spots)
21+
(min (first spots) min-spot)
22+
(max (- (first spots) min-spot)
23+
max-profit))))))
24+
(accumulate (rest spots0) (first spots0) 0)))
25+
26+
#;(define max-profit
27+
(lambda (spots)
28+
(cond
29+
((empty? spots) ...)
30+
((cons? spots)
31+
... (first spots) ...
32+
... (max-profit (rest spots) ...)))))

0 commit comments

Comments
 (0)