Genaue Berechnungen

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
DaSch
User
Beiträge: 72
Registriert: Mittwoch 28. März 2007, 20:04
Kontaktdaten:

Also wie krieg ich das hin damit mit Python etwas mit decimalstellen genau berechnet, ich schreib grad ein Prog dass Integrale Berechnen soll und da kommen ganz falsche Ergebnisse raus weil der irgendwie das float falsch rundet.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,

float soll "falsch runden"? Das glaube ich nicht ;-).
Schau mal hier: http://www.python.org/doc/2.5/tut/node16.html
Außerdem gibt es bereits das scipy-Modul mit diversen Integrationsroutinen.

Magst Du ein wenig Beispielcode posten, damit wir herausfinden können, wo evtl. der Fehler liegt?

Gruß,
Christian
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Oder das Modul decimal hilft dir weiter.

Gruss
DaSch
User
Beiträge: 72
Registriert: Mittwoch 28. März 2007, 20:04
Kontaktdaten:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import decimal
import time
grenze1 = None
grenze2 = None
while True:
    grenze1 = raw_input("erste Grenze: ")
    try:
        grenze1 = int(grenze1)
        print "Sie haben",grenze1,"eingegeben"
        str = raw_input("Ist das okay [y/n] ")
        input = str.find("y")
        if input==0:
            break
    except:
        print "Bitte eine Zahl eingeben"
    
while True:
    grenze2 = raw_input("zweite Grenze(größer als erste): ")
    try:
        grenze2 = int(grenze2)
        if grenze2 < grenze1:
            print "zweite Grenze ist kleiner als erste Grenze"
        else:
            print "Sie haben",grenze2,"eingegeben"
            str = raw_input("Ist das okay [y/n] ")
            input = str.find("y")
            if input==0:
                break
    except:
        print "Bitte eine Zahl eingeben"
print grenze1,grenze2
integral = 0
grenze1 = float(grenze1)
grenze2 = float(grenze2)
interval= 0.1
#Hier die Funktion eintragen
funktion = lambda x: x
i=grenze1
print interval,grenze1,grenze2
time.sleep(10)
while i < (grenze2):
    integral += (((funktion(i)+funktion((i+interval))/2)*interval))
    print integral,i
    i += interval
print "Das Integral ist",integral
Das ist jetzt so ziemlich simple und die Funktion wird im ProgCode eingegeben aber des ist auch mehr so ein Test
BlackJack

Wenn es auch eine Bibliothek sein darf, die nicht in der Standardbibliothek enthalten ist, dann gibt's auch eine Anbindung an die gmp-Bibliothek: http://code.google.com/p/gmpy/

Das Problem bei Deinem Quelltext ist wahrscheinlich, dass sich durch die vielen Additionen beim Intervall die Rechenungenauigkeiten addieren.

Ich habe einfach mal folgende Schleife an das Programm angehängt, die bekommt genauere Ergebnisse heraus:

Code: Alles auswählen

integral = 0
interval = 0.1
for step in xrange(int((grenze2 - grenze1) / interval)):
    point = step * interval
    integral += (funktion(point) + funktion(point + interval)) / 2 * interval
print integral
Numerik mit Fliesskommazahlen sind ein spannendes Thema, allerdings muss man auch ziemlich aufpassen.
rafael
User
Beiträge: 189
Registriert: Mittwoch 26. Juli 2006, 16:13

Und wo verwendest du da decimal?

Wandel die raw_inputs doch in decimal.Decimal Objekte um. Beispielsweise

Code: Alles auswählen

# ...
    try:
        grenze1 = decimal.Decimal(grenze1)
        # ...
Bernhard
User
Beiträge: 136
Registriert: Sonntag 15. Januar 2006, 20:31
Wohnort: Greifswald
Kontaktdaten:

Ich entschuldige mich vorneweg, falls ich das Offensichtliche übersehen haben sollte, aber ich habe das Problem noch nicht verstanden. DaSch: Mit welchen Eingabewerten kommt bei Dir welches falsche Ergebnis heraus und welches richtige sollte heraus kommen?
  • integral += (((funktion(i)+funktion((i+interval))/2)*interval))
hört sich für mich so an, als könnten da bei ungeschickter Wahl der Grenze1 und des Intervalls sehr schnell auch Int-Rechnungen anstelle von Float-Rechnungen durchgeführt werden. Damit wären dann ggf. auch "Rundungsfehler" schnell erklärt. Daher meine Fragen: Gib mal Beispielwerte an und falls Deine Beispielwerte ganzzahlig sind, dann ersetze 2 durch 2.0 und versuche es erneut.

Gruß,
Bernhard
BlackJack

Bei 0 und 1 sollte 0.5 herauskommen, bei DaSch's Implementierung kommt aber 0.88 heraus.

An der Ganzzahl kann es nicht liegen, da immer eine Fliesskommazahl durch die 2 geteilt wird, eine 2.0 ändert an der Stelle nichts am Ergebnis.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,
DaSch hat geschrieben:

Code: Alles auswählen

...
grenze1 = int(grenze1)...
grenze1 = float(grenze1)
Das ist nicht sehr clever: Erst kann Dein Anwender (und wenn es nur Du selber bist, solltest Du da aufpassen, denn Du wirst in einem halben Jahr nicht immer wissen, was Du genau geschrieben hast) beliebige Genauigkeit vor, konvertierst dann in einen Integer und rechnest am Ende mit einer Fließkommazahl weiter. Warum nicht gleich mit einer Fließkommazahl rechnen?

