restlose Division funktioniert nicht

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
#96*7*
User
Beiträge: 2
Registriert: Donnerstag 2. Mai 2013, 19:32

Heyho, ich bin neu in Python und benötige eure Hilfe. Um Python etwas zu üben, habe ich mir eine Aufgabe ergooglet.
http://www.informatik.uni-rostock.de/~n ... blatt1.pdf (Augabe Nr. 4)
Doch leider funktioniert mein Programm nicht. Aus irgendeinem Grund funktioniert die restlose Division nicht:

Code: Alles auswählen

#!/usr/bin/env python

a = float(raw_input("Bitte einen Euro-Betrag eingeben!\n"))
print a

#check if there are only 2 numbers after the point
if (a * 1000)%10  != 0:
	print "Bitte maximal 2 Nachkommastellen eingeben!"
	quit()

#calcualte which and how many conins are needed
#2,1,0.5,0.2,0.1,0.05,0.02,0.01

coins = [2,1,0.5,0.2,0.1,0.05,0.02,0.01]
needed_coins = [0 for i in range(0, len(coins))]

for i in range(0,len(coins)):
	needed_coins[i] = a // coins[i]
	a -= needed_coins[i] * coins[i]
	print "Brauche",int(needed_coins[i]),"x",coins[i],"Euro"
	#print a

print a
Ausgabe:

Code: Alles auswählen

#96*7*@computer:~/directory$ ./geld.py
Bitte einen Euro-Betrag eingeben!
9.45
9.45
Brauche 4 x 2 Euro
Brauche 1 x 1 Euro
Brauche 0 x 0.5 Euro
Brauche 2 x 0.2 Euro
Brauche 0 x 0.1 Euro
Brauche 0 x 0.05 Euro
Brauche 2 x 0.02 Euro
Brauche 0 x 0.01 Euro
0.01
Ich bin defintiv kein Informatikstudent und meine Hausaufgabe ist dies (leider) auch nicht.
mfg #96*7*
Zuletzt geändert von Anonymous am Donnerstag 2. Mai 2013, 19:45, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@#96*7*: Als erstes würde ich mal fragen wie Du ausgerechnet darauf kommst, dass ``//`` nicht funktioniert. Da gibt es ja noch andere Operationen.

Gratulation: Du hast die wunderbare Welt von Gleitkommezahlen entdeckt — und das die nicht genau sind. Rechne die Eingabe in Cents um und die Münzen ebenfalls in Cents und das Problem sollte weg sein. Alternativ gibt es in der Standardbibliothek das `decimal`-Modul.

Edit: Ich habe mal Dein auskommetiertes ``print a`` in der Schleife wieder aktiviert und die Gleitkommazahlen mit 50 Stellen nach dem Komma ausgegeben:

Code: Alles auswählen

Bitte einen Euro-Betrag eingeben!
9.45
9.45
Brauche 4 x 2 Euro
1.44999999999999928945726423989981412887573242187500
Brauche 1 x 1 Euro
0.44999999999999928945726423989981412887573242187500
Brauche 0 x 0.5 Euro
0.44999999999999928945726423989981412887573242187500
Brauche 2 x 0.2 Euro
0.04999999999999926725280374739668332040309906005859
Brauche 0 x 0.1 Euro
0.04999999999999926725280374739668332040309906005859
Brauche 0 x 0.05 Euro
0.04999999999999926725280374739668332040309906005859
Brauche 2 x 0.02 Euro
0.00999999999999926642013647892781591508537530899048
Brauche 0 x 0.01 Euro
0.00999999999999926642013647892781591508537530899048
Edit2: Das Problem ist übrigens schon der eingegebene Betrag, ohne das irgendwelche Rechenoperationen durchgeführt wurden:

Code: Alles auswählen

In [7]: '%.50f' % 9.45
Out[7]: '9.44999999999999928945726423989981412887573242187500'
BlackJack

@#96*7*: Noch ein paar Anmerkungen zum bisherigen Quelltext:

Namen sollten dem Leser verraten was der Wert dahinter im Kontext des Programms bedeutet. Das ist bei `a` ganz sicher nicht der Fall.

Die `quit()`-Funktion sollte nicht in Programmen verwendet werden. Die ist für das beenden einer interaktiven Shell gedacht. Die Dokumentation sagt dazu:
http://docs.python.org/2/library/constants.html?highlight=quit#quit hat geschrieben:They are useful for the interactive interpreter shell and should not be used in programs.
Die Formatierung des Quelltextes weicht von dem Richtlinien im Style Guide for Python Code ab. Insbesondere die Einrücktiefe sollte man beachten, aber auch die Leerzeichensetzung um Operatoren und Interpunktion, die der besseren Lesbarkeit dienen.

