Freitag, 3. Dezember 2010

 

Musterlösung zu Übungsbeispiele für Python vom 1.12.2010 (POS1: 1BHIF)

Prüfen, ob durch die Angabe dreier Längen ein Dreieck gegeben ist.
Zwei Seiten müssen immer länger als die dritte sein. Man muss dies für alle Möglichkeiten prüfen, denn z.B.
a = 3, b = 4, c = 1
Da stimmt zwar a + b > c (7 > 1) und b + c > a (4 > 3), jedoch nicht a + c > b (4 == 4)!
Bei der Lösung wird eine Schleife verwendet.
def ist_dreieck(a, b, c):
    """liefert True, wenn durch a, b und c ein gültiges Dreieck definiert
    ist"""
    i = 3
    while i > 0:
        if a + b <= c:
            return False # kein Dreieck
        a, b, c = b, c, a
        i = i - 1
    return True
Man könnte das Problem natürlich auch durch logische Verknüpfung der Prüfungen lösen:
def ist_dreieck(a, b, c):
    """liefert True, wenn durch a, b und c ein gültiges Dreieck definiert
    ist"""
    if a + b > c and b + c > a and c + a > b:
        ret = True
    else:
        ret = False
    return ret
Oder gleich so:
def ist_dreieck(a, b, c):
    """liefert True, wenn durch a, b und c ein gültiges Dreieck definiert
    ist"""
    return a + b > c and b + c > a and c + a > b
Bei der zweiten Aufgabe soll geprüft werden, ob durch zwei Seiten und einem Winkel ein Quadrat gegeben ist. Die beiden Seiten müssen gleich lang sein (und größer 0) und der Winkel 90°:
def ist_quadrat(a, b, w):
    """liefert True, wenn durch a und b sowie dem Winkel w ein 
    Quadrat definiert ist"""
    quadrat = False
    if a > 0:
        if a == b:
            if w == 90:
                quadrat = True
    # else entfällt, da die Variable quadrat schon mit False vorbelegt ist.
    return quadrat
Oder wieder kürzer mit logischer Verknüpfung:
def ist_quadrat(a, b, w):
    """liefert True, wenn durch a und b sowie dem Winkel w ein 
    Quadrat definiert ist"""
    return a == b and w == 90 and a > 0
Die Schaltjahrprüfung könnte so erfolgen:
def ist_schaltjahr(jahr):
    """liefert True, wenn das gegebene Jahr ein Schaltjahr ist.
    Seit Ende 1582 gilt der Gregorianische Kalender, bei dem folgende Regel
    für die Schaltjahrbestimmung anzuwenden sind:
        In allen Jahren, deren Jahreszahl durch vier teilbar ist, ist der 
        29. Februar ein Schalttag und damit ist dieses Jahr ein Schaltjahr.
        Eine Ausnahme bilden allerdings die vollen Jahrhundertjahre 1700, 
        1800, 1900 usw., auch Säkularjahre genannt. Hiervon erhalten nur 
        diejenigen einen Schalttag, deren Jahreszahl durch 400 teilbar ist. 
        Jedes vierte Säkularjahr ist somit ein Schaltjahr.
    Für alle Jahre <= 1582 liefert die Funktion False, weil da das
    Schaltjahr nicht definiert ist, sonst gilt obige Regel."""
    if jahr <= 1582:
        schaltjahr = False
    elif jahr % 400 == 0:
        schaltjahr = True
    elif jahr % 100 == 0:
        schaltjahr = False
    elif jahr % 4 == 0:
        schaltjahr = True
    else:
        schaltjahr = False
    return schaltjahr
Jahre bis 1582 können keine Schaltjahre sein, da der Gregorianische Kalender erst im Jahr 1582 definiert wurde. Zum Test der Schaltjahrprüfung noch ein paar Worte, aber zunächst die Testfunktion, die ein paar Jahreszahlen prüft, von denen man weiß, dass sie kein Schaltjahr sind sowie Zahlen, bei denen bekannt ist, dass sie Schaltjahre sind:
def test_ist_schaltjahr():
    for jahr in (1000, 1580, 1581, 1582, 1900, 1999, 2003):
        assert not ist_schaltjahr(jahr)
    for jahr in (1996, 2000, 2004):
        assert ist_schaltjahr(jahr)
    print("ist_schaltjahr ok")
Die Anweisung assert ist für Programmierer und dient zum Prüfen der Bedingung, die gleich dahinter folgt, gedacht. Zum Beispiel:
assert 3 < 4
Die Bedingung ist wahr, daher läuft das Programm weiter. Ist die Bedingung hingegen falsch, so wird das Programm mit einer Fehlermeldung abgebrochen:
>>> assert 3 > 4
Traceback (most recent call last):
  File "", line 1, in 
    assert 3 > 4
AssertionError
Zusätzlich kann ein Text als Fehlermeldung angegeben werden:
>>> assert 3 > 4, "3 ist nicht größer als 4"
Traceback (most recent call last):
  File "", line 1, in 
    assert 3 > 4, "3 ist nicht größer als 4"
