Berechnung Lagrange-Polynom mit geg. x-und y-Werten

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
moabit.rolf
User
Beiträge: 1
Registriert: Dienstag 16. Mai 2023, 17:55

Bild

hier die Dateien, die uns zur Verfügung gestellt wurden. Innerhalb dieser gibt es bereits vorgefertigten Code, welcher als eine Art Leitfaden oder zur Orientierung dienen soll.
https://we.tl/t-qIhRDKDVK6


wäre klasse, wenn mir da jemand unter die Arme greifen könnte, ich bin ehrlich gesagt ziemlich überfordert.

Danke im Voraus

Code: Alles auswählen

import matplotlib.pyplot as plt
import numpy as np

from my_func_lib import lagrange_polynom_interpolation, polynom


def plot_me(x_list: list[float], y_list: list[float], p: polynom):
    # MUSS NOCH GESCHRIEBEN WERDEN!
    fig = plt.figure()
    plt.plot(x_list, y_list, "x")
    plt.plot(x_poly, y_poly, ":")

    plt.savefig("pic.png", dpi=fig.dpi)
    plt.show()


def main():
    # Beispiel um ein Polynom zu erstellen: pol = polynom([1,2])
    x = [1, 2, 3]
    y = [3, 6, 0]

    p = lagrange_polynom_interpolation(x, y)
    print(p)
    p._round(tol=1e-12)
    print(p)
    print(f"An der Stelle x = 2 ist der Wert des Polynoms p(x) = {p.eval(2)}")

    plot_me(x_list=x, y_list=y, p=p)


if __name__ == "__main__":
    main()

Code: Alles auswählen

import numpy as np


class polynom:
    """Eine Klasse um eine Polynom darzustellen und die Addition und Multiplikation mit anderen Polynomen oder Zahlen zu definieren.
    Polynome werden dabei als Liste mit absteigenden Grad gespeichert.
    Bsp.:   x^2 -3x +2  <=> [1, -3, 2]
            4x +5       <=> [4, 5]

    Polynome können nur Addiert und Multipliziert werden. Die Subtraktion und Division ist noch nicht Definiert.
    """

    def __init__(self, poly: list[float]):
        self._poly = poly

    def __repr__(self) -> str:
        """Damit die Klasse eine Vernüpgtige Darstellung hat, wenn ein Element dieser Klasse geprinted ( print(polynom([1,2])) ) wird.
        Wir nutzen die Darstellung der Listen in Python und benutzen den String der in dieser Klasse definiert ist.

        Returns:
            str: Den String der in der Klasse "List" definiert ist.
        """
        return self._poly.__str__()

    def eval(self, x) -> float:
        # MUSS NOCH GESCHRIEBEN WERDEN!
        raise NotImplementedError

    def __add__(self, other):
        # MUSS NOCH GESCHRIEBEN WERDEN!
        raise NotImplementedError

    def __iadd__(self, other):
        """Wird benötigt um "+=" zu ermöglichen. Verweist am Ende auf die unter __add__ definierte Addition."""
        return self + other

    def __mul__(self, other):
        """Funktion die definiert, was passiert wenn zwei Polynome multipliziert werden. Durch diese Funktionen kann der "*" benutzt werden um Polynome zu multiplizieren.
        Ein Polynom kann mit einem anderen Polynom oder einer anderen Zahl multipliziert werden.
        Bsp.:   [1,2]*[1,3] = [1,5,6]
                3 * [1,2] = [3,6]
        Args:
            other (polynom | float): Das was mit dem Polynom multipliziert werden soll

        Raises:
            TypeError: Wenn weder ein Float noch ein Polynom zur Multiplikation verwendet werden sollen, wird ein Fehler produziert.

        Returns:
            polynom: Ein neues Polynom
        """
        if isinstance(other, polynom):
            gx = self.grad
            x = self._poly
            gy = other.grad
            y = other._poly

            gz = gx + gy
            len_z = gz + 1

            zi = [0] * len_z
            z = polynom(zi)
            for i in range(gx + 1):
                zij = zi.copy()
                for j in range(gy + 1):
                    zij[j + i] = x[i] * y[j]
                z += polynom(zij)

            return z
        if isinstance(other, (int, float)):
            x = self._poly
            y = [other * xi for xi in x]
            return polynom(y)
        raise TypeError

    def __imul__(self, other):
        """Wird gebraucht um "*=" zu umzuschreiben. Verweist am Ende auf die definition der Multiplikation.

        Args:
            other (polynom | float): Das was mit dem Polynom multipliziert werden soll

        Returns:
            polynom: Ein neues Polynom
        """
        return self * other

    def __eq__(self, other) -> bool:
        """Funktion die Benötigt wird um Gleichheit zwischen Polynomen zu bestimmen. Also das auf die Frage [1,2] == [1,2] mit True oder False geantwortet werden kann.

        Args:
            other (polynom): Anderes Polynom

        Returns:
            bool: Gibt zurück ob die beiden Polynome gleich sind (True) oder nicht (False).
        """
        return self._poly == other._poly

    def _cut(self):
        """Bei der Addition kann es sein, dass sich führende Terme kürzen. Das Polynom kann mit dieser Funktion gekürzt werden.
        Bsp.: [1,2] + [-1, 4] = [0,6]
        In diesem Beispiel würde [0,6] das Polynom nach der Addition sein, mit self._cut() wird [0,6] zu [6].

        Returns:
            polynom: gekürztes Polynom
        """
        idx = [i for i, e in enumerate(self._poly) if e != 0]
        self._poly = self._poly[idx[0] :]
        return self

    def _round(self, tol: float = 1e-14) -> None:
        """Zahlen wie 0.00000000000002 und 1.999999999 sollen zu 0 bzw. 2 gerundet werden.

        Args:
            tol (float, optional): Einstellung für die Toleranz. Defaults to 1e-14.

        Returns:
            polynom: Neues Polynom bei dem die Zahle nahe 0 auf 0 gesetzt wurden.
        """
        q = []
        for p in self._poly:
            f = np.floor(p)
            c = np.ceil(p)
            if abs(f - p) <= tol:
                q.append(f)
                continue
            if abs(c - p) <= tol:
                q.append(c)
                continue
            q.append(p)

        self._poly = q
        return self._cut()

    def full(self, variable: str = "x") -> str:
        """Für den Fall das jmd die Datstellung als Polynom präfeiert, der kann mit self.full eine Darstellung der Art:
            2x**2 + 3x**1 - 4x**0

        Das "x" kann dabei als optionale Variable übergeben werden. Wenn man f(y) haben will, sollte man self.full("y") eingeben.
        Args:
            variable (str, optional): Wer eine andere Variable als x möchte. Defaults to "x".

        Returns:
            str: String der via print ausgegeben werden kanns
        """
        g = self.grad
        str = ""
        for i in range(len(self._poly)):
            if i == 0:
                str += f"{self._poly[i]:.4f}{variable}^{g-i} "
                continue
            sig = "+" if self._poly[i] >= 0 else "-"
            str += f"{sig} {abs(self._poly[i]):.4f}{variable}^{g-i} "

        return str

    @property
    def grad(self) -> int:
        """Gibt den Grad vom Polynom zurück. Dieser ist um ein kleiner als die Länge der gespeicherten Liste.

        Returns:
            int: Grad des Polynoms
        """
        return len(self._poly) - 1


