Laden...

Verschachtelter Aufruf von Funktionen erlaubt?

Erstellt von Grzfrz vor 17 Jahren Letzter Beitrag vor 17 Jahren 5.599 Views
G
Grzfrz Themenstarter:in
48 Beiträge seit 2006
vor 17 Jahren
Verschachtelter Aufruf von Funktionen erlaubt?

Ich hab wieder mal ein Problem und komme nicht weiter.
Beispiel:
Ich habe eine Sin() funktionen. Diese ruft im zuge der Berechnung eine Funktion zum Potenzieren Pot() auf. Diese ruft wiederum den Logarithmus Ln() auf. Und jetzt kommts, der Ln() ruft wiederum die Pot() auf. Zwar mit anderen Argumenten als zuerst der Sin(), aber kanns sein das er da stolpert?

Ich bekomme nämlich immer "System.StackOverflowException wurde nicht behandelt." und als Hinweis: "Stellen sie sicher, dass sich keine Endlosschleife ergibt". Und der obige Fall ist das einzige, was er meiner Meinung nach als Endlosschleife erkennen könnte. Achja, das ganze läuft in der selben Klasse funktions ab..

F
10.010 Beiträge seit 2004
vor 17 Jahren

Natürlich sind recursive Funktionen machbar.

Aber Du wirst sicherlich die Abbruchbedingung vergessen haben.
Denn jede Operation muss irgendwann mal zu ende sein.

I
1.739 Beiträge seit 2005
vor 17 Jahren

Tja, zirkuläre Abhängigkeiten...
Also für die angesagten Funktionen gibts die Math-Klasse. Ansonsten bitte Code um dir die Fehler zu zeigen, die sonst immer wieder Auftreten(das eigentliche Problem ist wohl allgemein und trifft oft auf(meist ein ein typischer Fehler, der auf mangelnde Erfahrung(oder in dem Fall auch auf mangelnde Theorie) beruht), jedenfalls sollte es kein Beinbruch sein).

G
Grzfrz Themenstarter:in
48 Beiträge seit 2006
vor 17 Jahren

Das Problem ist, wenn ich den Sinus extra aufrufe zum testen, geht alles glatt?

@ikaros: Jau, nur das für unser Projekt die system.math funktionen allesamt viel zu ungenau sind (Fehler schon bei der 6. Nachkommastelle). Desshalb musste ich alles neu schreiben.. Heute wäre ich fertig geworden, wäre der Fehler nicht ^^

Ich habe die funktions Klasse mal angehängt.
Der relevante Teil:


        public static decimal Pot(decimal x, decimal n)
        {
            if (n != 0)
            {
                if (System.Math.Truncate(n) == n)
                {
                    if (n > 0m)
                    {
                        if (x >= 0m)
                        {
                            decimal x1 = x;
                            x = Exp(n * Ln(x1));
                        }
                        else
                        {
                            x *= -1m;
                            decimal x1 = x;
                            x = Exp(n * Ln(x1));
                            decimal minplus = System.Math.Abs(n) / 2m;
                            if (System.Math.Abs(minplus) - minplus == 0m)
                                x *= -1m;
                        }
                    }
                    else
                    {
                        if (x >= 0m)
                        {
                            decimal x1 = x;
                            x = Exp(n * Ln(x1));
                        }
                        else
                        {
                            x *= -1m;
                            decimal x1 = x;
                            x = Exp(n * Ln(x1));
                            decimal minplus = System.Math.Abs(n) / 2m;
                            if (System.Math.Abs(minplus) - minplus == 0m)
                                x *= -1m;
                        }
                    }
                }
                else
                {
                    if (x >= 0m)
                    {
                        decimal x1 = x;
                        x = Exp(n * Ln(x1));
                    }
                    else
                    {
                        x *= -1m;
                        decimal x1 = x;
                        x = Exp(n * Ln(x1));
                        decimal minplus = System.Math.Abs(n) / 2m;
                        if (System.Math.Abs(minplus) - minplus == 0m)
                            x *= -1m;
                    }
                }

            }
            else
                x = 1m;
            return x;
        }

        public static decimal Exp(decimal x)
        {
            decimal exp = 0m;
            for (int n = 0; n < 28; n++)
            {
                exp += Pot(x, n) / Facul(n);
            }
            return exp;
        }

        public static decimal Ln(decimal x)
        {
            decimal ln = 0m; //HIER tritt die Stackoverflow exception auf
            for (int n = 0; n < 27; n++)
            {
                ln += 1m / (2m * n + 1m) * Pot((x - 1m) / (x + 1m), 2m * n + 1m);
            }
            return 2m * ln + Rest(27,x);
        }

        private static decimal Rest(int n, decimal x)
        {
            decimal rest = 0m;
            rest = System.Math.Abs(((x - 1m) * (x - 1m)) / (2m * System.Math.Abs(x)) * Pot((x - 1m) / (x + 1m), 2m * n));
            return rest;
        }

        public static decimal Facul(decimal a)
        {
            decimal x = 1;
            for (ulong i = 1; i <= a; i++)
            {
                x *= i;
            }
            
            return x;
        }

