Seite 1 von 1

Programm soll Konstanten erstellen

Verfasst: Sonntag 6. Oktober 2013, 18:15
von Jina_Jita
Hallo!
Ich bin noch neu hier und hab direkt mal eine Frage:
Ich habe ein Programm welches einen eingegebenen Wert in eine Liste einsortiert und den Nachfolger und Vorgänger, als Bruch, ausgibt. Das ganze sieht bis jetzt so aus (Mit allen Brüchen bis n/64):

Code: Alles auswählen

def Test():
    B1_3=1/3
    B1_4=1/4
    B1_5=1/5
    B2_5=2/5
    B3_5=3/5
    B1_6=1/6
    listeDerBrueche = [B1_3,B1_4,B1_5,B2_5,B3_5,B1_6,]
    eingegebeneZahl = eval(input("Bitte Zahl eingeben: "))
    listeDerBrueche.append(eingegebeneZahl)
    listeDerBrueche.sort()
    position = listeDerBrueche.index(eingegebeneZahl)
    print (position)
    kleiner = listeDerBrueche.pop(position - 1)
    listeDerBrueche.append(kleiner)
    listeDerBrueche.append(eingegebeneZahl)
    listeDerBrueche.sort()
    print(kleiner)
    groeßer = listeDerBrueche.pop(position + 2)
    listeDerBrueche.append(groeßer)
    listeDerBrueche.append(eingegebeneZahl)
    listeDerBrueche.sort()
    print(groeßer)
Nun da ich die Sortierung genauer machen will habe ich ein Programm geschrieben, welches die Konstanten bis zu einem bestimmten vorher eingagebenen Wert generiert. Das Programm welches mir die Konstanten generiert hat sieht so aus:

Code: Alles auswählen

def BruecheGenerieren():
    Nenner = 1
    MaximalNenner = eval(input("Bitte maximal Nenner eingeben: ")) 
    while (Nenner <= MaximalNenner):
        ZaehlNenner = Nenner-1
        for Zaehler in range(1,ZaehlNenner):
            ZaehlerTest = Zaehler
            NennerTest = Nenner
            while NennerTest != 0:
                Rest = ZaehlerTest%NennerTest
                ZaehlerTest, NennerTest = NennerTest, Rest
            if ZaehlerTest == 1:
                str_Zaehler = str(Zaehler)
                str_Nenner = str(Nenner)
                print("B"+str_Zaehler+"_"+str_Nenner+"="+str_Zaehler+"/"+str_Nenner)
        Nenner = Nenner + 1
Etwas unschön, denn so muss man die geprintete Liste von Definitionen selber in das Programm kopieren.
Nun wollte ich fragen wie ich die Generierung der Liste und der Konstanten ind Programm einzubinden. Denn so könnte der Benutzer die Genauichkeit selber festlegen und das Programm wäre weniger lang.

Ich würde mich über Antworten freuen :D
Jina

Re: Programm soll Konstanten erstellen

Verfasst: Sonntag 6. Oktober 2013, 18:54
von BlackJack
@Jina_Jita: Das was Du da machst sieht unsinnig aus. Wenn man auf diese Weise Konstantendefinitionen generiert und in ein Programm als Quelltext einfügt, macht man definitiv etwas falsch. Das sind letztendlich durchnummerierte Namen und die sind so gut wie immer ein Zeichen dafür, dass man eigentlich eine Datenstruktur erstellen möchte. Zum Beispiel eine verschachtelte Liste mit den Werten so dass man über ``B[nominator][denominator]`` auf die einzelnen Werte zugreifen kann.

Mit den `eval()`-Aufrufen kann der Benutzer nahezu jeden beliebigen Python-Code ausführen, auch so etwas wie ``__import__('os').system('rm - rf ~')``. Wenn Du die Eingabe in eine ganze Zahl oder in eine Gleitkommazahl umwandeln möchtest, dann tu das, und nur das.

