[Anfänger] Variable wird nciht mehr gebunden

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.
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

Hallo,
ich habe in einem Kurs eine Aufgabe bekommen und muss diese nun Lösen.
Keien Angst ich möchte nciht die Lösung von euch wissen sondern habe ein anderes Problem

In der while schleife die erste "if"
dort möchte ich guess neu binden -> guess = round((low+high)/2,2)

leider behält aber guess die ganze zeit den initialen wert den ich am Anfang des scripts vergeben habe

Ich zermarter mir nun shcon den ganzen Tag den kopf und weiß nicht wo mein Fehler liegt.

Achtung vor Codeausführung: durch dieses nicht binden ist eine endloss schleife entstanden

Über eine Antwort würde ich mich sehr freuen

Chris

Code: Alles auswählen

balance = 10000
annualInterestRate = 8.0
low = round(balance/12,2)
high = round(balance*(1+(annualInterestRate/12)**12)/12,2)
guess = (low+high)/2

def rechnen(x,y,z):    #x=balance, y=anualInterestRate, z=guess
    i = 0
    while i < 12:
        i = i+1
        x = (x - z) + ((x - z)*(y/12))
    return x

while round(rechnen(balance,annualInterestRate,guess),2) != 0:
    if round(rechnen(balance,annualInterestRate,guess),2) > 0:
        low = guess
        guess = round((low+high)/2,2)
        print guess,'goesser'
    if round(rechnen(balance,annualInterestRate,guess)) < 0:
        high = guess
        guess = round((low+high)/2,2)
        print guess,'kleiner'

print 'guess:',guess
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Chirs79: Du solltest mal Deine Startbedingungen überprüfen. Wann kann das Verfahren überhaupt funktionieren? Was bewirkt das Runden? Versuche die Funktion nur einmal und nicht dreimal pro Durchgang zu berechnen.
BlackJack

@Chirs79: Statt einbuchstabiger Namen die in einem Kommentar durch jeweils ein Wort erklärt werden, hätte man besser gleich das jeweilige Wort genommen. Dann braucht man den Kommentar nicht. Was die Namensschreibweise angeht wäre ein Blick in den Style Guide for Python Code angebracht.
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

erstmal vielen dank für eure schnellen Antworten.
Ich habe die Berechnung nun anstatt dreimal einmal in der while drinnen.
Auch habe ich versucht auf die schnelle etwas aus dem Style guide zu beachten ( die def ist nun hoffentlich ok)

Allerdings habe ich immer noch das gleich problem.
Er/Sie durchläuft andauernd die erste if und ändert denw ert von guess nicht :-(

Code: Alles auswählen

balance = 10000
annualInterestRate = 8.0
low = balance/12
high = balance*(1+(annualInterestRate/12)**12)/12
guess = (low + high)/2

def rechnen(
        balance,anualInterestRate,guess):
    i = 0
    while i < 12:
        i = i+1
        balance = (balance - guess) + ((balance - guess)*(anualInterestRate/12))
    return balance

balance1 = round(rechnen(balance,annualInterestRate,guess),2) 

while round(balance1,2) != 0.00:
    balance1 = rechnen(balance,annualInterestRate,guess)
    if balance1 > 0.00:
        low = guess
        guess = (low+high)/2
        print guess,'goesser'
    if balance1 < 0.00:
        high = guess
        guess = (low+high)/2
        print guess,'kleiner'

print 'guess:',round(guess,2)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Chirs79: Du solltest mal Deine Startbedingungen überprüfen.
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

Sirius3 hat geschrieben:@Chirs79: Du solltest mal Deine Startbedingungen überprüfen.
Ist der Fehler darin so offensichtlich?

Wenn ja bin ich nach dem ganzen Tag davor sitzten wahrschenlich mit einem Tunnelblick gestraft.
Ich glaube dann sollte ich nach einer Nacht schlafen da ncohmla drauf schauen.
Ich kann aktuell nichts entdecken was mich irgendworan zweifeln lässt :K

Danke aber tortzdem. Vllt. geht mir morgen ja ein licht auf :idea:
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

Ok, frische Kopf frische idee

So mittlerweile habe ich rausgefunden, dass die VariablenBindung funktioniert.
Nur leider wrd der die Variable balance in def rechnen nicht kleiner sondern größer und dies verursacht die Störung.

Nun bin ich wieder bei den Startbedingungen angekommen und sehe bei der Berechnung von high den Fehler.

Ein anderes script welches die "Berechnung" ohne bisection durchführt liefert mir ein ergebnis für "guess" welche weit über high liegt.
Also muss in der Berechnungsformel mein Fehler liegen

Was mir bei der Entdeckung geholfen hat war das Script nicht einfach so laufen zu lassen sondern die while schleife auf eine gerineg anzahl (30) zu begrenzen und mir die wert ausgeben zu lassen.

Danke soweit schonmal an eure Hartnäckigkeit -> Prüfe die Startbedingungen ;-)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Chirs79: ganz sinnvoll ist es, Bedingungen auch explizit im Programm zu schreiben, dafür gibt es "assert". In Deinem Fall also:

Code: Alles auswählen

assert rechnen(balance,annualInterestRate,high) > 0, "Funktionswert bei high zu klein %f" % rechnen(balance,annualInterestRate,high)
assert rechnen(balance,annualInterestRate,low) < 0, "Funktionswert bei low zu groß %f" % rechnen(balance,annualInterestRate,low)
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

@Sirius3:
ähm ja danke. Da ich erst seit 2Wochen in der Mateire drinnen bin, kann ich dir bei dem Vorschlag nicht ganz folgen bzw verstehe ihn einfahc nicht :-(

Ich habe es weiter angepasst aber irgendwie habe ich wohl noch einen denkfehler bei der if clause
epsilon habe ich eingefügt weil eine abweichung von ein paar cent ok sind

Code: Alles auswählen

balance = 320000
annualInterestRate = 0.2
epsilon = 0.01
low = balance/12.0
high = (balance*(1+annualInterestRate/12)**12)/12
guess = (low + high)/2

def rechnen(
        balance,anualInterestRate,guess):
    i = 0
    while i < 12:
        i = i+1
        balance = (balance - guess) + ((balance - guess)*(anualInterestRate/12))
        return balance

balance1 = rechnen(balance,annualInterestRate,guess) 

i = 0
while abs(balance1 - guess) >= epsilon:
    if balance1 > guess:
        high = guess
    else:
        low = guess
    guess = (low+high)/2
    balance1 = rechnen(balance,annualInterestRate,guess)
print 'guess:',round(guess,2)
Falls es jemanden interessiert habe ich zu der Aufgabe auch Testcases bekommen:

Test Case 1:
balance = 320000
annualInterestRate = 0.2

Result Your Code Should Generate:
-------------------
Lowest Payment: 29157.09



Test Case 2:
balance = 999999
annualInterestRate = 0.18

Result Your Code Should Generate:
-------------------
Lowest Payment: 90325.03
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Chirs79: So viel zu folgen gibt es nicht; Du prüfst am Anfang, ob die Startwerte so sind, dass das Verfahren konvergiert. Statt i händisch zu zählen solltest Du eine for-Schleife benutzen
eckhard
User
Beiträge: 33
Registriert: Montag 14. Dezember 2015, 10:06
Wohnort: Karlsruhe

Eine Frage zur Zeile 25:

Code: Alles auswählen

balance1 = rechnen(balance,annualInterestRate,guess)
rechnen wird immer mit dem sich nicht ändernden Wert balance gefüttert.
Dann wird rechnen auch immer den gleichen Wert liefern.
Sollte es vielleicht

Code: Alles auswählen

balance1 = rechnen(balance1,annualInterestRate,guess)
heißen?
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

eckhard hat geschrieben:Eine Frage zur Zeile 25:

Code: Alles auswählen

balance1 = rechnen(balance,annualInterestRate,guess)
rechnen wird immer mit dem sich nicht ändernden Wert balance gefüttert.
Dann wird rechnen auch immer den gleichen Wert liefern.
Sollte es vielleicht

Code: Alles auswählen

balance1 = rechnen(balance1,annualInterestRate,guess)
heißen?
Hi,
das ist korrekt das der wert sich nicht ändert da sich guess ändert.

Balance ist fest vorgegeben sowie annualInterestRate

Es geht dabei um die Abzahlung einer Kreditkarte/dahrlehen etc.

Balance ist die Schuld am Tag 0. Nach einem Jahr soll alles abbezahlt sein.
Um dies unter berücksichtigung der Monatlichen Zinsen zu berechnen soll erstmal mit guees = (high+low)2 ein startwert als annahme erzeugt werden
Die Berechnung läuft dann 12mal durhc (1 JAhr) und hat im optimalfall am ende eine balance1 von 0

liegt der Wert über 0 (zu wenig abbezhalt) muss also mein low höher werden um ein neues mittel zu finden, bei balance1 < 0 also zuviel bezahlt muss mein high niedriger werden

Ich hoffe ich konnt so etwas Klarheit schaffen

Danke für das interesse :-)
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