und der Sinus selbst:


        public static decimal Sin(decimal winkel, int genauigkeit)
        {
            while (winkel > 360m)
                winkel -= 360m;
            while (winkel < -360m)
                winkel += 360m;
            
            decimal sin1 = 0m;
            decimal sinalt = 99m;
            int n = 0;
            decimal grenze = 9 * Pot(10m, genauigkeit * -1);
            
            if (winkel > 180m)
            {
                decimal x = (winkel - 180m) * pi / 180m;
                for (; ; )
                {
                    sin1 += funktions.Pot(-1m, n) * (funktions.Pot(x, 2 * n + 1) / funktions.Facul(2 * n + 1));
                    if (n > 12 || System.Math.Abs(sinalt - sin1) < grenze)
                        break;
                    n++;
                    sinalt = sin1;
                }
                sin1 *= -1m;
            }
            else if (winkel < -180m)
            {
                decimal x = (winkel + 180m) * pi / 180m;
                for (; ; )
                {
                    sin1 += funktions.Pot(-1m, n) * (funktions.Pot(x, 2 * n + 1) / funktions.Facul(2 * n + 1));
                    if (n > 12 || System.Math.Abs(sinalt - sin1) < grenze)
                        break;
                    n++;
                    sinalt = sin1;
                }
                sin1 *= -1m;
            }
            else
            {
                decimal x = winkel * pi / 180m;
                for (; ; )
                {
                    sin1 += funktions.Pot(-1m, n) * (funktions.Pot(x, 2 * n + 1) / funktions.Facul(2 * n + 1));
                    if (n > 12 || System.Math.Abs(sinalt - sin1) < grenze)
                        break;
                    n++;
                    sinalt = sin1;
                }
            }
            return sin1;
        }

Beim decimal ln = 0m; der Ln() funktion tritt der Fehler auf. Ich habe allmählich wirklich keine Ideen mehr was da los ist..

PS: Sicher wird sich jemand über den 3x vorhandenen Code bei Pot() wundern ^^ Normalerweise ersetzt die ersten beiden gleichen Blöcke eine einfachere Berechnung, die etwas schneller geht. Der Fehler tritt aber nur bei diesem exp(b*(lna)) auf, also habe ich um immer damit zu arbeiten den Code so abgewandelt 😉

I
1.739 Beiträge seit 2005
vor 17 Jahren
Ungenauigkeit

liegt bei Float.
Rechnen geht mit Double( casten, casten, casten oder eigene Typen(leider ohne Coprozessor)).
Deinen Code hab ich nur überflogen(später(vieviel später weiss ich nicht(bin nur ein unzuverlässiges Forumsmitglied))).

T
512 Beiträge seit 2006
vor 17 Jahren

Wärs nicht irgendwie cleverer die Potenzen und die Fakultät gleich mit der Schleife auszurechnen? Damit brauchst du schonmal keine Pow Funktion und die Präzesion sollte darunter nicht leiden.


potx = x;
x = x*x;
facn = 1;
sign = 1;

while(true)
{
    sin1 += sign * x / facn;
    if (n > 12 || System.Math.Abs(sinalt - sin1) < grenze)
        break;

    ++n;
    facn *= n;
    ++n;
    facn *= n;
    potx *= x;
    sign *= -1;

    sinalt = sin1;
}

Und das gleiche System bitte nochmal auf die Exp und Ln Funktion und dein Problem sollte sich erledigen. In der Mathematik Programmiersprache mag das ja ne ganz schöne Formel sein aber bei ner imperativen Programmiersprache sollte man das so nicht bringen 😉

Achja und vermeide es wenn du viel mit festen decimal werten arbeitestn (1m) Variablen kurze mit l beginnende Namen zu geben. 1m und lm sind nicht wirklich unterscheidbar und ln ist auch nicht viel besser g

(EDIT: noch nen Fehler im Code gefunden)

e.f.q.

Aus Falschem folgt Beliebiges

G
Grzfrz Themenstarter:in
48 Beiträge seit 2006
vor 17 Jahren