Die Operationen in `Test()`, was per Konvention als Funktion nicht gross geschrieben werden sollte, sind mir auch ein Rätsel. Das ist total verwirrend. Wenn ich das jetzt richtig sehe ist der eingegebene Wert am Ende (mindestens) drei mal in der sortierten Liste. Und diese `pop()`-Operationen mit anschliessenden Anfügen des Elements und dann sortieren sind allesamt unsinnig. `kleiner` kann übrigens so wie es dort steht durchaus einen Wert ergeben der *grösser* als `eingegebeneZahl` ist. Auf der anderen Seite kann die Zuweisung an `groesser` auch zu einer Ausnahme führen.

Vielleicht verrätst Du mal was Du *eigentlich* erreichen willst.

Re: Programm soll Konstanten erstellen

Verfasst: Montag 7. Oktober 2013, 11:56
von BlackJack
@Jina_Jita: Noch ein paar Anmerkungen:

Klammern um Bedingungen sind in Python nicht nötig.

Die ``while``-Schleife in `BruecheGenerieren()` ist eigentlich ein Fall für eine ``for``-Schleife. Das spart zwei Zeilen Quelltext und man sieht gleich im Schleifenkopf welchen Wert `Nenner` in jedem Schleifendurchlauf annimmt, ohne dass man in beziehungsweise am Ende der Schleife suchen muss wie `Nenner` aktualisiert wird.

Es werden einige sehr triviale Sachen an Namen gebunden. Das trägt nicht wirklich zum Verständnis bei sondern verlängert nur den Quelltext und führt unnötige Namen ein.

Das zusammensetzen von Zeichenketten und Werten mittels ``+`` und `str()` ist sehr unübersichtlich und eher BASIC als idiomatisches Python. Für diese Aufgabe hat Python Zeichenkettenformatierung mittels der `format()`-Methode auf Zeichenketten und Platzhaltern in der Zeichenkette. Da kann man dann sogar den selben Wert mehrfach verwenden und muss ihn nur einmal übergeben.

Wenn man die bis hierher angesprochenen Sachen ändert, dann sähe die `brueche_generieren()`-Funktion so aus:

Code: Alles auswählen

def brueche_generieren(maximal_nenner):
    for nenner in range(1, maximal_nenner + 1):
        for zaehler in range(1, nenner - 1):
            zaehler_test = zaehler
            nenner_test = nenner
            while nenner_test != 0:
                zaehler_test, nenner_test = (
                    nenner_test, zaehler_test % nenner_test
                )
            if zaehler_test == 1:
                print('B{0}_{1} = {0}/{1}'.format(zaehler, nenner))
Noch ein wenig verständlicher kann man das machen indem man den Test als eigene Funktion schreibt. Dann muss der Leser nicht überlegen was dieser Test eigentlich prüft, weil er einen Namen hat, der dass sagt.

Code: Alles auswählen

def ggt(a, b):
    while b != 0:
        a, b = b, a % b
    return a


def brueche_generieren(maximal_nenner):
    for nenner in range(1, maximal_nenner + 1):
        for zaehler in range(1, nenner - 1):
            if ggt(zaehler, nenner) == 1:
                print('B{0}_{1} = {0}/{1}'.format(zaehler, nenner))
Mir stellt sich allerdings immer noch die Frage nach welchen Kriterien hier Brüche generiert werden, beziehungsweise ob die Funktion wirklich das tut was sie soll. Wenn man 10 als Argument übergibt, ist es dann Absicht, dass die vier Werte 1/2, 2/3, 3/4, und 4/5 fehlen? Oder fehlen am Ende die anderen Brüche bei denen der Unterschied zwischen Zähler und Nenner 1 ist ebenfalls? Beschreib doch mal in Worten/mathematisch wie die Brüche aussehen sollen die von der Funktion generiert werden sollen.

Und immer noch unschön ist natürlich das generieren von Quelltext und eigentlich überhaupt, dass die Funktion auch die Ausgabe übernimmt. Man könnte stattdessen ein Wörterbuch erstellen, welches Tupel aus Zähler und Nenner auf die Gleitkommazahl abbildet:

Code: Alles auswählen

def brueche_generieren(maximal_nenner):
    result = dict()
    for nenner in range(1, maximal_nenner + 1):
        for zaehler in range(1, nenner - 1):
            if ggt(zaehler, nenner) == 1:
                result[(zaehler, nenner)] = zaehler / nenner
    return result

Re: Programm soll Konstanten erstellen

