Seite 12 von 14

Verfasst: Sonntag 24. Mai 2009, 17:01
von BlackJack
@SuperDAU²: Du berechnest immer noch ``betrag - scheine`` zweimal. Wie gesagt, die Bedingung in der inneren ``while``-Schleife kann man so umstellen, dass man da nichts ausrechnen muss.

Und statt der äusseren ``while``-Schleife würde ich eine ``for``-Schleife über `scheine` schreiben. Dann wirst Du das `i` los. Wenn Du nicht alle Scheine durchlaufen möchtest, auch wenn der `betrag` schon bei 0 ist, kannst Du die Schleife auch immer noch durch ein `break` verlassen.

Du gehst auch nicht mit dem Fall um, wenn der Anwender einen Betrag eingibt, der nicht durch die vorhandenen Scheine abgebildet werden kann.

Verfasst: Sonntag 24. Mai 2009, 17:02
von SuperDAU²
Ich habe es mal probiert:

Code: Alles auswählen

#!/usr/bin/env python
# Bankautomat mit Hausfrauenmischung

scheine = [500, 200, 100, 50, 20, 10, 5]
scheine2 = [5, 10, 20, 50, 100, 200, 500]
trennzeichen = ', '
auszahlung = []
betrag = int(raw_input('Wieviel soll ausgezahlt werden? '))

for i in range(0, 7):
    if betrag >= scheine2[i]:
        auszahlung.append(str(scheine2[i]))
        betrag = betrag - scheine2[i]
        continue

while betrag > 0:
    while betrag >= scheine[i]:
        auszahlung.append(str(scheine[i]))
        betrag = betrag - scheine[i]
    i = i + 1

print 'Ihre Auszahlung:', trennzeichen.join(auszahlung)
Allerdings habe ich folgende Ausgabe:

Code: Alles auswählen

Wieviel soll ausgezahlt werden? 675
Ihre Auszahlung: 5, 10, 20, 50, 100, 200, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
Das gibt zwar exakt 675, aber trotzdem wird die while-Schleife nicht richtig ausgeführt.

Edit: Problem selbst gelöst:

Code: Alles auswählen

#!/usr/bin/env python
# Bankautomat

scheine = [500, 200, 100, 50, 20, 10, 5]
scheine2 = [5, 10, 20, 50, 100, 200, 500]
trennzeichen = ', '
u = 0
auszahlung = []
betrag = int(raw_input('Wieviel soll ausgezahlt werden? '))

for i in range(0, 7):
    if betrag >= scheine2[i]:
        auszahlung.append(str(scheine2[i]))
        betrag = betrag - scheine2[i]
        continue

while betrag > 0:
    while betrag >= scheine[u]:
        auszahlung.append(str(scheine[u]))
        betrag = betrag - scheine[u]
    u = u + 1

print 'Ihre Auszahlung:', trennzeichen.join(auszahlung)
Um eine bessere Übersicht zu erhalten, hätte ich gerne etwas wie das

Code: Alles auswählen

Ihre Auszahlung: 2*200, 1*100, 2*50, 3*20, 1*10, 1*5

Verfasst: Sonntag 24. Mai 2009, 17:12
von BlackJack
SuperDAU²: Was denkst Du was `i` in Zeile 15 für einen Wert hat? Überprüf mal ob Deine Erwartung mit der Realität übereinstimmt.

Und die erste Schleife sollte definitiv über die `scheine2` gehen, ohne den Index. Und man sollte dafür keine extra Liste angeben, wenn man die auch aus `scheine` erstellen kann -> `reversed()`.

Das ``continue`` ist sinnfrei.

Verfasst: Sonntag 24. Mai 2009, 17:55
von SuperDAU²
Hier nochmals der überarbeitete Code:

Code: Alles auswählen

#!/usr/bin/env python

scheine = [5, 10, 20, 50, 100, 200, 500]
trennzeichen = ', '
u = 0
auszahlung = []
betrag = int(raw_input('Wieviel soll ausgezahlt werden? '))

