Grundlagen der funktionsorientierten Programmierung mit SCHEME/Lotto 6 aus 49
Aus ProgrammingWiki
Wiki-Quelltext dieser Seite.
<language>Scheme</language>
[[Bild:Lotto_Ziehungsgeraet1.jpg|miniatur|hochkant=1.5|Ziehungsgerät zum Lotto 6 aus 49]]
'''Autor: Berger, Veit'''
<code id="53ec6802bf3da" hidden>
;-- M. Hielscher / Chr. Wagenknecht --
(define random-maker
(let* ((multiplier 48271)
(modulus 2147483647)
(apply-congruence
(lambda (current-seed)
(let ((candidate (modulo (* current-seed multiplier) modulus)))
(if (zero? candidate)
modulus
candidate))))
(coerce
(lambda (proposed-seed)
(if (integer? proposed-seed)
(- modulus (modulo proposed-seed modulus)) 19880214))))
(lambda (initial-seed)
(let ((seed (coerce initial-seed)))
(lambda args
(cond ((null? args)
(set! seed (apply-congruence seed))
(/ (- modulus seed) modulus))
((null? (cdr args))
(let* ((proposed-top
(ceiling (abs (car args))))
(exact-top
(if (inexact? proposed-top)
(inexact->exact proposed-top)
proposed-top))
(top
(if (zero? exact-top)
1
exact-top)))
(set! seed (apply-congruence seed))
(inexact->exact (floor (* top (/ seed modulus))))))
((eq? (cadr args) 'reset)
(set! seed (coerce (car args))))
(else
(display "random: unrecognized message")
(newline))))))))
(require-library 'sisc/libs/srfi/srfi-19)
(import srfi-19)
(define random (random-maker (time-nanosecond (current-time))))
</code>
== Vorüberlegungen ==
In einer beliebigen Anzahl sollen Lottoziehungen 6 aus 49 simuliert werden.<br />
Zunächst ist es kein Problem, Zufallszahlen zwischen 1 und 49 zu bestimmen (s. [[Grundlagen der funktionsorientierten Programmierung mit SCHEME/Komplexe Anwendungen: Prozeduren mit Zeichenketten und Listen|Prozeduren mit Zeichenketten und Listen]]).<br />
Wir sollten aber daran denken, dass diese Zahlen in einer korrekten Lottoziehung paarweise verschieden sein müssen.
== Algorithmus ==
Wir wollen uns für eine endständig rekursive Problemlösung entscheiden, in der wir eine Liste mit korrekten Lottozahlen im rekursiven Abstieg einer entsprechenden Prozedur bilden. Mit einer Zählvariablen steuern wir den Ablauf:
* Falls der Zähler Null ist, gib die vollständige Liste einer Lottoziehung zurück.
* Anderenfalls lege eine Zufallszahl zwischen 1 und 49 fest.
* Prüfe, ob sie bereits Element der aktuellen Teilziehung ist.
* Trifft dieser Fall zu, wiederhole die Ziehung bei unverändertem Zählerstand.
* Anderenfalls füge die Zufallszahl zur Teilziehung hinzu und führe die Ziehung bei dem um 1 verminderten Zähler fort.
== Implementation ==
Um eine aufsteigend sortierte Lottoziehung zu ermitteln, modifizieren wir den Listen-Konstruktor <tt>cons</tt> geringfügig zu <tt>cons-sort</tt>.
<code id="53ebc626eaa08">
(define lottozahl
(lambda (max)
(+ 1 (random max))))
(define lottoziehung
(lambda (n max)
(letrec
([element?
(lambda (el ls)
(cond
[(null? ls) #f]
[(eqv? el (car ls)) #t]
[else
(element? el (cdr ls))]))]
[cons-sort
(lambda (el ls)
(cond
[(null? ls) (list el)]
[(< el (car ls)) (cons el ls)]
[else
(cons (car ls) (cons-sort el (cdr ls)))]))]
[ziehung
(lambda (n ls)
(if (= n 0)
ls
(let ([z (lottozahl max)])
(if (element? z ls)
(ziehung n ls)
(ziehung (- n 1) (cons-sort z ls))))))])
(ziehung n ()))))
(define lottoziehungen
(lambda (n max anz)
(if (= anz 0)
()
(cons (lottoziehung n max) (lottoziehungen n max (- anz 1))))))
</code>
<run id="53ebc626eaacc">
(lottoziehungen 6 49 5)
</run>
== Gleichverteilung der Lottozahlen ==
Wir wollen uns von einer hinreichenden Güte der Lottozahlen überzeugen.<br />
Dazu zählen wir das Auftreten jeder gezogenen Lottozahl in einer 49-elementigen Liste. Bei einer großen Anzahl von Ziehungen müsste sich eine Gleichverteilung der Lottozahlen ergeben. Diese visualisieren wir in einem Balkendiagramm (s. [[Grundlagen der funktionsorientierten Programmierung mit SCHEME/Mehrfachrekursionen|Mehrfachrekursionen]]).<br />
Zur Erinnerung: Syntax der Prozedur <tt>balkendiagramm</tt> für numerische Listen:
(balkendiagramm <ls>) ; <ls> ... Liste mit Zahlenwerten
<code id="53ec6802bf478" hidden>
(define rand 50)
(define maxbreite (- 400 (* 2 rand)))
(define maxhoehe (- 300 (* 2 rand)))
(define minmax
(lambda (ls)
(letrec
([extremsuche
(lambda (extrem ls op)
(cond
[(null? ls) extrem]
[(op extrem (car ls)) (extremsuche (car ls) (cdr ls) op)]
[else
(extremsuche extrem (cdr ls) op)]))])
(cons (extremsuche 0 ls >)
(extremsuche 0 ls <)))))
(define balken
(lambda (hoehe breite)
(forward turtle breite) (left turtle 90)
(forward turtle hoehe) (left turtle 90)
(forward turtle breite) (left turtle 90)
(forward turtle hoehe) (left turtle 90)
(forward turtle breite)))
(define balkendiagramm
(lambda (ls)
(let* ([breite (/ maxbreite (length ls))]
[extremwerte (minmax ls)]
[min (car extremwerte)] [max (cdr extremwerte)])
(letrec
([zeichnen
(lambda (min max ls)
(if (null? ls)
'ok
(begin
(balken (floor (* (/ (car ls) (- max min)) maxhoehe)) (floor breite))
(zeichnen min max (cdr ls)))))])
(clear canvas)
(hide turtle)
(move turtle rand (+ rand (* (/ max (- max min)) maxhoehe))) (right turtle 90)
(zeichnen min max ls)))))
</code>
<code id="53ec6802bf509">
(define verteilung
(lambda (min max ls)
(letrec
([startliste
(lambda (pos)
(if (< pos min)
()
(cons 0 (startliste (- pos 1)))))]
[poszaehlen
(lambda (pos erg-ls)
(if (= pos 1)
(cons (+ 1 (car erg-ls)) (cdr erg-ls))
(cons (car erg-ls) (poszaehlen (- pos 1) (cdr erg-ls)))))]
[zaehlen
(lambda (ls erg-ls)
(cond
[(null? ls) erg-ls]
[(list? (car ls)) (zaehlen (car ls) (zaehlen (cdr ls) erg-ls))]
[else
(zaehlen (cdr ls) (poszaehlen (car ls) erg-ls))]))])
(zaehlen ls (startliste max)))))
</code>
<canvas w="400" h="300"></canvas>
<run id="53ec6802bf5c5">
(balkendiagramm (verteilung 1 49 (lottoziehungen 6 49 10000)))
</run>
Zurück zum [[Grundlagen der funktionsorientierten Programmierung mit SCHEME/Der Algorithmusbegriff|Algorithmusbegriff]].