Bergervei/GnomSort

Aus ProgrammingWiki

Wiki-Quelltext dieser Seite.

<language>Scheme</language>
<code id="4db9d77895031" hidden>
(define listequal?
  (lambda (ls1 ls2)
    (cond
      [(and (null? ls1) (null? ls2)) #t]
      [(or (null? ls1) (null? ls2)) #f]
      [(and (list? (car ls1)) (list? (car ls1)))
       (and (listequal? (car ls1) (car ls2)) (listequal? (cdr ls1) (cdr ls2)))]
      [else 
       (and (equal? (car ls1) (car ls2)) (listequal? (cdr ls1) (cdr ls2)))])))

(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 turtle)
    (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 nr)
    (let* ([breite (/ maxbreite (length ls))] 
           [extremwerte (minmax ls)]
           [min (car extremwerte)] [max (cdr extremwerte)]
           [canvas (eval (string->symbol (string-append "canvas" (number->string nr))))]
           [turtle (eval (string->symbol (string-append "turtle" (number->string nr))))])
      (letrec
          ([zeichnen
            (lambda (min max ls)
              (if (null? ls)
                  'ok
                  (begin
                    (balken (floor (* (/ (car ls) (- max min)) maxhoehe)) (floor breite) turtle)
                    (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>

== Algorithmus ==

Gartenzwerge sortieren Blumentöpfe, die in einer Reihe aufgestellt sind, der Größe nach.<br />
Sie benutzen dazu den folgenden Algorithmus:<br />

<ul>
<li> Beginne links und vergleiche die beiden ersten Töpfe.</li>
<li> Stehen sie in der richtigen Reihenfolge gehe ein Schritt nach rechts.</li>
<li> Vertausche die beiden Töpfe, wenn sie nicht in der korrekten Reihenfolge stehen, und gehe einen Schritt nach links.<br />
Ist der Schritt nach links nicht möglich, gehe ein Schritt nach rechts.</li> 
<li> Wiederhole das Sortieren bis zum letzten Blumentopf.</li>
</ul>

Die Größe der Blumentöpfe wird mit Zufallszahlen festgelegt:
 
<code id="4db9d8505757f">
(define zufallszahl
  (lambda (min max)
    (+ (randomint (- max min -1)) min)))

(define zufallsliste
  (lambda (min max anzahl)
   (if (= anzahl 0)
        ()
        (cons (zufallszahl min max) (zufallsliste min max (- anzahl 1))))))

;GnomSort
(define gnomsort
  (lambda (ls)
    (letrec
        ([sort
          (lambda (ls1 ls2)
            ...])
      (reverse (sort ls ())))))
</code>

<check>
(and (listequal? (gnomsort '(12 4 5 6 33 2 6 7 1 47)) '(1 2 4 5 6 6 7 12 33 47))
     (listequal? (gnomsort '(147)) '(147))
     (listequal? (gnomsort '()) '()))
</check>

<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
'''Ungeordnete Zufallszahlen:'''
<canvas w="400" h="300"></canvas>
</td>
<td>&nbsp;</td>
<td align="center">
'''Geordnete Zufallszahlen:'''
<canvas w="400" h="300"></canvas>
</td>
</tr>
</table>

<run id="4db9d8505774c">
(define ls (zufallsliste 1 1000 100))
(balkendiagramm ls 1)
(define ls-sort (gnomsort ls))
(balkendiagramm ls-sort 2)
</run>

'''Hinweis:''' Um mehrere Zeichenflächen nutzen zu können, muss die Prozedur <tt>balkendiagramm</tt> mit einem zusätzlichen Parameter ausgestattet werden, der einer fortlaufenden Nummerierung entspricht:
 (balkendiagramm
   &lt;ls&gt;       ; Liste mit Zahlenwerten   
   &lt;nr&gt;)      ; fortlaufende Nummerierung des Balkendiagramms

== Effizienz von GnomSort ==

=== Average-Case-Experimente ===

Wir definieren unseren GnomSort-Algorithmus mit einem "eingebauten" (Zeittakt-)Zähler:

<code id="4dbdb73943187">
(define count 0)

(define gnomsort-aufwand
  (lambda (ls)
    (letrec
        ([sort
          (lambda (ls1 ls2) 
            (cond
              [(null? ls1) ls2]
              [(null? (cdr ls1))
               (set! count (+ 1 count)) ;gib die einelementige Liste zurück
               (cons (car ls1) ls2)]
              [(> (car ls1) (cadr ls1))
               (set! count (+ 1 count)) ;vertausche das n-te Paar und gehe nach links
               (if (null? ls2)
                   (sort (cons (car ls1) (cddr ls1)) (cons (cadr ls1) ls2))
                   (sort (cons (car ls2) (cons (cadr ls1) (cons (car ls1) (cddr ls1)))) (cdr ls2)))]
              [else
               (set! count (+ 1 count)) ;gehe nach rechts
               (sort (cdr ls1) (cons (car ls1) ls2))]))])
      (reverse (sort ls ())))))


(define gnomsort-ausgabe
  (lambda (min max anzahl)
    (let ([ls (zufallsliste min max anzahl)])
      (set! count 0)
      (display "Ungeordnete Zufallszahlen:") (newline)
      (display ls) (newline)
      (display "Geordnete Zufallszahlen:") (newline)
      (display (gnomsort-aufwand ls)) (newline)
      (display "Anzahl der Vertauschungen bzw. Bewegungen: ") (display count) (newline)
      'ok)))
</code>

<run id="4db9d77895339">
(gnomsort-ausgabe 1 1000 20)
</run>

Im Weiteren wollen wir uns nur auf die Zählergebnisse unseres "Zeittaktmessers" konzentrieren.<br />
Dazu sollen die Wiederholungen des Experimentes automatisiert werden:

<code id="4db9d778951bf">
(define gnomsort-mehrfach
  (lambda (min max anzahl whlg)
    (letrec 
        ([summe
          (lambda (n)           
            (if (= n 0)
                0
                (begin
                  (set! count 0)
                  (gnomsort-aufwand (zufallsliste min max anzahl))
                  (+ count (summe (- n 1))))))])     
      (exact->inexact (/ (summe whlg) whlg)))))
</code> 

<run id="4db9d778953a3">
(gnomsort-mehrfach 1 1000 20 100)
</run>

Schließlich sollen die Ergebnisse unserer empirischen Untersuchung wieder im Balkendiagramm visualisiert werden.

<code id="4db9d77895234">
(define experiment
  (lambda (min max anzahl schrittweite whlg)
    (if (< anzahl 1)
        ()
        (append (experiment min max (- anzahl schrittweite) schrittweite whlg)
                (list (gnomsort-mehrfach min max anzahl whlg))))))
</code>

'''Achtung!''' Mit kleinen Wiederholraten und etwas Geduld experimentieren!

<canvas w="400" h="300"></canvas>

<run id="4db9d7789541b">
(balkendiagramm (experiment 1 1000 200 5 10) 3)
</run>

Die empirischen Untersuchungen zeigen im Mittel offensichtlich einen ''quadratischen Zeitaufwand'' von GnomSort.<br />
Es gilt:

<table border="0" cellspace="0" cellpadding="4">
<tr valign="top">
<td rowspan="2"><b>average case: &nbsp;</b></td>
<td><math>T(n) = \frac{1}{2} \cdot n^2</math> &nbsp; bzw.</td>
</tr>
<tr>
<td><math>T(n) = \mathcal{O}(n^2)</math></td>
</tr>
</table>

=== Best- und Worst-Case-Experimente ===

<code id="4dbbdcbaddba0">
(define gnomsort-spezial
  (lambda (min max anzahl whlg . ls)
    (letrec
        ([anwenden 
          (lambda (zuf-ls ls)
            (if (null? ls)
                zuf-ls
                (apply (car ls) (list (anwenden zuf-ls (cdr ls))))))]
         [summe
          (lambda (n)           
            (if (= n 0)
                0
                (begin
                  (set! count 0)
                  (gnomsort-aufwand (anwenden (zufallsliste min max anzahl) ls))
                  (+ count (summe (- n 1))))))])
       (exact->inexact (/ (summe whlg) whlg)))))

(define experiment-spezial
  (lambda (min max anzahl schrittweite whlg . ls)
    (if (< anzahl 1)
        ()
        (append (apply experiment-spezial min max (- anzahl schrittweite) schrittweite whlg ls)
                (list (apply gnomsort-spezial min max anzahl whlg ls))))))
</code>

<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
'''GnomSort: best-case'''
<canvas w="400" h="300"></canvas>
</td>
<td>&nbsp;</td>
<td align="center">
'''GnomSort: worst-case'''
<canvas w="400" h="300"></canvas>
</td>
</tr>
</table>

<run id="4dbbdcbaddc87">
(balkendiagramm (experiment-spezial 1 1000 200 5 1 gnomsort) 4)
(balkendiagramm (experiment-spezial 1 1000 200 5 5 reverse gnomsort) 5)
</run>