Eine Liste mit Nullen für `needed_coins` könnte man mit der Multiplikation auf Listen kürzer schreiben: ``[0] * len(coins)``. Aber dieses vorbelegen einer Liste mit Dummywerten, die dann in einer Schleife mittels Indexzugriff nacheinander durch den tatsächlich benötigten Wert ersetzt werden, ist kein idiomatisches Python. Man würde hier mit einer leeren Liste beginnen und die Werte dort mit der `append()`-Methode anhängen.

Ebenfalls ein „anti pattern” ist ``for i in range(len(sequence)):`` um dann `i` als Index für den Zugriff auf die Elemente von `sequence` zu verwenden. Man kann in Python *direkt* über die Elemente iterieren, ohne den unnötigen Umweg über einen Index. Sollte man *zusätzlich* einen Index benötigen, gibt es die `enumerate()`-Funktion. Und wenn man ”parallel” über die Werte von mehr als einer Sequenz iterieren möchte, gibt es `zip()` beziehungsweise `itertools.izip()`.

Wenn man in `needed_coins` nur die Anzahl speichert, hat man am Ende zwei parallele Listen bei denen Elemente mit dem gleichen Index zusammen gehören. So etwas sollte man vermeiden, weil es das Programm komplexer und fehleranfälliger macht. Man könnte in der Liste Tupel mit (Anzahl, Wert) der benötigten Münzen speichern.

Das Programm sähe dann in etwas „pythonischer” so aus:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8

COIN_VALUES = [2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01]


def main():
    amount = float(raw_input('Bitte einen Euro-Betrag eingeben!\n'))
    if (amount * 1000) % 10 != 0:
        print 'Bitte maximal 2 Nachkommastellen eingeben!'
    else:
        needed_coins = list()
        for coin_value in COIN_VALUES:
            count = int(amount // coin_value)
            if count:
                needed_coins.append((count, coin_value))
            amount -= count * coin_value
            print 'Brauche', count, 'x', coin_value, 'Euro'
        print needed_coins


if __name__ == '__main__':
    main()
Es hat natürlich immer noch das Problem mit der Ungenauigkeit von Gleitkommazahlen im Rechner.

Und die Ausgabe entspricht nicht der Aufgabenstellung, welche die Anzahl der Münzen insgesamt fordert. Die Beispielausgabe sieht nach Python 3 aus, denn in Python 2 sähe der € anders aus wenn man einfach so eine Liste mit Zeichenketten (egal ob `str` oder `unicode`) mit ``print`` ausgibt. Da müsste man sich in Python 2 etwas mehr Mühe geben und den Inhalt der Liste in eine Zeichenkette mit durch Kommas getrennten Werten umwandeln. Etwas was man IMHO auch in Python 3 hätte tun sollen, denn Listen sind nicht wirklich zur Anzeige für den Endbenutzer gedacht. IMHO.

Was Du ansonsten noch machen könntest, wäre das Problem auf Funktionen aufteilen. Eine übliche, grobe Aufteilung ist immer die Eingabe, die Verarbeitung, und die Ausgabe zu trennen.
#96*7*
User
Beiträge: 2
Registriert: Donnerstag 2. Mai 2013, 19:32

Danke dir! Ich habe keine so lange und ausführliche Antwort erwartet :wink:
Rmnpython
User
Beiträge: 5
Registriert: Dienstag 14. Juni 2016, 19:59

Ich habe diesen Code mal ausprobiert. Wir haben das Problem, dass dieses Programm meinchmal einen Cent zuviel oder einen Cent zu wenig ausgibt. Wir würden gerne wissen, woran dies liegt und wie wir es beheben können. Außerdem ist ja die Aufgabe nicht zu 100% erfüllt, denn das Programm zeigt nicht an wv. Münzen man mindestens braucht.
@BlackJack
BlackJack

@Rmnpython: Ich hatte da ja nur den Quelltext vom OP überarbeitet. Der Hinweis mit Gleitkommazahlen und am besten alles in Cent zu rechnen, also mit ganzen Zahlen, aus meinem anderen Beitrag bleibt bestehen.

Edit: Das hatte ich nach dem überarbeiteten Quelltext ja bereits geschrieben. :-)
Antworten