Verfasst: Montag 7. Oktober 2013, 15:29
von Jina_Jita
Danke für die Hinweise mit der for-Schleife und so :)
Das eigentliche Ziel dieser Programe soll sein Dezimalzahlen mit Nachkommastellen in Brüche umzuformen.
Dazu wird die Zahl in eine Liste mit Brüchen einsortiert, und der Wert welcher am Nächsten dran liegt wird als ungefährer Wert ausgegeben.
Die Brüche welche generiert werden sollen sind alle Brüche bis zu einem eingegenbenen Nenner zum Beispiel wird 4 eingegeben und das Programm probiert alle Brüche aus, jedoch werden alle kürzbaren Brüche aussortiert:
  • 1/2
    1/3
    2/3
    3/3 wird aussortiert
    1/4
    2/4 wird aussortiert
    3/4
    4/4 wird aussortiert

Re: Programm soll Konstanten erstellen

Verfasst: Montag 7. Oktober 2013, 15:46
von BlackJack
@Jina_Jita: Also sind die bisher berechneten Werte nicht vollständig weil von Deiner Funktion 2/3 und 3/4 auch nicht berechnet werden.

Nach der Beschreibung des *eigentlichen* Ziels reelle Zahlen in Brüche umzuwandeln verstehe ich den Sinn dieser Liste mit Brüchen nicht. Eine gegebene reelle Zahl wandelt man von Hand doch auch einfacher um, ohne dass man eine Liste mit Brüchen benötigt. Man muss doch nur schauen wie viele Nachkommastellen die Zahl hat und entsprechend einen Zähler für die Nachkommastellen (ohne ein Komma) im Nenner wählen und dann den Bruch kürzen. Also 0,5 = 5/10 = 1/2.

Oder man macht es sich ganz einfach und verwendet gleich den richtigen Datentyp: `fractions.Fraction`. Wichtig dabei: Wenn möglich nicht den Umweg über Gleitkommazahlen gehen, weil die inhärent ungenau sind. 0,1 lässt sich zum Beispiel nicht exakt als `float` speichern. Beispiel:

Code: Alles auswählen

In [65]: from fractions import Fraction

In [66]: Fraction(0.5)
Out[66]: Fraction(1, 2)

In [67]: Fraction(0.1)
Out[67]: Fraction(3602879701896397, 36028797018963968)

In [68]: Fraction('0.5')
Out[68]: Fraction(1, 2)

In [69]: Fraction('0.1')
Out[69]: Fraction(1, 10)

Re: Programm soll Konstanten erstellen

Verfasst: Montag 7. Oktober 2013, 16:37
von EyDu
Und wenn die Daten nur als float vorliegen, dann solltest du das Programm vernünftig algorithmisch lösen und nicht einfach alle Möglichkeiten durchprobieren. Hier ist zum Beispiel ein guter Ansatz beschrieben, welcher in einer Hand voll Zeilen umsetzbar ist. Damit kannst du dann auch die Genauigkeit der Annäherung festlegen.

Edit:

Code: Alles auswählen

import math

def faray(f, eps):
    assert 0.0 < f < 1.0

    a, b = 0, 1
    c, d = 1, 1
    
    while True:
        i, j = a + c, b + d
        x = float(i)/j
        
        if abs(x - f) <= eps:
            return i, j
        elif f < x:
            c, d = i, j
        else:
            a, b = i, j


for eps in (0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001,
            0.000000001, 0.0000000001, 0.00000000001):
    a, b = faray(math.pi - 3.0, eps)
    print "%d/%d = %.15f" % (a, b, float(a)/b)

Code: Alles auswählen

1/5 = 0.200000000000000
1/7 = 0.142857142857143
9/64 = 0.140625000000000
15/106 = 0.141509433962264
16/113 = 0.141592920353982
16/113 = 0.141592920353982
3423/24175 = 0.141592554291624
4543/32085 = 0.141592644537946
4687/33102 = 0.141592653011903
14093/99532 = 0.141592653618937
37576/265381 = 0.141592653581078
Mit dem eps muss man natürlich vorsichtig sein, denn auch hier spielen die Ungenauigkeiten von Floats rein. Daher bietet ggf. ein Test gegen den Nenner (maximale Größe) an.