So.
Erstmal vielen Dank für die Unterstützung.
Mittlerweile ist das Problem gelöst.

Was waren meine Fehler?:

1. bei der Startdefinition "high" fehlte vorne eine Klammer
2. bei der "Umformulierung der Funktion rechnen() von xyz zu sprechenden Namen ist mir ein Fehler unterlaufen
3. Währen der Fehlersuche vertauschte ich dann hin und wieder mal high und low in der if abfrage und veränderter die while Bedingung weil ich den Fehler dort vermutete

Kann ich das Thema irgendwie auf gelöst setzen?

Hier nun der Funktionierende Code:

Code: Alles auswählen

balance = 999999
annualInterestRate = 0.18
epsilon = 0.05
low = balance/12.0
high = (balance*(1+annualInterestRate/12)**12)/12
guess = (low + high)/2
def rechnen(x,y,z): #x=balance, y=anualInterestRate, z=guess
i = 0
while i < 12:
i = i+1
x = (x - z) + ((x - z)*(y/12))
return x 
balance1 = rechnen(balance,annualInterestRate,guess)

while abs(balance1) >= epsilon:
if balance1 > 0.00:
low = guess
else:
high = guess
guess = (low+high)/2
balance1 = rechnen(balance,annualInterestRate,guess)

print 'Lowest Payment:',round(guess,2)
BlackJack

@Chirs79: Die Themen werden hier üblicherweise nicht als gelöst markiert.

Wenn man `high` nicht so kompliziert macht, dann kann man auch keinen Fehler machen. Ich hätte da einfach den Gesamtbetrag genommen, denn da sieht man sofort ohne lange über eine Formel nachdenken zu müssen, dass der Betrag grösser ist als der optimale Betrag pro Monat.

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function


def calculate_one_year(balance, annual_interest_rate, monthly_payment):
    for _ in xrange(12):
        balance -= monthly_payment
        balance *= 1 + annual_interest_rate / 12
    return balance


def calculate_lowest_monthly_payment(balance, annual_interest_rate):
    lower_limit = balance / 12
    upper_limit = balance
    monthly_payment = 0.01  # Just in case `balance` is very small.
    while abs(lower_limit - upper_limit) >= 0.001:
        monthly_payment = (lower_limit + upper_limit) / 2
        if calculate_one_year(
            balance, annual_interest_rate, monthly_payment
        ) > 0:
            lower_limit = monthly_payment
        else:
            upper_limit = monthly_payment
    return monthly_payment
        

