Approximation von Pi mit Formel von Srinivasa Ramanujan  

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
fcz1896
User
Beiträge: 4
Registriert: Freitag 26. Oktober 2018, 14:10

Hallo zusammen,
Ich habe erst kürzlich an der Hochschule mit dem Programmieren begonnen, nun muss ich ein Programm erstellen, dass eine Approximation von Pi berechnet. Dies geschieht anhand dieser Formel von Srinivasa Ramanujan. In dieser Formel gibt es ein Summenzeichen, was mir in Python Mühe bereitet. Ich erhalte ein Resultat, das aber leider weit entfernt von Pi ist. Hier mein bisheriges Programm, wie gesagt ich bin Anfänger:
import math
first_part = float((2*math.sqrt(2))/9801)
n=0
second_part = (math.factorial(4*n))/(math.factorial(n))**4
third_part = ((1103+26390*n)/(396**(4*n)))
while third_part >(1e-15):
second_part = (math.factorial(n)*4)/(math.factorial(n))**4
third_part = ((1103+26390*n)/(396**(4*n)))
a=second_part*third_part
a=a+1
n+=1
pi=(a*first_part)**-1
print (pi)  

Vielen Dank für eure Hilfe
Benutzeravatar
__blackjack__
User
Beiträge: 14033
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@fcz1896: Da in Python Einrückung wichtiger Teil der Syntax ist, müsstest Du Code in [ code ] Tags posten. Im „Vollständigen Editor“ (Schaltfläche unter der Texteingabe mit dem Titel „Schnellantwort“) gibt es dafür auch eine Schaltfläche zum Einfügen der Tags wo der Quelltext dann zwischen stehen sollte. Wenn der Quelltext ausgewählt war, wird er beim klick auf die Code-Schaltfläche zwischen die Tags gesetzt.

Was ist denn Dein Problem mit dem Summenzeichen? Das ist in Code ausgedrückt ja eine Schleife.

Anmerkungen zum Code: Gleichen Code und gleiche Daten sollte man nicht wiederholen. Das ist unnötige Arbeit beim Programmieren, fehleranfälliger beim schreiben und erst recht wenn man Code anpassen muss/will, und auch der Leser wird mehr ”belastet”, weil der ja weiss das sich Programmierer nicht wiederholen, und dementsprechend nach Unterschieden in den Kopien sucht und sich fragt ob da ein Fehler gemacht wurde wenn es keine Unterschiede gibt. Konkret sollte die Berechnung von `second_part` und `third_part` da nicht zweimal im Code stehen, sondern nur einmal *in* der Schleife. Die Schleife selbst sollte dann die Abbruchbedingung mit einem ``if`` im Schleifenkörper haben und gegebenenfalls mit einem ``break`` abbrechen wenn das Ergebnis fest steht. Im Grunde ist da momentan auch ein Programmierfehler drin, denn wenn die Schleifenbedingung schon beim ersten mal nicht erfüllt würde, wird der Schleifenkörper gar nicht durchlaufen und damit auch `a` gar nicht definiert. Das wird *nach* der Schleife aber verwendet.

Also kann/sollte man aus der Schleife eine ”Endlosschleife” machen die am Ende eine Abbruchbedingung mit ``if`` und ``break`` kodiert hat. Entweder mit ``while True:`` oder mit einer ``for``-Schleife über ganze Zahlen, denn `n` ist hier ja eigentlich eine Schleifenlaufvariable.

Für den `float()`-Aufruf bei `first_part` sehe ich keinen Grund. Bei den Berechnungen sind einige Klammerpaare überflüssig. Also *so* überflüssig das man nicht mal argumentieren kann, dass die beim Lesen/verstehen helfen.

Warum hast Du die Berechnung von `a` in zwei Teile aufgeteilt? Das ``+ 1`` kann man doch auch sofort machen, statt in einem extra Schritt.

`first_part` wird lange berechnet bevor der Wert überhaupt benötigt wird. Das kann man nicht nur hinter die Schleife ziehen, sondern auch gleich direkt in die Berechnung einbauen. Der Name selbst hatte ja keinen Mehrwert.

Code: Alles auswählen

#!/usr/bin/env python3
import math
from itertools import count


def main():
    for n in count():
        second_part = math.factorial(4 * n) / math.factorial(n)**4
        third_part = (1103 + 26390 * n) / 396**(4 * n)
        a = second_part * third_part + 1
        if third_part <= 1e-15:
            break
        
    pi = (a * 2 * math.sqrt(2) / 9801)**-1
    print(pi)


if __name__ == '__main__':
    main()
Ich sehe in dem Code jetzt aber auch nirgends wo etwas wie bei einem Summenzeichen üblich aufsummiert wird.

Ich bin zu faul um nach der mathematischen Formel zu suchen. Ich vermute mal das Du die halt irgendwo falsch in Code umgesetzt hast, insbesondere weil es Code ja keine Entsprechung eines Summenzeichens gibt. Beim Programmieren hilft es in der Regel Probleme die man nicht so leicht lösen kann in kleinere Teilprobleme aufzuteilen und die dann einzeln in Funktionen zu lösen. Bei einem Summenzeichen könntest Du eine Funktion schreiben, die einen Summanden berechnet, und diese Funktion dann in einer Schleife verwenden welche die ausgerechneten Summanden dann aufsummiert.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
ThomasL
User
Beiträge: 1378
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

@fcz1896: eine Summation sieht meist so aus:

Code: Alles auswählen

summe = 0
for n in count():
    term = ...
    summe += term
    if term < 1e-15:
        break
Wie sieht bei Dir der Term aus, der aufsummiert werden soll?