Nun ja, BJ hat das Problem ja sehr genau erkannt und auch ein Beispiel gegeben, wie es besser geht. Das Problem bleibt jedoch bestehen, wenn man ohne Approximation summiert: Das "Integral" wird durch das Inkrement oder Interval bestimmt, weil es sich um eine "Summe" handelt. Die (meiner Ansicht nach) einfachste, halbwegs exakte Möglichkeit ist die Integration nach der Simpsonschen Regel (gar nicht so schwer, wie es ausschaut!). Dies und mehr ist übrigens in scipy integriert - außerdem gibt es ein Tutorial mit Einleitung in die Mathematik hinter den einzelnen Funktionen. Soll natürlich niemanden abhalten selber zu coden ;-).

Gruß,
Christian
Bernhard
User
Beiträge: 136
Registriert: Sonntag 15. Januar 2006, 20:31
Wohnort: Greifswald
Kontaktdaten:

Danke, BlackJack, für das Beispiel. Ich hatte übersehen, dass durch das feste Intervall mit 0.1 ja automatisch ein Float irgendwo da hinein kommt. Das ist aber nicht das Problem. Das Problem scheint mir die Schleifenbedingung.
Wenn man die Grenzen, wie von Blackjack vorgeschlagen mit 0.0 und 1.0 eingibt, dann rechnet der Originalcode auch noch die Fläche zwischen 1.0 und 1.1 mit. Das folgende Beispiel kommt auf den richtigen Wert 0.5:

Code: Alles auswählen

grenze1 = 0.0 #habe hier ein wenig gekürzt
grenze2 = 1.0

integral = 0.0
grenze1 = float(grenze1)
grenze2 = float(grenze2)
interval= 0.1

#Hier die Funktion eintragen
funktion = lambda x: x

i=grenze1
while i < (grenze2-interval):    #hat vorher zu lange gerechnet !
    integral += (funktion(i)+funktion(i+interval))/2 *interval
    print funktion(i),'+',funktion(i+interval),'/2 *',interval,' = ', (funktion(i)+funktion(i+interval))/2 *interval
    print "Summe bisher:",integral
    i += interval
print
print 'Integral ist ',integral
Gruß,
Bernhard
BlackJack

Die laufende Summierung ist aber auch ein Problem weil in jedem Schritt `i` etwas ungenauer wird. Beispiel:

Code: Alles auswählen

[In [80]: [0.1] * 10
Out[80]:
[0.10000000000000001,
 0.10000000000000001,
 0.10000000000000001,
 0.10000000000000001,
 0.10000000000000001,
 0.10000000000000001,
 0.10000000000000001,
 0.10000000000000001,
 0.10000000000000001,
 0.10000000000000001]

In [81]: sum([0.1] * 10)
Out[81]: 0.99999999999999989

In [82]: 0.1 * 10
Out[82]: 1.0
Die beiden letzten Ergebnisse müssten ja mathematisch betrachtet gleich sein. Da aber nach dem 10. mal 0.1 addieren etwas herauskommt, das kleiner ist als 1, rechnet die Schleife im Original noch ein Intervall weiter.

Ich könnt's jetzt nicht beschwören, aber ich habe so ein Bauchgefühl, dass man nur die richtigen Grenzen und eine passende Intervallbreite finden muss, damit Deine Schleife auch einmal zuviel oder zu wenig ausgeführt wird.
Bernhard
User
Beiträge: 136
Registriert: Sonntag 15. Januar 2006, 20:31
Wohnort: Greifswald
Kontaktdaten:

Das ist schon ganz schön erschreckend, wenn man bedenkt, dass hier Fehler auftreten, obwohl interval mit 0.1 ja doch keine Zahl mit vielen Nachkommastellen ist.

Deine Version

Code: Alles auswählen

for step in xrange(int((grenze2 - grenze1) / interval)): 
dürfte da robuster sein.

Glaubst Du, dass sie völlig problemlos ist, oder könnte auch sie duch geeignete Grenzen und Intervalle (z.B. Differenz der Grenzen sehr ähnlich wie Intervall) torpediert werden?

Gruß,
Bernhard
BlackJack

Da die Fliesskommazahlen nur endliche Genauigkeit haben kann man da sicher auch Fälle bekommen, bei denen es Probleme gibt.

Ich habe es mal mit rationalen Zahlen versucht:

Code: Alles auswählen

from gmpy import mpq

funktion = lambda x: x
grenze1 = 0
grenze2 = 1
integral = 0
interval = mpq(1, 10)
for step in xrange(int((grenze2 - grenze1) / interval)):
    point = step * interval
    integral += (funktion(point) + funktion(point + interval)) / 2 * interval
print integral
Das bleibt natürlich nur solange genau, wie `funktion()` ein rationales Ergebnis liefert und im Zweifelsfall genug Speicher zur Verfügung steht um Zähler und Nenner zu speichern.
DaSch
User
Beiträge: 72
Registriert: Mittwoch 28. März 2007, 20:04
Kontaktdaten:

also wenn man z.B. also Beispiel mit der Formel für y=x und den Grenzen 0 bis 1 mit einem Interval von 1 rechnet dann bekommt man ein korrecktes Ergebnis, das bring aber einen nicht weiter wenn man für einen größere Abstand zwischen den Grenzen dann Murks bekommt, das Problem ist wirklich wie geschildert dass mit der Menge der multiplikationen sich auch der Fehler multipliziert

Die Idee ist auch dass man das Intervall kleiner machen kann um dann die grenzen auch näher aneinander legen kann und dann trotzdem ein sinnvolles Ergebins rausbekommt egal welche Formel man da jetzt eintippt
Antworten