def main():
    for balance, annual_interest_rate in [(320000, 0.2), (999999, 0.18)]:
        print(
            'Lowest Payment: {0:.2f}'.format(
                calculate_lowest_monthly_payment(balance, annual_interest_rate)
            )
        )


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Chirs79: ein "gelöst" gibt es hier nicht, weil es immer noch etwas zu sagen gibt :D

z.B: auf oberster Ebene sollte nichts außer Konstanten und Definitionen stehen, die Variablennamen in rechnen sollten mehr Aussagekraft haben, statt sie in einem Kommentar erklären zu müssen. Statt while solltest Du eine for-Schleife verwenden. Durch Umwandeln in eine while-True-Schleife spart man sich kopierten Code. Statt explizit zu runden läßt man das die Stringformatierung machen:

Code: Alles auswählen

def rechnen(balance,anualInterestRate,guess):
    for _ in range(12):
        balance = (balance - guess) * (1 + anualInterestRate/12)
    return balance

def main():
    balance = 999999
    annualInterestRate = 0.18
    epsilon = 0.05
    low = balance/12.0
    high = (balance*(1+annualInterestRate/12)**12)/12

    while True:
        guess = (low + high) / 2
        balance1 = rechnen(balance, annualInterestRate, guess)
        if abs(balance1) < epsilon:
            break
        if balance1 > 0:
            low = guess
        else:
            high = guess
    
    print 'Lowest Payment: %.2f' % guess

if __name__ == '__main__':
    main()
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

Dann hole ich mal aus:

WOW

Meins sieht gewollt aus und deins gekonnt :-)

Ich habe aber noch kleine Fragen dazu:

Code: Alles auswählen

for _ in range(12):
Der Underscore ist das eine Variable wie wenn ich i benutzen würde oder hat das noch ne andere Beudeutung?
Oder ist das einfach State of the art oder sozusagen dein persönlicher Favorit?

Code: Alles auswählen

if __name__ == '__main__':
main()
das versteh ich irgendiwe nicht so ganz. Klar ist das der "Startbefehl" aber das __name__ == ergibt sich mir nicht

Gruß Chris

P.S.: Ist es eigentlich Möglich den Usernamen im Forum zu ändern? Haben ne Wuchstaben Verbechselung drinne ;-) Chirs79 -> Chris79 ?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Chirs79 hat geschrieben:Der Underscore ist das eine Variable wie wenn ich i benutzen würde oder hat das noch ne andere Beudeutung?
Ja. Es ist nur eine reguläre Variable aber man benutzt _ per Konvention für Variablen die man anlegen muss aber nicht benutzt werden.
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

DasIch hat geschrieben:
Chirs79 hat geschrieben:Der Underscore ist das eine Variable wie wenn ich i benutzen würde oder hat das noch ne andere Beudeutung?
Ja. Es ist nur eine reguläre Variable aber man benutzt _ per Konvention für Variablen die man anlegen muss aber nicht benutzt werden.
Ok, danke, das habe ich verstanden :-)
Chris79
User
Beiträge: 15
Registriert: Dienstag 26. Januar 2016, 21:45

Chirs79 hat geschrieben:

Code: Alles auswählen

if __name__ == '__main__':
main()
das versteh ich irgendiwe nicht so ganz. Klar ist das der "Startbefehl" aber das __name__ == ergibt sich mir nicht

Gruß Chris

P.S.: Ist es eigentlich Möglich den Usernamen im Forum zu ändern? Haben ne Wuchstaben Verbechselung drinne ;-) Chirs79 -> Chris79 ?
Kann mir bitte das nochmal jemand näher bringen?

Danke
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

__name__ ist eine Variable die vom Interpreter definiert wird und ist ein String mit dem Namen des Moduls. Wenn das Modul direkt ausgeführt wird anstatt importiert zu werden, ist der Name des Moduls __main__. Die if Konstruktion führt also main() nur aus wenn das Modul direkt ausgeführt wird und nicht wenn es importiert wird.
Antworten