Berechnung von Logarithmen und Potenzen

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 43 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

    Berechnung von Logarithmen und Potenzen

    Guten Tag

    Ich wollte mal fragen, wie ich ohne die vorhandenen Funktion im Framework Logarithmen sowie alle Potenzen berechnen kann. Fuer Potenzen habe ich mir schon etwas geschrieben, leider funktioniert das aber nur mit Integern, naemlich mit einer For-Schleife.

    Ich moechte ungern komplette Codes haben, eher kleine Denkanregungen.

    EDIT:

    C#-Quellcode

    1. public static decimal Pow(decimal x, decimal y)
    2. {
    3. decimal res = x;
    4. decimal n = y - 1;
    5. for (int i = 0; i < n; i++)
    6. {
    7. res *= x;
    8. }
    9. return res;
    10. }


    Vielen Dank im Voraus

    Jonas Jelonek

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Jonas Jelonek“ ()

    Potenzen mit rationalen Zahlen lassen sich zerlegen in Potenzen mit ganzen Zahlen und Wurzelziehen mit ganzzahligem Grad. Ganzzahliges Potenzieren lässt sich am besten durch Exponentiation by squaring umsetzen, Wurzelziehen mit ganzzahligem Grad ist umsetzbar mit dem nth root algorithm, einem Sonderfall des Newton-Verfahrens.
    Es sei aber gesagt, dass das mitunter sehr langsam werden kann. Ich hab das mal in meiner Rational-Klasse probiert und die Laufzeit wächst exponentiell bei steigender Komplexität des Bruches.

    Der Logarithmus wäre wohl am einfachsten über seine zugehörige Taylor-Reihe anzunähern.

    Jonas Jelonek schrieb:

    naemlich mit einer For-Schleife.
    Wenn Du Dir einen solchen Wert zu Fuß berechnen willst, beschaff Dir erst mal die theoretischen Grundlagen, z.B.: Quelle 1, Quelle 2.
    Dann überleg Dir einen Ablauf und, was besonders wichtig ist, ein Abbruchkriterium für Schleifen.
    Bedenke, dass ein Double in .NET eine begrenzte Anzahl an Stellen hat (8 Byte Zahlendarstellung). In C / C++ könntest Du mit long double (10 Byte Zahlendarstellung) noch etwas genauer sein.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Die For-Schleife hatte ich nur beim Berechnen der Potenzen benutzt, so hier:

    C#-Quellcode

    1. Siehe oben.

    Ausserdem verwende ich kein Double, sondern Decimal, meines Wissens kann dieser Datentyp mehr aufnehmen, da er ja 16 Bytes verwendet.
    Ich werde mich mit den Informationen mal genauer auseinandersetzen und versuchen, was gescheites zu schreiben.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Jonas Jelonek“ ()

    Ich habe mich jetzt mal hingesetzt mit der binären Exponentiation und habe was versucht, leider funktioniert das nicht wirklich.

    C#-Quellcode

    1. public static string DecToBin(int dec)
    2. {
    3. string res = "";
    4. int i = dec;
    5. while (true)
    6. {
    7. int x = i / 2;
    8. res = res + (i % 2).ToString();
    9. if (x == 0)
    10. { break; }
    11. i = x;
    12. }
    13. char[] str = res.ToCharArray();
    14. Array.Reverse(str);
    15. return new string(str);
    16. }
    17. public static decimal Pow(decimal x, decimal y)
    18. {
    19. string bin = DecToBin((int)x);
    20. int res = (int)x;
    21. for (int i = 0; i <= bin.Length; i++)
    22. {
    23. res *= res;
    24. if (bin.Substring(i, 1) == "1") //Hier
    25. {
    26. res *= (int)x;
    27. }
    28. }
    29. return res;
    30. }
    Leider kann ich keine Fehlerangabe machen, da das Programm bei der markierten Stelle einfach abbricht, ohne Fehlermeldung.

    PS: Ich weiss nicht warum dieser Code-Tag wieder so buggt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Jonas Jelonek“ ()

    So hab ich das z.B. implementiert:

    C#-Quellcode

    1. public static float Pow(float @base, int exponent)
    2. {
    3. if (@base == 0)
    4. {
    5. if (exponent == 0)
    6. return float.NaN;
    7. return 0;
    8. }
    9. if (@base == 1)
    10. return 1;
    11. if (exponent < 0)
    12. return 1 / Pow(@base, -exponent);
    13. float result = 1;
    14. while (exponent > 0)
    15. {
    16. if ((exponent & 1) != 0)
    17. result *= @base;
    18. exponent >>= 1;
    19. @base *= @base;
    20. }
    21. return result;
    22. }

    Beachte, dass der Exponent wirklich ein Ganzzahltyp sein muss, da ansonsten der Bitshift nicht funktioniert. Das reicht aber auch, denn selbst ein Double (der nebenbei bemerkt größer werden kann, als Decimal, er ist nur nicht so genau) hat den maximalen Exponenten 308, du würdest schon lange vor Ende eines Int32 einen Überlauf verursachen (egal mit welcher Basis).
    Mal abgesehen davon, dass DecToBin(value) einfach ​Convert.ToString(value, 2) entspricht, ist diese Funktion sowieso ziemlich lahm (weg. String). Bitshift und Bitwise-AND sind dagegen atomare Operationen und daher viel viel schneller. Der Code ist aus nem C++-Code von Stackoverflow üersetzt, ich glaube nicht, dass sich da noch irgend was optimieren lässt.

    Jonas Jelonek schrieb:

    warum meine Methode nicht funktioniert
    weil sie offensichtlich falsch ist.
    Arbeite mal Deinen Algorithmus zeilenweise ab und rechne die Ergebnisse mit nem Taschenrechner nach, da wirst Du den Fehler ganz schnell finden.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich habe jetzt mal versucht, den nth root algorithm zu implementieren, jedoch bekomme ich nicht ganz richtige Werte raus.

    C#-Quellcode

    1. public static decimal Root(decimal x, int n, int i)
    2. {
    3. decimal imval = x;
    4. for (int l = 1; l <= i; l++)
    5. {
    6. imval = 1m / n * ((n - 1) * imval + (x / Pow(imval, n - 1)));
    7. }
    8. return imval;
    9. }
    Nur was ist daran falsch? Vielleicht, dass ich immer die gleiche Variable nutze? Ich bekomme naemlich beim Rechnen mit n=2 und x=16 einen Wert von 4.1 usw., ein nicht ganz ferner Wert von dem, was ich mit n=3 und x=8 bekommen hab. Mit meinem Taschenrechner habe ich es alles auch durchgerechnet, dann natuerlich mit einer Variable fuer jeden neuen Wert, wie kann ich das in meinem Code am besten Loesen?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Jonas Jelonek“ ()

    Jonas Jelonek schrieb:

    wie kann ich das in meinem Code am besten Loesen?
    Schreib Dir mal den (richtigen) Algo Zeile für Zeile auf und überleg dann, wie das in Code zu gießen wäre.
    3 oder 4 Zeilen sollten reichen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Jonas Jelonek schrieb:

    angenommen
    er sei richtig so ist der .NET-Algo noch lange nicht richtig.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich habe mir jetzt den Algorithmus in den einzelnen Schritten auf Papier aufgeschrieben, und habe den jetzt im Code auch nochmal in einzelnen Schritten, trotzdem bekomme ich noch nicht das richtige Ergebnis.

    C#-Quellcode

    1. public static decimal Root(decimal x, int n, int i)
    2. {
    3. decimal imval = x;
    4. for (int l = 1; l <= i; l++)
    5. {
    6. decimal a = (decimal)Math.Pow((double)imval, (double)(n - 1));
    7. decimal b = x / a;
    8. decimal c = (n - 1) * imval;
    9. decimal d = b + c;
    10. decimal e = d / n; // bzw. d * (1/n)
    11. imval = e;
    12. }
    13. return imval;
    14. }
    Liegt es nun daran, dass ich immer imval nehme anstatt immer eine neue Variable oder woran koennte es sonst liegen?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Jonas Jelonek“ ()

    Ich habe eben einen Fehler in der Schleife entdeckt, habe den Code jetzt auch ausgebessert, jedoch komme ich beim Errechnen mit den Werten n=3 und x=8 nicht genau auf zwei, sondern nur immer mit Abweichungen. Das Problem ist aber, ich weiss nicht woran das liegt. Rechnet der Computer vielleicht zu genau mit Decimal und rechnet dadurch dann so vorbei?

    C#-Quellcode

    1. public static decimal Root(decimal x, int n, int i)
    2. {
    3. decimal imval = x;
    4. for (int l = 1; l <= i; l++)
    5. {
    6. decimal a = (decimal)Math.Pow((double)imval, (double)(n - 1)); MessageBox.Show("a = " + a.ToString());
    7. decimal b = x / a; MessageBox.Show("b = " + b.ToString());
    8. decimal c = (n - 1) * imval; MessageBox.Show("c = " + c.ToString());
    9. decimal d = b + c; MessageBox.Show("d = " + d.ToString());
    10. decimal e = d / n; MessageBox.Show("e = " + e.ToString());
    11. imval = e;
    12. }
    13. return imval;
    14. }
    Ich habe mir die Werte auch immer ausgeben lassen und auch selber nachgerechnet, hat soweit alles gestimmt.