for i in range(0, 7):
    if betrag >= scheine[i]:
        auszahlung.append(str(scheine[i]))
        betrag = betrag - scheine[i]
scheine.reverse()
while betrag > 0:
    while betrag >= scheine[u]:
        auszahlung.append(str(scheine[u]))
        betrag = betrag - scheine[u]
    u = u + 1

print 'Ihre Auszahlung:', trennzeichen.join(auszahlung)
Mir kam die Idee es so umzuschreiben, aber dann bleibt das Programm stehen.

Code: Alles auswählen

#!/usr/bin/env python

scheine = [5, 10, 20, 50, 100, 200, 500]
trennzeichen = ', '
u = 0
auszahlung = []
betrag = int(raw_input('Wieviel soll ausgezahlt werden? '))

for i in range(0, 7):
    if betrag >= scheine[i]:
        auszahlung.append(str(scheine[i]))
        betrag = betrag - scheine[i]
scheine.reverse()
for u in range(0, 7):
    while betrag > 0:
        while betrag >= scheine[u]:
            auszahlung.append(str(scheine[u]))
            betrag = betrag - scheine[u]

print 'Ihre Auszahlung:', trennzeichen.join(auszahlung)
Wo ist der Fehler? Der Interpreter bleibt in Zeile 16 stecken.

Verfasst: Sonntag 24. Mai 2009, 18:18
von numerix
Da du die Scheine umkehrst, ist scheine zu Beginn 500. Ist der Betrag < 500, dann ist die Bedingung der inneren while-Schleife nie erfüllt, folglich wird der Betrag nicht verringert und die äußere while-Schleife wird zur Endlosschleife.

Verfasst: Sonntag 24. Mai 2009, 18:19
von BlackJack
SuperDAU²: Vergiss mal die erste auf Modulebene Schleife und gehe die zweite in Gedanken mit dem Betrag 15 mal Schritt für Schritt in Gedanken durch. Dann solltest Du drauf kommen warum das eine Endlosschleife werden kann.

Verfasst: Sonntag 24. Mai 2009, 19:15
von SuperDAU²
Ich habe das Problem jetzt verstanden. Aber ich finde keine andere Lösung dazu als die vorherige Variante.

Was ist mit meiner Frage zwecks der Übersichtlichkeit? Wie bekomme ich eine Ausgabe in der Art von "6*500, 1*200, 1*100, 1*50, 3*20, 1*10, 2*5"?

Verfasst: Sonntag 24. Mai 2009, 19:48
von snafu
Was ist denn das genaue Problem? Falls es um die Anzahl der Vorkommnisse einer Sorte geht: Listen haben die Methode `count()`, die als Argument die Bezeichnung des zu zählenden Elements nimmt.

Verfasst: Sonntag 24. Mai 2009, 20:08
von EyDu
@BlackJack: Danke für den Hinweis. Ich hatte die Umformung wegen der besseren Lesbarkeit nicht vorgenommen. Für mein Produktivsystem werde ich dies nun natürlich aus Performance-Gründen entsprechend ändern.

Verfasst: Sonntag 24. Mai 2009, 22:44
von SuperDAU²
Vielen Dank an snafu für den Tipp mit "count()". Irgendwie werde ich das Gefühl nicht los, dass man "auszahlungsliste" leichter hätte erstellen können. Geht das etwa wieder mit einer for-Schleife?

Code: Alles auswählen

#!/usr/bin/env python
# Bankautomat

scheine = [5, 10, 20, 50, 100, 200, 500]
u = 0
multi = '*'
trenn = ', '
trenn2 = ''
auszahlung = []
betrag = int(raw_input('Wieviel soll ausgezahlt werden? '))

for i in range(0, 7):
    if betrag >= scheine[i]:
        auszahlung.append(str(scheine[i]))
        betrag = betrag - scheine[i]

scheine.reverse()

while betrag > 0:
    while betrag >= scheine[u]:
        auszahlung.append(str(scheine[u]))
        betrag = betrag - scheine[u]
    u = u + 1