Du meinst, zu jeder Winkelfunktion (um beim Beispiel zu bleiben) die benötigten anderen Funktionen reinschreiben? Das könnte dann ein ziemlich langes Stück werden, wie gesagt verschachtelt sich das bei einigen ganz ordentlich..

Ehrlich gesagt blick ich bei deinem Code nicht ganz durch g
sin1 += funktions.Pot(-1m, n) * (funktions.Pot(x, 2 * n + 1) / funktions.Facul(2 * n + 1));

z.B. die Fakultät: z.B. brauch ich beim 2. Durchlauf (2*2+1)! = 120, dann beim 3. 7! = 5040. Du ersetzt das durch
++n;
facn *= n;
++n;
facn *= n;
da kommt aber nicht das selbe raus, oder?
Meine Idee wäre eben gewesen, eine Klasse mit Math. Funktionen zu schreiben, die ich dann universell für alles nutzen kann (auch ln und exp werden später getrennt benötigt), und da habe ich es für übersichtlicher gehalten, die Winkelfunktionen (die zum Teil ziemliche Brocken auch so sind, da ich sie auf genauigkeit getrimmt habe) auf den existierenden Funktionen basieren zu lassen.

Das mit 1m <-> lm ist natürlich richtig, werde ich beherzigen ^^

T
512 Beiträge seit 2006
vor 17 Jahren

Du bestimmst die Potenzen immer neu. Du rechnest x11 aus, ich rechne einfach das x9 was ich schon kenne mal x^2 was ich schon kenne. Wozu da ne Potenzfunktion? Und genau das kannst du bei den anderen auch benutzen.

Fakultät ist ja so definiert: n! = (n-1)! * n
Genau das rechne ich.

Ich hab nur die Bedeutung von n geändert. Der Wert in n ist jetzt direkt der Exponent, statt dass 2*n + 1 der Exponent wäre wie bei deinem Code.

Gehs doch einfach mal durch.
Erster Durchlauf: facn = 1, n = 1 (ok n=1 hab ich vergessen hinzuschreiben, du initialisierst ja noch mit 0)
++n => n = 2
facn *= 2 => facn = 2
++n => n = 3
facn *= 3 => facn = 6 = 3! => richtiger Wert für den 2. Durchlauf.

++n => n = 4
facn *= 4 => facn = 24
++n => n = 5
facn *= 5 => facn = 120 = 5! => richtiger Wert für den 3. Durchlauf.

Wie gesagt ist das was du gemacht hast ja mathematisch ganz hübsch. Aber in der realen Welt braucht jeder Funktsionsaufruf Speicher. Irgendwann ist keiner mehr da und du bekommst diese Fehlermeldung. Lösung: Weniger Funktionsaufrufe. Ich seh auch echt keinen Sinn dahinter einen Funktionsaufruf, hinter dem ein umfangreicher Algorithmus steckt, einem simplen c = a*b vorzuziehen.

Wenn du nur x^n ausrechnen willst ist die Potenzfunktion gut. Wenn dich die Vorhergehenden Ergebnisse auch interessieren nicht. Stell dir einfach vor wie du das per Hand machen würdest. Du würdest mit Sicherheit nicht jede Potenz neu bestimmen. Du würdest das von der vorhergehenden Iteration hernehmen und damit das für die aktuelle Iteration berechnen.

e.f.q.

Aus Falschem folgt Beliebiges

G
Grzfrz Themenstarter:in
48 Beiträge seit 2006
vor 17 Jahren

Ok, ich gebe zu die Überlegung hat was (vor allem was beschleunigendes ^^) 🙂

Danke dir, dann werde ich mal beginnen das ganze Zeug zu überarbeiten. Hoffentlich gibt er dann wenigstens Ruhe mit seinem StackOverflow.. Aber zumindestens schneller sollts dann laufen.

I
1.739 Beiträge seit 2005
vor 17 Jahren

Original von Grzfrz
Ok, ich gebe zu die Überlegung hat was (vor allem was beschleunigendes ^^) 🙂

Danke dir, dann werde ich mal beginnen das ganze Zeug zu überarbeiten. Hoffentlich gibt er dann wenigstens Ruhe mit seinem StackOverflow.. Aber zumindestens schneller sollts dann laufen.

Anmerkung:
Selbstverständlich ist Rekursion das Schnelllste(nur in der Küze des Codes), das Iterative(in der Kürze der Geschwindigkeit) ist oft besser(velrangt aber oft bessere Anwendung von unsinnigen Formeln).
(nicht immer...)