@ThomasL: toll, eine Seite, wo der Pythoncode auch ohne Einrückung abgedruckt ist, und factorial selbst definiert wird, obwohl das schon in math.factorial existiert und pow benutzt, obwohl es ja ** gibt.
Benutzeravatar
ThomasL
User
Beiträge: 1378
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Sirius3 hat geschrieben: Samstag 27. Oktober 2018, 09:53 @ThomasL: toll, eine Seite, wo der Pythoncode auch ohne Einrückung abgedruckt ist, und factorial selbst definiert wird, obwohl das schon in math.factorial existiert und pow benutzt, obwohl es ja ** gibt.
Nun ja, allgemeiner Tenor hier im Forum ist ja wohl nicht jeden Pups vorzukauen sondern zum selber nachdenken und lernen anzuregen.
Mit etwas Pythongrundlagen kann man sich den Code dort entsprechend erarbeiten und erkennen, dass dieser etwas anders ist als man selber zuvor gecodet hat.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hmja. Der Tenor ist richtig. Aber wenn ich sehe, wieviel Code da draußen dermaßen offensichtlich abgeschrieben/kopiert wird, und wie sich in speziellen Communities wirklich grauenvolle Muster entwickeln und halten, dann bin ich deiner These, dass sich da was ausmengelt skeptisch gegenüber. Das DU das tust, daran zweifele ich nicht ;)
fcz1896
User
Beiträge: 4
Registriert: Freitag 26. Oktober 2018, 14:10

Vielen Dank an alle für eure Hilfe und ich entschuldige mich für die unprofessionelle Darstellung meines Beitrages. Ich bin nun ein gutes Stück weitergekommen, aber noch nicht ganz am Ziel. Ich vermute ich habe eine Endschlosschlaufe programmiert;

Code: Alles auswählen

import math
first_part=(math.sqrt(8))/9801
n=0
summe=0

while True:
    second_part = ((math.factorial(n)*4)/(math.factorial(n))**4)
    third_part=((1103+26390*n)/(396**(4*n)))
    summe+=second_part*third_part
    if third_part<=1e-15:
        break
    n+1
pi=(summe*first_part)**-1
print (pi)
@__blackjack__ ich habe versucht meine Fehler und unnötigen Codezeilen so gut es geht zu korrigieren, vielen Dank für deine Inputs.
@ThomasL Danke für die Musterlösung, einige Funktionen darin wurden bei uns noch nicht behandelt, weshalb ich sie nicht verwenden kann.
Gibt es eine Möglichkeit meinen neuen Code mit den verwendeten Tools zum laufen zu bringen?
Nochmals danke für euren Support!
Benutzeravatar
ThomasL
User
Beiträge: 1378
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

@fcz1896 deine if Abfrage um die Schleife zu verlassen ist nicht ganz richtig, da fehlt ein _part (alles das was hinter dem Summenzeichen steht muss da hin)
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

@fcz1896: __blackjack__ hat Dir gezeigt, wie man eine Schleife mit Zählvariable richtig macht. Mit while statt for geht das zwar auch, aber dann macht man halt auch leicht mal einen Fehler, weil die while-Schleife deutlich komplizierter ist.

In die Formeln hast Du beim Abschreiben von Dir selbst auch noch zusätzlich Fehler eingebaut, die man dank der vielen überflüssigen Klammern kaum sieht.
fcz1896
User
Beiträge: 4
Registriert: Freitag 26. Oktober 2018, 14:10

Ich habe schlussendlich noch eine Lösung gefunden. Sie ist nicht perfekt aber falls jemand mal vor dem gleichen Problem steht, hier mein Code:

Code: Alles auswählen

import math

n=0

summe=0      #Zwischenspeicher

summe_temp=1     #Sonst Division durch 0 auf Zeile 17
summe_teil3 =((1103+26390*n)/396**(4*n))
while summe_teil3>10**-15:
    summe_temp = (math.factorial(4*n)/(math.factorial(n)**4))
    summe_teil3 =((1103+26390*n)/396**(4*n))
    summe = summe + summe_temp*summe_teil3  #Summenzeichen
    n = n+1

pi= 1/(((2*((math.sqrt(2))))/9801)*summe)   #1. Teil 

print (pi)

print (math.pi)
Nochmals vielen Dank an alle für eure Hilfe.
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Zeile mit `summe_temp=1` ist überflüssig, wie auch der Kommentar falsch. Das `summe_teil3` außerhalb der Schleife wird auch nirgends benutzt, außer in die while-Schleife einzutreten, daher for- oder while-True-Schleife.

Code: Alles auswählen

import math

n=0
summe = 0
while True:
    teil2 = math.factorial(4*n) / math.factorial(n)**4
    teil3 = (1103+26390*n) / 396**(4*n)
    summe += teil2 * teil3
    n += 1
    if teil3 <= 1e-15:
        break

pi = 1 / (2 * math.sqrt(2) / 9801 * summe)
print(pi)
print(math.pi)
oder um die teuren Operationen zu vermeiden:

Code: Alles auswählen

from itertools import count

summe = teil3 = 1103
teil2 = 1
for n in count(4, step=4):
    teil2 *= (n-3) * (n-2) * (n-1) * n / (99 * n) ** 4 
    teil3 += 26390
    summe += teil2 * teil3
    if teil3*teil2 <= 1e-15:
        break

pi = 1 / (summe * 8**0.5 / 9801)
print(pi)
print(math.pi)
Benutzeravatar
kbr
User
Beiträge: 1507
Registriert: Mittwoch 15. Oktober 2008, 09:27

Hmm, die schnelle (zweite) Variante ist bei mir geringfügig langsamer als die mit den math-Funktionsaufrufen (3,7 vs 3,4 µs).
Ich habe da, je nach Betriebssystem, aber schon recht eigenartige Unterschiede gesehen.
Antworten