def lagrange_polynom_interpolation(
    x: list[int | float], y: list[int | float]
) -> polynom:
    # MUSS NOCH GESCHRIEBEN WERDEN!
    raise NotImplementedError
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Und was ist Deine bisherige Leistung außer den Aufgabentext einzuscannen?
Wir können hier gerne bei konkreten Fragen helfen, aber wir reagieren allergisch, wenn uns nur die Hausaufgaben hingeklatscht werden.

Zum Code an sich läßt sich sagen, dass Dein Lehrer dringend Python lernen sollte. Ein Module my_func_lib zu nennen ist aus mehreren Gründen schlecht. Dass es sich bei einem Modul um eine "Lib" handelt, ist offensichtlich, das in den Namen zu schreiben unsinnig, das Präfix my_ trägt nicht zum Verständnis bei, kann also auch weg, bleibt `func` was so generisch ist, dass es auch weg kann. Module sollten wie alles andere auch, nach dem benannt sein, was es enthält. Im Supermarkt steht ja auf der Packung auch Cornflakes oder Schokolade drauf, und nicht überall nur "Essen".

Datentypen sollten nicht in Variablen vorkommen, x_list ist also nur x und die Typannotation ist zwischen zu einschränkend und komplett falsch. Es wird numpy eingebunden, also liegt zumindest die Vermutung nahe, dass x auch ein numpy-Array sein könnte. `plot_me` plottet hoffentlich nicht mich, sondern ein Polynom, so dass der Name `plot_polynom` aussagekräftiger wäre.

Methoden mit _ sind nicht öffentlich, sollten also nicht außerhalb der Klasse direkt aufgerufen werden.