auszahlungsliste = [str(auszahlung.count('500')), multi, str(scheine[0]), trenn,
str(auszahlung.count('200')), multi, str(scheine[1]), trenn,
str(auszahlung.count('100')), multi, str(scheine[2]), trenn,
str(auszahlung.count('50')), multi, str(scheine[3]), trenn,
str(auszahlung.count('20')), multi, str(scheine[4]), trenn,
str(auszahlung.count('10')), multi, str(scheine[5]), trenn,
str(auszahlung.count('5')), multi, str(scheine[6])]

print 'Ihre Auszahlung:', trenn2.join(auszahlungsliste)
Außerdem habe ich eine weitere Frage: Wenn ein Schein in "auszahlung" nicht vorkommt, wie kann ich ihn dann in der "auszahlungsliste" entfernen?

Oder habt ihr eine andere bzw bessere Lösung für das Problem?

Verfasst: Sonntag 24. Mai 2009, 22:51
von BlackJack
@SuperDAU²: Schau Dir mal die `zip()`-Funktion an, Zeichenkettenformatierung mittels ``%``-Operator, und dann werd endlich mal `i` und `u` los, indem Du direkt über die Elemente der Liste iterierst.

Zumindest solltest Du aber die Initialisierung von `u` direkt vor der Schleife machen und nicht irgendwo oben, als weit weg von der Stelle wo's letztendlich gebraucht wird.

Verfasst: Sonntag 24. Mai 2009, 22:53
von EyDu
War doch klar, dass Werte von Geldscheinen auf den ersten binären Nachkommastellen von PI beruhen:

Code: Alles auswählen

>>> reduce(lambda a,b: a+[(a[-1]*(4+b))/2], tuple((int(math.pi*2**8)>>i)&1 for i in range(7, 1, -1)), [5])
[5, 10, 20, 50, 100, 200, 500]
Sollten also neue Noten eingeführt werden, so ergeben sich die Beträge:

Code: Alles auswählen

>>> reduce(lambda a,b: a+[(a[-1]*(4+b))/2], tuple((int(math.pi*2**18)>>i)&1 for i in range(16, 1, -1)), [5])
[5, 10, 25, 50, 100, 250, 500, 1000, 2000, 4000, 10000, 25000, 62500, 156250, 390625, 976562]

Verfasst: Sonntag 24. Mai 2009, 23:06
von SuperDAU²
@BlackJack:
Den Tipp mit zip() habe ich verstanden, aber was du mit
Zeichenkettenformatierung mittels ``%``-Operator, und dann werd endlich mal `i` und `u` los, indem Du direkt über die Elemente der Liste iterierst.
meinst, weiß ich leider nicht. Könntest du mir bitte ein Beispiel geben?

PS: Hast du die Fragen im meinem letzten Post gesehen?

MfG

Verfasst: Sonntag 24. Mai 2009, 23:11
von EyDu
String-Formatierung:

Code: Alles auswählen

>>> "formatiere mich; int: %d, str: %s, float: %f" % (42, "spam", 3.1415)
'formatiere mich; int: 42, str: spam, float: 3.141500'
Indizes:

Code: Alles auswählen

for schein in scheine:
    if betrag >= scheine:
        auszahlung.append(str(scheine))
        betrag = betrag - scheine

Verfasst: Sonntag 24. Mai 2009, 23:42
von BlackJack
Ich hätte da jetzt noch diesen schicken 3-Zeiler im Angebot:

Code: Alles auswählen