AssertionError: 3 ist nicht größer als 4
Angewendet auf obige Testfunktion bedeutet das, dass immer, wenn assert not ist_schaltjahr(jahr) die Funktion ist_schaltjahr(jahr) den Wert False leifern muss und wenn assert ist_schaltjahr(jahr) steht, True geleifert werden muss, damit das Programm nicht abgebrochen wird. Der nächste Punkt ist die for jahr in (1000, 1580, 1581, 1582, 1900, 1999, 2003):-Zeile. Die Variable jahr bekommt der Reihe nach die Werte 1000, 1580, 1581, 1582, 1900, 1999, 2003 und der eingerückte Code-Block (die eine Zeile) wird ausgeführt. Das ist die for-Schleife, welche im Buch im Kapitel 7 auch erklärt wird. Die Funktion test_ist_schaltjahr() prüft also zunächst ein paar Jahre, die kein Schaltjahr sind und ein paar Schaltjahre. Die Fibonaccizahlen können wie folgt ermittelt werden:
def fib(n):
    """liefert die Fibonaccizahl zu n. Die Zahlen sind wie definiert:
        fib(0) -> 0
        fib(1) -> 1
        fib(2) -> 1
        fib(3) -> 2
        fib(4) -> 3
        fib(5) -> 5
        fib(6) -> 8
        ...
        fib(10) -> 55
        ...
        fib(15) -> 610"""
    f = 0
    a = 0
    b = 1
    i = 0
    while i < n:
        f = a + b
        b, a = a, f
        i += 1
    return f
Für n = 0 und n = 1 sind die (Anfangs-)Werte vordefiniert. fib(0) = 0 und fib(1) = 1. Danach gilt die Regel, dass die n. Fibonaccizahl die Summe der beiden Vorgänger ist, also fib(n - 1) + fib(n - 2) (Siehe dazu auch Wikipedia: Fibonacci-Folge). Die Variable f hat immer den Wert der aktuellen Fibonaccizahl und ist daher mit 0 initialisiert. Die Variable a und b haben immer den Wert der beiden Vorgänger. Sie können das leicht sehen, wenn Sie print(...)-Anweisungen einfügen und sich dadurch den Ablauf der Berechnung anzeigen lassen:
def fib(n):
    """liefert die Fibonaccizahl zu n."""
    f = 0
    a = 0
    b = 1
    i = 0
    while i < n:
        f = a + b
        print(i, ":  f =", f, "a =", a, "b =", b) # Testausgabe
        b, a = a, f
        i += 1
    return f

print(fib(10))
..gibt dann folgendes aus:
0 :  f = 1 a = 0 b = 1
1 :  f = 1 a = 1 b = 0
2 :  f = 2 a = 1 b = 1
3 :  f = 3 a = 2 b = 1
4 :  f = 5 a = 3 b = 2
5 :  f = 8 a = 5 b = 3
6 :  f = 13 a = 8 b = 5
7 :  f = 21 a = 13 b = 8
8 :  f = 34 a = 21 b = 13
9 :  f = 55 a = 34 b = 21
55
Die Ausgabe einer Tabelle mithilfe der Funktion fib() ist einfach (oder doch etwas kompliziert, weil die eckigen Klammern dazukommen):
def fibtab(n):
    """erzeugt eine Tabelle der Fibonaccizahlen bis inclusive n.
    z.B. gibt fibtab(10) folgendes aus:
        [0, 1, 1, 2, 3, 5, 8]
    oder fibtab(100):
        [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
    Hinweis:
        verwenden Sie als Zeilenumbruch ", " beim print, also
            print(wert, end=', ')
        außer natürlich beim letzten Wert, hier ist "]" gefordert.
    """
    i = 0
    print("[", end = "")
    f = fib(i)
    while f <= n:
        print(f, end = "")
        i += 1
        f = fib(i)
        if f > n:
            print("]")
        else:
            print(", ", end = "")
Wir müssen sicherstellen, dass zunächst "[" ohne Zeilenumbruch ausgegeben wird. Die Funktion print(...) hat einen optionalen Parameter end, der angibt, was zum Schluss ausgegeben werden soll. Standardmäßig ist das "\n", was einen Zeilenumbruch bewirkt. Setzt man den Wert end = "", so wird ein Leerstring, also nichts, ausgegeben. In der Schleife muss man noch prüfen, ob man das Ende erreicht hat und in diesem Fall "]" (mit Zeilenumbruch) ausgibt. Im Normalfall wird ein Komma ohne Zeilenumbruch ausgegeben (letzte Zeile). Beachten Sie, dass der Parameter n hier die Grenze der Fibonaccizahl angibt und nicht die wievielte (wie bei fib(n)). Die Lösung zum Beispiel, die Summe der Vielfachen von 3 und 5 kleiner als ein Maximum:
def sum_3_5(max):
    """
    bilde Summe der Vielfachen von 3 und 5 kleiner als max.
    Beispielsweise liefert sum_3_5(10) 23, weil
        die Vielfachen von 3 < 10 sind
            3 + 6 + 9 = 18
        die Vielfachen von 5 < 10 sind
            5
        18 + 5 = 23
    sum_3_5(20) liefert 78, weil
        3+  6+9+   12+15+18=63
          5+    10+        =15 (15 ist in obiger Liste schon enthalten!)
    sum_3_5(25) liefert 143, weil
        3+  6+9+   12+   18+   21+24=93 (Vielfache von 5 stehen darunter)
          5+    10+   15+   20      =50
    sum_3_5(100) liefert 2318 (ohne Beweis)
    """
    summe = 0
    i = 0
    while i < max:
        if i % 5 == 0:
            summe += i
        elif i % 3 == 0:
            summe += i
        i += 1
    return summe