Klassen werden nach Konvention großgeschrieben, also `Polynom`, das ist hilfreich, weil es sich von der Schreibweise von der konkreten Polynom-Instanz, die man dann `polynom` (statt p) nennen könnte, unterscheidet.

Ein Polynom bekommt als Argument ein poly(nom)? Das ist so, wie wenn auf der Zutatenliste eines Kuchens "Kuchen" steht. Völlige unsinnig. Richtig wäre coefficients.
Die __repr__-Methode sollte einen String zurückgeben, der erkennen läßt, dass es sich um eine Polynom-Instanz handelt, hier wird aber die Repräsentation einer Liste zurückgegeben, damit läßt sich eine Liste in der Ausgabe nicht von einem Polynom unterscheiden. Die Funktion ist also falsch. Magische Funktionen (__str__) ruft man nicht direkt auf.
Bei den nächsten Methoden (eval, __add__, __mul__) fehlen plötzlich die Typannotationen, wenn schon Annotationen dann richtig (siehe oben) und vollständig.
Auch für Dich wäre es wichtig zu wissen, ob `eval´ nun nur für ein einzelnes x, eine Liste mit x-Werten oder einem numpy-Array funktionieren sollte.

Bei __mul__ werden wieder schlechte Variablennamen benutzt, warum gx und gy?
Über Indizes iteriert man nicht, wenn man es vermeiden kann. Es ist unsinnig, eine Leere Liste zu definieren und die in jedem Schleifendurchgang zu kopieren, statt einfach immer wieder eine neue leere Liste zu erzeugen.

Die isinstance-Prüfung auf int und float ist viel zu einschränkend, es gibt noch etliche weitere Klassen, die sich wie Zahlen verhalten.

In _cut wird sehr umständlich der kleinste Index != 0 ermittelt. Da das ja eher die Ausnahme ist, sollte man nicht alle Koeffizienten durchgehen müssen. Funktionen, die den internen Zustand ändern, sollten sich nicht selbst als Rückgabewert haben, das verwirrt nur, weil der Anwender denkt, er bekäme ein neues Objekt.
`_round` rundet nicht wirklich, und wenn man die Funktion schon so nennt, sollte man sie auch benutzen. continue sollte man vermeiden. Und hier stimmt zusätzlich noch die Typannotation des Rückgabewerts mit dem Rückgabewert überein.

Code: Alles auswählen

import matplotlib.pyplot as plt
import numpy as np

from polynom import lagrange_polynom_interpolation, Polynom


def plot_polynom(x, y, polynom):
    # MUSS NOCH GESCHRIEBEN WERDEN!
    fig = plt.figure()
    plt.plot(x, y, "x")
    plt.plot(x_poly, y_poly, ":")

    plt.savefig("pic.png", dpi=fig.dpi)
    plt.show()


def main():
    # Beispiel um ein Polynom zu erstellen: pol = polynom([1,2])
    x = [1, 2, 3]
    y = [3, 6, 0]

    polynom = lagrange_polynom_interpolation(x, y)
    print(polynom)
    polynom.make_numbers_nice(tol=1e-12)
    print(polynom)
    print(f"An der Stelle x = 2 ist der Wert des Polynoms p(x) = {p.eval(2)}")

    plot_polynom(x, y, polynom)


if __name__ == "__main__":
    main()

Code: Alles auswählen

import numpy as np
from itertools import count

class Polynom:
    """Eine Klasse um eine Polynom darzustellen und die Addition und Multiplikation mit anderen Polynomen oder Zahlen zu definieren.
    Polynome werden dabei als Liste mit absteigenden Grad gespeichert.
    Bsp.:   x^2 -3x +2  <=> [1, -3, 2]
            4x +5       <=> [4, 5]

    Polynome können nur Addiert und Multipliziert werden. Die Subtraktion und Division ist noch nicht Definiert.
    """

    def __init__(self, coefficients):
        self._coefficients = coefficients

    def __repr__(self) -> str:
        """Damit die Klasse eine Vernüpgtige Darstellung hat, wenn ein Element dieser Klasse geprinted ( print(polynom([1,2])) ) wird.
        Wir nutzen die Darstellung der Listen in Python und benutzen den String der in dieser Klasse definiert ist.

        Returns:
            str: Den String der in der Klasse "List" definiert ist.
        """
        return f"Polynom({self._coefficients!r})"

    def eval(self, x) -> float:
        # MUSS NOCH GESCHRIEBEN WERDEN!
        raise NotImplementedError

    def __add__(self, other):
        # MUSS NOCH GESCHRIEBEN WERDEN!
        raise NotImplementedError

    def __iadd__(self, other):
        """Wird benötigt um "+=" zu ermöglichen. Verweist am Ende auf die unter __add__ definierte Addition."""
        return self + other

    def __mul__(self, other):
        """Funktion die definiert, was passiert wenn zwei Polynome multipliziert werden. Durch diese Funktionen kann der "*" benutzt werden um Polynome zu multiplizieren.
        Ein Polynom kann mit einem anderen Polynom oder einer anderen Zahl multipliziert werden.
        Bsp.:   [1,2]*[1,3] = [1,5,6]
                3 * [1,2] = [3,6]
        Args:
            other (polynom | float): Das was mit dem Polynom multipliziert werden soll

        Raises:
            TypeError: Wenn weder ein Float noch ein Polynom zur Multiplikation verwendet werden sollen, wird ein Fehler produziert.

        Returns:
            polynom: Ein neues Polynom
        """
        if isinstance(other, polynom):
            grad = self.grad
            result = type(self)([0])
            for i, c in enumerate(self._coefficients)):
                values = [c * d for d in other._coefficients]
                values.extend([0] * (grad - i))
                result += type(self)(values)
            return result
            
        if isinstance(other, (int, float)):
            scaled_coefficients = [other * c for c in self._coefficients]
            return type(self)(scaled_coefficients)
        raise TypeError

    def __imul__(self, other):
        """Wird gebraucht um "*=" zu umzuschreiben. Verweist am Ende auf die definition der Multiplikation.

        Args:
            other (polynom | float): Das was mit dem Polynom multipliziert werden soll

        Returns:
            polynom: Ein neues Polynom
        """
        return self * other

    def __eq__(self, other) -> bool:
        """Funktion die Benötigt wird um Gleichheit zwischen Polynomen zu bestimmen. Also das auf die Frage [1,2] == [1,2] mit True oder False geantwortet werden kann.

        Args:
            other (polynom): Anderes Polynom

        Returns:
            bool: Gibt zurück ob die beiden Polynome gleich sind (True) oder nicht (False).
        """
        return self._coefficients == other._coefficients

    def _cut(self):
        """Bei der Addition kann es sein, dass sich führende Terme kürzen. Das Polynom kann mit dieser Funktion gekürzt werden.
        Bsp.: [1,2] + [-1, 4] = [0,6]
        In diesem Beispiel würde [0,6] das Polynom nach der Addition sein, mit self._cut() wird [0,6] zu [6].

        Returns:
            polynom: gekürztes Polynom
        """
        idx = 0
        while self._coefficients[idx] == 0:
            idx += 1
        self._coefficients = self._coefficients[idx:]

    def make_numbers_nice(self, tol: float = 1e-14):
        """Zahlen wie 0.00000000000002 und 1.999999999 sollen zu 0 bzw. 2 gerundet werden.

        Args:
            tol (float, optional): Einstellung für die Toleranz. Defaults to 1e-14.

        Returns:
            polynom: Neues Polynom bei dem die Zahle nahe 0 auf 0 gesetzt wurden.
        """
        new_coefficients = []
        for coefficient in self._coefficients:
            rounded_coefficient = round(c)
            if abs(rounded_coefficient - coefficient) <= tol:
                new_coefficients.append(rounded_coefficient)
            else:
                new_coefficients.append(coefficient)

        self._coefficients = new_coefficients
        self._cut()

    def full(self, variable: str = "x") -> str:
        """Für den Fall das jmd die Datstellung als Polynom präfeiert, der kann mit self.full eine Darstellung der Art:
            2x**2 + 3x**1 - 4x**0

        Das "x" kann dabei als optionale Variable übergeben werden. Wenn man f(y) haben will, sollte man self.full("y") eingeben.
        Args:
            variable (str, optional): Wer eine andere Variable als x möchte. Defaults to "x".

        Returns:
            str: String der via print ausgegeben werden kanns
        """
        result = []
        for power, coefficient in zip(count(self.grad, -1), self._coefficients):
            if not result:
                result.append(f"{coefficient:.4f}{variable}^{power}")
            else:
                sign = "+" if coefficient >= 0 else "-"
                result.append(f"{sign} {abs(coefficient):.4f}{variable}^{power}")
        return " ".join(result)

    @property
    def grad(self) -> int:
        """Gibt den Grad vom Polynom zurück. Dieser ist um ein kleiner als die Länge der gespeicherten Liste.

        Returns:
            int: Grad des Polynoms
        """
        return len(self._poly) - 1


def lagrange_polynom_interpolation(
    x: list[int | float], y: list[int | float]
) -> polynom:
    # MUSS NOCH GESCHRIEBEN WERDEN!
    raise NotImplementedError()
Antworten