S=500,200,100,50,20,10,5;f=reduce;print', '.join('%d*%d'%(a,b) for a,b in zip((
lambda r,x:[a+b for a,b in zip(f(lambda (a,b),c:(a+[b//c],b%c),S,([],x))[0],
r[::-1])])(*f(lambda(a,b),c:(a+[b>=c],b-(b>=c)*c),S[::-1],([],input()))),S))
Berechnet die Haushaltsmischung. Falls beim Betrag ein Rest bleiben sollte, also der Betrag nicht durch 5 teilbar ist, fällt dieser Rest einfach weg. Testlauf:

Code: Alles auswählen

bj@s8n:~$ python forum2.py
355
0*500, 0*200, 2*100, 2*50, 2*20, 1*10, 1*5
Ein Beispiel für Zeichenkettenformatierung ist auch drin. :-)

Verfasst: Sonntag 24. Mai 2009, 23:46
von SuperDAU²
@EyDu:
Das mit der Stringformatierung hab ich jetzt auch verstanden. Wo mir das weiterhilft muss ich noch herausfinden :P

und zu dem 2. Codeblock:
Wenn ich den in mein Programm einfüge, wird dieser Block einfach übersprungen. Außerdem verstehe ich nicht, womit "schein" definiert ist.

@BlackJack:
Toll, wenn du mir den 3-Zeiler noch erklären kannst. :roll:

Verfasst: Montag 25. Mai 2009, 00:02
von EyDu
Immerhin passt der Code schon auf eine Bildschirmzeile:

Code: Alles auswählen

print reduce(lambda(a,c),b:(a%b,c+(a/b and[(a/b,b)]or[])),[500,200,100,50,20,10,5],(input(),[]))
print reduce(lambda(a,c),b:(a%b,c+[(a/b,b)]),[500,200,100,50,20,10,5],(input(),[]))

Code: Alles auswählen

sebastian@sepp:~$ python test.py
227
(2, [(1, 200), (1, 20), (1, 5)])
227
(2, [(0, 500), (1, 200), (0, 100), (0, 50), (1, 20), (0, 10), (1, 5)])
Und der Restbetrag erledigt sich ganz von alleine.

@SuperDAU²: du sollst den Code nicht einfach bei dir einfügen, er ersetzt deine erste for-Schleife des Codes. Wenn du das mit "schein" nicht verstehst, dann wirf noch einmal einen Blick ins Tutorial.

Edit: Indizes gespart.
Edit2: Modulo existiert... :roll:

Verfasst: Montag 25. Mai 2009, 00:18
von BlackJack
Da waren doch glatt noch 21 Zeichen "zuviel" drin:

Code: Alles auswählen

S=500,200,100,50,20,10,5;f=reduce;r,x=f(lambda(a,b),c:([b>=c]+a,b-(b>=c)*c),
S[::-1],([],input()));print', '.join('%d*%d'%(a,b)for a,b in zip([a+b for a,b
in zip(f(lambda(a,b),c:(a+[b/c],b%c),S,([],x))[0],r)],S))
@SuperDAU²: Was ist denn daran nicht verständlich!? ;-)

Verfasst: Montag 25. Mai 2009, 00:41
von EyDu
@BlackJack: ich hab mir deine Version mal genauer angeschaut, da haben wir wohl nahezu identischen Code produziert.

Und mal mit Formatierung:

Code: Alles auswählen

print ", ".join("%d*%d"%x for x in reduce(lambda(a,c),b:(a%b,c+[(a/b,b)]),[500,200,100,50,20,10,5],(input(),[]))[1])

Code: Alles auswählen

sebastian@sepp:~$ python test.py
227
0*500, 1*200, 0*100, 0*50, 1*20, 0*10, 1*5

Verfasst: Montag 25. Mai 2009, 06:45
von BlackJack
@EyDu: Jup, das sieht von der Struktur her aus wie meins. Nur das ich die Haushaltsmischung noch mit aufgenommen habe. Jetzt mit 41 Zeichen weniger, bin jetzt bei 173:

Code: Alles auswählen

f=reduce;r,x=f(lambda(a,b),c:([(b>=c,c)]+a,b-(b>=c)*c),[5,10,20,50,100,200,500],
([],input()));print', '.join(f(lambda(a,b),(c,d):(a+['%d*%d'%(b/d+c,d)],b%d),r,
([],x))[0])
Die Zeichenkettenformatierung könntest Du auch noch weiter reinziehen und damit den Generatorausdruck einsparen.