Die Laufvariable i wird immer dann zur summe dazugezählt, wenn sie durch 5 teilbar ist oder wenn sie durch 3 teilbar ist. Der Operator % berechnet den Rest der Division. Ist der Rest 0, so ist die erste durch die zweite Zahl teilbar. Das Beispiel zum finden des nächstkleineren bzw. gleichen Quadrats kann so mit einer Probiermethode gelöst werden:
def minquad(zahl):
    """finde die nächstkleinere oder gleichgroße ganze Zahl, die eine 
    Quadratzahl ist. Beispiele:
        minquad(16) -> 16 (weil 4 * 4 = 16 <= 16)
        minquad(24) -> 24 (weil 4 * 4 = 16 <= 14)
        minquad(25) -> 25 (weil 5 * 5 = 25 <= 25)
        minquad(26) -> 25 (weil 5 * 5 = 25 <= 26)
    """
    i = 0
    mq = 0
    while i ** 2 <= zahl:
        mq = i ** 2
        i += 1
    return mq
i ** 2 bedeutet i2. Man könnte natürlich auch i * i schreiben. Diese Funktion prüft einfach sukzessive alle Quadrate, bis eines gefunden wurde, das größer als die gegebene Zahl ist. Der Wert davor (die Variable mq) ist natürlich das Ergebnis, weil das der größte Wert war, für den die Bedingung gilt. Es geht natürlich schneller und einfacher, wenn man sich eine Formel überlegt:
from math import sqrt
from math import floor
def minquad(zahl):
    """finde die nächstkleinere oder gleichgroße ganze Zahl, die eine 
    Quadratzahl ist. Beispiele:
        minquad(16) -> 16 (weil 4 * 4 = 16 <= 16)
        minquad(24) -> 16 (weil 4 * 4 = 16 <= 24)
        minquad(25) -> 25 (weil 5 * 5 = 25 <= 25)
        minquad(26) -> 25 (weil 5 * 5 = 25 <= 26)
    """
    return int(floor(sqrt(zahl))) ** 2
Die Funktion floor(x) liefert die nächstkleinere ganze Zahl zu x (bei positiven Zahlen schneidet dir Funktion die Nachkommastellen der übergebenen Zahl ab). Z.B:
>>> from math import floor
>>> floor(3.14)
3
>>> floor(3)
3
>>> floor(-3.14)
-4
Wenn man die nächstkleinere ganze Zahl der Wurzel quadriert, hat man das gewünschte Ergebnis. Die Turtle-Funktion zum Dreieckzeichnen könnte wie folgt aussehen und braucht nicht weiter erklärt werden:
def dreieck(a, w):
    """zeichnet ein gleichseitiges Dreieck der Seitenlänge a, gedreht um
    den Winkel w. w = 0 bedeutet, dass die eine Seite horizional ist. w = 90
    bedeutet, dass das Dreieck nach LINKS um 90° gedreht ist, d.h. eine Seite
    ist vertikal.
    Das Dreieck soll rechts neben des angegebenen Punktes gezeichnet werden.
    """
    # nehme an Turtle sieht nach oben (wie auch beim Starten)
    right(90 - w)
    pendown()
    for i in range(3):
        forward(a)
        left(120)
    penup()
    left(90 - w)
Um ein Dreieck an eine bestimmte Position zu setzen, kann zusätzlich die Funktion setpos(x, y), welche die Turtle an die Postion x,y setzt, verwendet werden:
def dreieck_an_pos(a, w, x, y):
    """zeichnet ein Dreieck mit Hilfe der Funktion dreieck(a, w) an der 
    Position x, y."""
    penup()
    setpos(x, y)
    dreieck(a, w)
Abgesehen von den Turtle-Funktionen können Sie die Funktionen direkt auf der Website http://codepad.org testen. Dort können Sie auch andere Programmiersprachen probieren. Die Python-Version ist leider nicht aktuell, daher könnte es sein, dass einiges doch nicht direkt so funktioniert. Diese einfachen Beispiele funktionieren aber.
Hier ist zu minquad(zahl) zu sehen:

Labels: , ,


Kommentare:

Kommentar veröffentlichen

Abonnieren Kommentare zum Post [Atom]





<< Startseite

This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]